o
    i>`                     @   sl  d dl mZ d dlZd dlmZ d dlmZ d dlmZ d dlm	Z	 d dl
mZ d dlmZ d d	lmZ d d
lmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ d dl m!Z! d dl"m#Z# d dl"m$Z$ ee%Z&dddddddZ'G dd  d eZ(d!e)fd"d#Z*d$d% Z+dS )&    )IterableN)Any)Optional)WeakKeyDictionary)core)
get_logger)get_argument_value)format_trace_id)AGENT_MANIFEST)DISPATCH_ON_TOOL_CALL)INPUT_VALUE)METADATA)NAME)OUTPUT_VALUE)PARENT_ID_KEY)ROOT_PARENT_ID)	SPAN_KIND)
SPAN_LINKS)BaseLLMIntegration)_get_nearest_llmobs_ancestor	safe_json)	_SpanLink)Span)tracerworkflowtaskagenttool)crewr   r   r   flowflow_methodc                       s^  e Zd ZU dZi Zeeee f ed< i Z	eeeee
f f ed< g Zee ed< e Zeeeeeee
f f f ed< d/deded	eee
f d
ef fddZdd Z		d0dedee
 d	eee
f dee
 ded
dfddZdd Zdd Zdd Zdd Zdd  Zd!d" Zd#d$ Zd%d& Zd'd( Zd)d* Zdee de
d	e
d
dfd+d,Zd-d. Z  Z S )1CrewAIIntegrationcrewai_crews_to_task_span_ids_crews_to_tasks_planning_crew_ids!_flow_span_to_method_to_span_dictFoperation_idsubmit_to_llmobskwargsreturnc                    s  | dr|d \}}tj| | jr|rtd|f t j||fi |}| ddkrLt	|d}g | j
|< i | j|< | ddrJ| j| |S | ddkrt	|d}| dd	}| j
 |g t|j | j |i t|i }	t|j|	d
< | ddkri | j|< | ddkr| dd	}
t|
tr|
nd	}|jd u r|S | j |ji |i }|d
t|ji |S )N_ddtrace_ctxzthreading.execution	operationr   planningFr   instance_id span_idr    r!   	span_name)getr   context_provideractivatellmobs_enabledr   dispatchsupertrace_get_crew_idr$   r%   r&   appendstrr1   
setdefaultr'   
isinstance_parentupdate)selfr(   r)   r*   
tracer_ctx
llmobs_ctxspancrew_idtask_id	task_noder2   method_name	span_dict	__class__ W/home/ubuntu/.local/lib/python3.10/site-packages/ddtrace/llmobs/_integrations/crewai.pyr9   3   s:   







zCrewAIIntegration.tracec                 C   s.   t  }| jrtddjj}||fS |dfS )zcExtract current tracer and llmobs contexts to propagate across threads during async task execution.zthreading.submitrL   N)r   current_trace_contextr6   r   dispatch_with_resultsrC   value)rA   curr_trace_ctxcurr_llmobs_ctxrL   rL   rM   _get_current_ctxT   s   z"CrewAIIntegration._get_current_ctxNr0   rD   argsresponser-   c                 C   s   | tt|d |dkr8t|d}| |||| | j|d  | j|d  || j	v r6| j	
| d S d S |dkrF| |||| d S |dkrT| |||| d S |dkrb| |||| d S |dkrp| |||| d S |dkr~| |||| d S d S )Nr   r   r   r   r    r!   )_set_ctx_itemr   OP_NAMES_TO_SPAN_KINDr3   r:   _llmobs_set_tags_crewr$   popr%   r&   remove_llmobs_set_tags_task_llmobs_set_tags_agent_llmobs_set_tags_tool_llmobs_set_tags_flow_llmobs_set_tags_flow_method)rA   rD   rT   r*   rU   r-   rE   rL   rL   rM   _llmobs_set_tags^   s(   

z"CrewAIIntegration._llmobs_set_tagsc                 C   s   | dd }t|d}| j|g }|r5|d }t|t|jdddd}	|tp+g }
|	t|
|	g  t
|ddt
|d	dt
|d
dt
|ddt
|ddd}t||ddddp[d}|t|tdt|i |jrld S |	tt
|dd d S )N_dd.instancer   outputfromtor1   trace_id
attributesprocessr0   r.   cacheverbosememory)rj   r.   rk   rl   rm   r   inputsToptionalzCrewAI Crewraw)rY   r:   r$   r3   r   r	   rh   _get_ctx_itemr   rV   getattrr   _set_ctx_itemsr   r   r   errorr   )rA   rD   rT   r*   rU   crew_instancerE   task_span_idslast_task_span_id	span_linkcurr_span_linksmetadatarn   rL   rL   rM   rX   y   s.   





z'CrewAIIntegration._llmobs_set_tags_crewc                 C   s>  t |d}|dd }t|dd }t|dd}t|dd}	t|dd|r.t|dkr.|d	 ndt|d
dt|ddt|ddd}
|r| j| t|i dg }| |rpt|}t	t|j
t|jdddd}|| |tpvg }|t||  |t|r|ndt|
t|	i |jrd S |tt|dd d S )Nr   ra   idnamer0   descriptionexpected_output      async_executionFhuman_inputoutput_file)r   contextr   r   r   
span_linksinputrd   rg   zCrewAI Taskrq   )r:   rY   rs   lenr%   r3   r<   _is_planning_taskr   r   r1   r	   rh   r;   rr   r   rV   rt   r   r   r   ru   r   )rA   rD   rT   r*   rU   rE   task_instancerF   	task_nametask_descriptionr{   r   parent_spanry   rz   rL   rL   rM   r[      s:   






z'CrewAIIntegration._llmobs_set_tags_taskc              	   C   s  | dd}| || t|dd}t| ddd}t||ddd	d
p%d}t|}	tt|jt|j	dddd}
|	
tp@g }|	t||
g  tt|	jt|j	dddd}|
tp`g }|t|rh|ndt||dt||g i |jr{dS |t| dS )zSet span links and metadata for agent spans.
        Agent spans are 1:1 with its parent (task/tool) span, so we link them directly here, even on the parent itself.
        ra   Nroler0   r   r~   r   r   Tro   rc   rd   rg   r   CrewAI Agent)r   r   )r3   _tag_agent_manifestrs   r   r   r   r<   r1   r	   rh   rr   r   rV   rt   r   r   ru   r   )rA   rD   rT   r*   rU   agent_instance
