o
    i/                     @  s   d Z ddlmZ ddlZddlZddlZddlZddlmZm	Z	m
Z
mZ eeZeddZedZedZed	d
Zd-ddZd.ddZd/ddZd0ddZ		d1d2d'd(Z		d1d2d)d*ZG d+d, d,ZdS )3u  
AI agent runner for post-call analysis.

Loads ``business_agent_config`` records for a given bid from the database,
builds prompts using the call transcript and speaker segments, calls the
configured LLM (AWS Bedrock or Ollama), and returns an aggregated analysis
dict.

Each agent produces a JSON blob stored under its ``agent_name`` key.  Two
top-level shortcuts — ``quality_score`` and ``summary`` — are promoted from
the first agent that emits them so the call records list can display scores
without parsing per-agent JSON.
    )annotationsN)AnyDictListOptional
AWS_REGIONz	us-east-1AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_NOVA_MODELzamazon.nova-lite-v1:0
transcriptstrspeaker_segments
List[Dict]returnc                 C  sl   |r2g }|D ]$}| d| dd}| dpd }|r*|d|  d|  q|r2d|S | p5dS )	zOReturn a readable conversation string from speaker_segments, or raw transcript.rolespeakerSpeakertext [z]: 
)getstripappendupperjoin)r   r   linessegr   r    r   7/home/aiteam/pcaa-dev/dashboard-backend/agent_runner.py_format_transcript"   s   
r    templatecontextDict[str, Any]c                 C  s2   |  D ]\}}| d| d t|pd} q| S )zHReplace ``{key}`` placeholders in *template* with values from *context*.{}r   )itemsreplacer   )r!   r"   keyvaluer   r   r   _render_template0   s   r*   scoring_parametersc                 C  sX  dd | D }|sdS dg}t dd |D }|d| d i }|D ]}|d	p,d
}||g | q$| D ]f\}}|d| d |D ]V}|dpRd}	|drZdnd}
|d|d  d|	 |
 d|dd d |dr|d|d   |dr|d|d   |dr|d|d   qJq;|d d|S ) zFormat a list of scoring parameter dicts into a structured prompt block.

    Only parameters with ``enabled != False`` are included.
    c                 S  s   g | ]}| d dur|qS )enabledF)r   .0pr   r   r   
<listcomp><   s    z3_build_scoring_parameters_block.<locals>.<listcomp>r   z SCORING PARAMETERS TO EVALUATE:
c                 s  s"    | ]}t |d pdV  qdS )	max_scorer   N)intr   r-   r   r   r   	<genexpr>A   s     z2_build_scoring_parameters_block.<locals>.<genexpr>zTotal possible score: z points
parameter_groupGeneralz
[]parameter_typeRequiredis_fatalu
    ⚠ FATALu     • parameter_namez (u
   ) — max r1   r   z ptscheck_descriptionz    Check: detailed_descriptionz    Detail: sample_utterancesz    Example: z
For each parameter return: score (0 to max_score), evidence (exact quote), reasoning, and applicable (true/false). If a FATAL parameter scores 0, set overall quality_score to 0.r   )sumr   r   
setdefaultr&   r   )r+   activer   totalgroupsr/   g
group_nameparamsptypefatalr   r   r   _build_scoring_parameters_block7   s8   .



rH   r   r   c           
   
   C  s>  zt |  W S  ty   Y nw dD ]l\}}| |}|dkr"qd}d}d}t| |d |dD ]M\}}|r;d}q2|dkrD|rDd}q2|d	krM|sM| }|s||krX|d
7 }q2||kr|d
8 }|dkrzt | ||d
  W     S  ty~   Y  nw q2qtd| }	|	rz	t |	dW S  ty   Y dS w dS )z3Extract the first JSON object or array from *text*.))r$   r%   )r   r6   r   FN)start\T"   z\{[\s\S]+\})	jsonloadsr   	Exceptionfind	enumerateresearchgroup)
r   
start_charend_charidxdepthin_strescapeichmatchr   r   r   _extract_json_   sT   

 r_   皙?   model_idsystem_promptuser_messagetemperaturefloat