agent_roler   r   r   parent_span_linkrz   ry   rL   rL   rM   r\      s8   

z(CrewAIIntegration._llmobs_set_tags_agentc           	   	   C   s   | dd }t|dd}tt|dd}|dd}|t|r!|ndtd|it|i |jr0d S |	t
| zt|trAt|}dd | D }W n tjtfy^   tjd	d
d Y nw tt|t|d|f d S )Nra   r}   r0   r~   r   zCrewAI Toolc                 S   s   i | ]\}}|d kr||qS )security_contextrL   .0kvrL   rL   rM   
<dictcomp>   s    z;CrewAIIntegration._llmobs_set_tags_tool.<locals>.<dictcomp>z6Failed to filter out security context from tool input.T)exc_infofunction)rY   rs   _extract_tool_description_fieldr3   rt   r   r   r   ru   rV   r   r>   r<   jsonloadsitemsJSONDecodeError	TypeErrorlogwarningr   r7   r   r   )	rA   rD   rT   r*   rU   tool_instance	tool_namer~   
tool_inputrL   rL   rM   r]      s0   

z'CrewAIIntegration._llmobs_set_tags_toolc                 C   sR  |sd S i }d|d< t |dr|jr|jnd|d< t |dr#|j|d< t |dr-|j|d< t |dr^t |jd	r>|jj|d	< i }t |jd
rL|jj|d
< t |jdrX|jj|d< |r^||d< t |drjd|ji|d< i }t |drxd|j	i|d< t |drd|j
i|d< |r||d< t |dr|j|d< t |dr| |j|d< |t| d S )NCrewAI	frameworkr   r   r}   goal	backstoryllmmodel
max_tokenstemperaturemodel_settingsallow_delegationhandoffsallow_code_executioncode_execution_permissionscode_execution_modemax_itermax_iterationstools)hasattrr   r   r   r   r   r   r   r   r   r   r   _get_agent_toolsr   rV   r
   )rA   rD   r   manifestr   r   rL   rL   rM   r      sB   