max_tokensr2   c                 C  s   ddl }|jdtttd}dd|igdg}|rd|igng }|j| ||||dd	}	|	d
i }
|
di dg }|D ]}d|v rI|d   S q=dS )zFInvoke an AWS Bedrock Converse-API model and return the text response.r   Nzbedrock-runtime)service_nameregion_nameaws_access_key_idaws_secret_access_keyuserr   r   content)re   	maxTokens)modelIdmessagessysteminferenceConfigoutputmessagern   r   )boto3client_AWS_REGION_AWS_KEY_AWS_SECRETconverser   )rb   rc   rd   re   rg   rv   rw   rq   rr   responsert   rn   blockr   r   r   _call_bedrock   s,   r~   c           	      C  sn   ddl }tdd}| d|dd|dgd||d	d
}|j| d|dd}|  | di ddS )z7Call a local Ollama model and return the response text.r   NOLLAMA_BASE_URLzhttp://localhost:11434rr   rm   rl   F)re   num_predict)modelrq   streamoptionsz	/api/chatx   )rN   timeoutru   rn   r   )requestsosgetenvpostraise_for_statusrN   r   )	rb   rc   rd   re   rg   req_libbase_urlpayloadrespr   r   r   _call_ollama   s   	r   c                   @  s*   e Zd ZdZdddZ		ddddZdS )AgentRunnerzRun all enabled AI agents for a bid against a given call transcript.

    Parameters
    ----------
    db_handler:
        Shared ``DatabaseHandler`` instance (used to load agent configs).
    r   Nonec                 C  s
   || _ d S )N)_db)self
db_handlerr   r   r   __init__   s   
zAgentRunner.__init__Nbidr   callidr   r   Optional[List[Dict]]call_metadataOptional[Dict[str, Any]]r#   c                 C  s  | j |}|std|| g g dS t||pg }|||d|p#i }g g d}	d}
d}|D ]}|dds:q0|d }|dpDd	 }|d
pMt}|dpTd}|dp[d}t|dpcd}t	|dpld}g }|d}t
|trzt|}W n ty   i }Y nw t
|tr|dpg }t|}i |d|i}t||}|rd|vr| d| }td|||||t| zd|d	krt|||||}n|dkrt|||||}ntd| t|}|du rd|i}||	|< |	d | t
|tr'|
sd|v rt|d |	d< d}
|s'd|v r't|d |	d< d}W q0 tyM } ztjd||||dd  |	d! | W Y d}~q0d}~ww |	S )"a  Run all enabled agents and return an aggregated analysis dict.

        Returns
        -------
        dict with keys:
            - ``<agent_name>``: parsed JSON output for each agent
            - ``quality_score``: float, promoted from the first agent that emits it
            - ``summary``: str, promoted from the first agent that emits it
            - ``agents_run``: list of agent names that were executed
            - ``agents_failed``: list of agent names that raised an error
        z'[%s][%s] No agent configs found for bid)
agents_runagents_failed)r   r   r   Fagent_enabledT
agent_namemodel_providerbedrockrb   rc   r   user_prompt_templatez{transcript}re   r`   rg   ra   runtime_configr+   z{scoring_parameters}z

z9[%s][%s] Running agent '%s' via %s/%s (scoring_params=%d)ollamazUnknown model provider: N
raw_outputr   quality_scoresummaryz[%s][%s] Agent '%s' failed: %s)exc_infor   )r   get_agent_configsloggerinfor    r   lower_DEFAULT_BEDROCK_MODELrf   r2   
isinstancer   rN   rO   rP   dictrH   r*   lenr~   r   
ValueErrorr_   r   error)r   r   r   r   r   r   configs	formattedr"   resultquality_score_setsummary_setcfgr   providerrb   rc   user_templatere   rg   r+   raw_cfgscoring_blockcontext_with_paramsrd   raw_textparsedexcr   r   r   run   s   









zAgentRunner.run)r   r   )NN)r   r   r   r   r   r   r   r   r   r   r   r#   )__name__
__module____qualname____doc__r   r   r   r   r   r   r      s    
r   )r   r   r   r   r   r   )r!   r   r"   r#   r   r   )r+   r   r   r   )r   r   r   r   )r`   ra   )rb   r   rc   r   rd   r   re   rf   rg   r2   r   r   )r   
__future__r   rN   loggingr   rS   typingr   r   r   r   	getLoggerr   r   r   rx   ry   rz   r   r    r*   rH   r_   r~   r   r   r   r   r   r   <module>   s.    






(3'