z%CrewAIIntegration._tag_agent_manifestc                 C   sZ   |rt |ts	g S g }|D ]}i }t|dr|j|d< t|dr%|j|d< || q|S )Nr}   r~   )r>   listr   r}   r~   r;   )rA   r   formatted_toolsr   	tool_dictrL   rL   rM   r     s   



z"CrewAIIntegration._get_agent_toolsc              	   C   s:   t ||ddddp
i }|t|jpdt|tt|i d S )Nr   rn   Tro   zCrewAI Flow)r   rt   r   r}   r   r   r<   )rA   rD   rT   r*   rU   rn   rL   rL   rM   r^   $  s    z'CrewAIIntegration._llmobs_set_tags_flowc           
   
   C   s  | dd }| di }dd |dd  D dd | D |d}| j|ji |ji d	g }|jt|d
g v rU|jd urU|tt	|jj
t|jdddd |jt|dg v rz| j|ji t	|i }	|	t	|j
t|jd |t|jpdt|tt	|t|i d S )Nra   z_dd.initial_flow_statec                 S   s   g | ]}t |qS rL   r   )r   argrL   rL   rM   
<listcomp>-  s    zBCrewAIIntegration._llmobs_set_tags_flow_method.<locals>.<listcomp>r   c                 S   s   i | ]	\}}|t |qS rL   r   r   rL   rL   rM   r   .  s    zBCrewAIIntegration._llmobs_set_tags_flow_method.<locals>.<dictcomp>)rT   r*   
flow_stater   _start_methodsr   rd   rg   _routers)r1   rh   zFlow Method)rY   r   r'   r3   r?   r}   rs   r;   r   r<   r1   r	   rh   r=   r@   rt   r   r   r   r   )
rA   rD   rT   r*   rU   flow_instanceinitial_flow_state
input_dictr   rI   rL   rL   rM   r_   )  s6    

z.CrewAIIntegration._llmobs_set_tags_flow_methodc              	   C   s:   | j sd S z| |||| W d S  ttfy   Y d S w )N)r6   _llmobs_set_span_link_on_flowKeyErrorAttributeError)rA   	flow_spanrT   r*   r   rL   rL   rM   llmobs_set_span_links_on_flowM  s   z/CrewAIIntegration.llmobs_set_span_links_on_flowc              
      s  t ||dddd}|sdS | j|i   |}|r#|t|dg v r%dS t|di }d}| D ]u\}	\}
}||vr<q1 |	i }|d	g }|
d
krcd}|tt|d t	|j
dddd q1t fdd|D roq1d}|D ]2} |i |ttd t	|j
dddd |tpg }fdd|D }|t| qsq1|du r|tpg }|tt|d t	|j
dddd |t| dS )a{  
        Set span links for the next queued listener method(s) in a CrewAI flow.

        Notes:
        - trigger_method is either a method name or router result, which trigger normal/router listeners respectively.
          We skip if trigger_method is a router method name because we use the router result to link triggered listeners
        - AND conditions:
            - temporary output->output span links added by default for all trigger methods
            - once all trigger methods have run for the listener, remove temporary output->output links and
              add span links from trigger spans to listener span
        r   trigger_methodTro   Nr   
_listenersFr   ANDr1   rc   r   rd   rg   c                 3   s&    | ]}  |i  d du V  qdS )r1   N)r3   )r   _trigger_method)flow_methods_to_spansrL   rM   	<genexpr>{  s
    
zBCrewAIIntegration._llmobs_set_span_link_on_flow.<locals>.<genexpr>c                    s$   g | ]}|d  t  d  kr|qS )r1   )r<   )r   link)method_span_dictrL   rM   r     s    zCCrewAIIntegration._llmobs_set_span_link_on_flow.<locals>.<listcomp>)r   r'   r3   rs   r   r=   r;   r   r<   r	   rh   anyrr   r   rV   )rA   r   rT   r*   r   r   trigger_span_dict	listeners	triggeredlistener_namecondition_typelistener_triggersrI   r   methodflow_span_span_links!span_links_minus_tmp_output_linksrL   )r   r   rM   r   U  sn   




z/CrewAIIntegration._llmobs_set_span_link_on_flowc              	   C   s
  | j sdS t||dddd}t||dddd}|du s|du r&td dS |du r1td	 dS t|d
d}t|d}|| jv }| j|i 	t
|i }	g }
tt|ddtr|jD ]*}t|d
d}| j|i t
|i }|d}|
t|t|jdddd q\|
|	d< dS |s|st|rt
|jntt|jddddg|	d< dS |r| j|g r| j| d }t|r|ntt|jddddg|	d< dS | j|g }tt|t|}td|d D ]}||  }|
t|t|jdddd q|
|	d< dS )a!  Set span links for the next queued task in a CrewAI workflow.
        This happens between task executions, (the current span is the crew span and the task span hasn't started yet)
        so we create span links to be set on the task span once it starts later.
        We rely on 3 cases to determine the appropriate span links:
        1. queued_task.context is set with the most recently finished tasks that directly feed into the queued task.
        2. queued_task.context is empty and there are no finished task outputs,
            meaning this is the first task (tasks if async) in the crew workflow.
        3. queued_task.context is empty, but there are n finished task outputs,
            meaning that the last n task outputs should be the pre-requisite tasks for the queued task.
        Nr   r   Tro   r   task_outputszENo queued task or finished task outputs found, skipping span linking.z-No current span found, skipping span linking.r|   r0   r   r   r1   rc   r   rd   rg   r   rb   )r6   r   r   debugrs   r:   r&   r%   r3   r=   r<   r>   r   r   r;   r   r	   rh   r1   r   r$   minr   range)rA   rD   rT   r*   queued_taskfinished_task_outputsqueued_task_idrE   is_planning_crew_instancequeued_task_noder   finished_taskfinished_task_idfinished_task_nodefinished_task_span_idplanning_task_span_idfinished_task_spansnum_tasks_to_linkirL   rL   rM   _llmobs_set_span_link_on_task  sv   







z/CrewAIIntegration._llmobs_set_span_link_on_taskc                 C   s<   t |d}| j|r| j| d t|jkrdS || jv S )a  Check if the current task is a planning task, since we need to add span links manually for planning tasks.
        This is done by checking if the task span is the first task in the crew execution and
        planning is enabled on the crew instance.
        r   r   F)r:   r$   r3   r<   r1   r&   )rA   rD   rE   rL   rL   rM   r     s   

z#CrewAIIntegration._is_planning_task)F)Nr0   )!__name__
__module____qualname___integration_namer$   dictr<   r   __annotations__r%   r   r&   r   r'   r   boolr9   rS   r   r`   rX   r[   r\   r]   r   r   r^   r_   r   r   r   r   __classcell__rL   rL   rJ   rM   r"   *   sF   
 &(!

 #%$HKr"   tool_descriptionc                 C   s$   |  dd}t|dkr| S |d S )NzTool Description: r   rb   )rsplitr   )r   fieldsrL   rL   rM   r     s   r   c                 C   sN   |dkrd| j  d| j S |dkr#| tp| j}d| j  d| S | j  S )z,Return the crew ID from a crew or task span.r   crew__r   )rh   r1   rr   r   	parent_id)rD   r-   r   rL   rL   rM   r:     s   r:   ),collections.abcr   r   typingr   r   weakrefr   ddtrace.internalr   ddtrace.internal.loggerr   ddtrace.internal.utilsr   ddtrace.internal.utils.formatsr	   ddtrace.llmobs._constantsr
   r   r   r   r   r   r   r   r   r   !ddtrace.llmobs._integrations.baser   ddtrace.llmobs._utilsr   r   ddtrace.llmobs.typesr   ddtrace.tracer   r   r   r   rW   r"   r<   r   r:   rL   rL   rL   rM   <module>   sN    
   N