o
    ico                  
   @  s  d dl mZ d dlmZ d dlmZ 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# 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-m0Z0 d d)l-m1Z1 d d*l-m2Z2 d d+l-m3Z3 d d,l-m4Z4 d d-l-m5Z5 d d.l6m7Z7 erd d/l8m9Z9 ee:Z;e<e=ee=e<e=e	f e>e= e>e<e=e=f  e>e/ f f Z?eeee e!e,fZ@dd6d7ZAG d8d9 d9ZBdd?d@ZCddDdEZDddFdGZEddHdIZFddKdLZGddMdNZHdOdP ZIddRdSZJddUdVZKdWdX ZLddZd[ZMddadbZNddddeZOddgdhZPddidjZQddldmZRddndoZSddpdqZTddrdsZU																										ddddZVdddZWdddZXdddZYeG dd dZZG dd dZ[dS )    )annotations)asdict)	dataclass)is_dataclassN)TYPE_CHECKING)Any)Iterator)Optional)Sequence)Union)cast)config)	SpanTypes)
get_logger)format_trace_id)CLAUDE_AGENT_SDK_APM_SPAN_NAME)CREWAI_APM_SPAN_NAME)DEFAULT_PROMPT_NAME)GEMINI_APM_SPAN_NAME)INPUT_PROMPT)INTERNAL_CONTEXT_VARIABLE_KEYS)INTERNAL_QUERY_VARIABLE_KEYS)IS_EVALUATION_SPAN)LANGCHAIN_APM_SPAN_NAME)LITELLM_APM_SPAN_NAME)LLMOBS_STRUCT)LLMOBS_TRACE_ID)ML_APP)NAME)OPENAI_APM_SPAN_NAME)PARENT_ID_KEY)PROPAGATED_ML_APP_KEY)
SESSION_ID)	SPAN_KIND)
SPAN_LINKS)VERTEXAI_APM_SPAN_NAME)Document)Message)Prompt)ToolDefinition)_Meta)_MetaIO)
_SpanField)	_SpanLink)Span)LLMObsSpanDatapromptUnion[dict[str, Any], Prompt]strict_validationboolreturnValidatedPromptDictc                   s.  t | tstdt| j dtj}| d}| d}| d}| d}| d}| d}| d	}	| d
}
| d}| d}| d}|r`|d u rTtd|d u r`|	d u r`td|rh|	rhtd|pp| dt	 }|
pudg}|pzdg}t |t
std| dt|j t |trtdd |D stdt|j t |trtdd |D stdt|j |rt |t
std| dt|j |rt |t
std| dt|j |rt |tstd| dt|j tdd |D std td!d | D std"|r.t |t
s.td#| dt|j |	rWt |	ts;td$|	D ] t  trQt fd%dd&D sUtd'q=|r{t |tsmtd(| d)t|j td*d |D s{td+g }|	r|	D ]}|t|d, |d- d& q|rt |t
std.| dt|j |rt |t
std/| dt|j i }|r||d< |r||d0< |r||d< |r||d< |r||d< |r||d< |r||d	< |r||d< |r ||t< |r||t< |r||d< |r||d< |S )1Nz&Prompt must be a dictionary, received .idversionlabeltags	variablestemplatechat_templaterag_context_variablesrag_query_variablesprompt_uuidprompt_version_uuidz'id' must be providedzKOne of 'template' or 'chat_template' must be provided to annotate a prompt.zDOnly one of 'template' or 'chat_template' can be provided, not both._contextquestionz
prompt_id z must be a string, received c                 s      | ]}t |tV  qd S N
isinstancestr.0i rM   I/home/ubuntu/.local/lib/python3.10/site-packages/ddtrace/llmobs/_utils.py	<genexpr>f       z#_validate_prompt.<locals>.<genexpr>z2ctx_variables must be a list of strings, received c                 s  rE   rF   rG   rJ   rM   rM   rN   rO   i   rP   z4query_variables must be a list of strings, received z	version: zlabel: ztags: z: must be a dictionary of string key-value pairs, received c                 s  rE   rF   rG   rK   krM   rM   rN   rO   x   rP   z#Keys of 'tags' must all be strings.c                 s  rE   rF   rG   rQ   rM   rM   rN   rO   z   rP   z%Values of 'tags' must all be strings.z
template: zPchat_template must be a list of dictionaries with string-string key value pairs.c                 3  s    | ]}| v V  qd S rF   rM   rQ   ctrM   rN   rO      s    )rolecontentz[Each 'chat_template' entry should be a string-string dictionary with role and content keys.zvariables: z1 must be a dictionary with string keys, received c                 s  rE   rF   rG   rQ   rM   rM   rN   rO      rP   z(Keys of 'variables' must all be strings.rU   rV   zprompt_uuid: zversion_uuid: ml_app)rH   dict	TypeErrortype__name__r   _llmobs_ml_appget
ValueErrorr   rI   listallvaluesappendr'   r   r   )r0   r2   rW   	prompt_idr8   r9   r:   r;   r<   r=   ctx_variable_keysquery_variable_keysr@   version_uuidfinal_prompt_idfinal_ctx_variable_keysfinal_query_variable_keysfinal_chat_templatemsgvalidated_promptrM   rS   rN   _validate_promptE   s   















$rm   c                   @  s4   e Zd Zdd Zdd Zdd Zdd Zd	d
 ZdS )AnnotationContextc                 C  s   || _ || _d S rF   )_register_annotator_deregister_annotator)selfro   rp   rM   rM   rN   __init__   s   
zAnnotationContext.__init__c                 C     |    d S rF   ro   rq   rM   rM   rN   	__enter__      zAnnotationContext.__enter__c                 C  rs   rF   rp   rq   exc_typeexc_valexc_tbrM   rM   rN   __exit__   rw   zAnnotationContext.__exit__c                      |    d S rF   rt   ru   rM   rM   rN   
__aenter__      zAnnotationContext.__aenter__c                   r~   rF   rx   ry   rM   rM   rN   	__aexit__   r   zAnnotationContext.__aexit__N)r[   
__module____qualname__rr   rv   r}   r   r   rM   rM   rM   rN   rn      s    rn   oobjectattrrI   defaultc                 C  s"   t | tr| ||S t| ||S rF   )rH   rX   r]   getattr)r   r   r   rM   rM   rN   	_get_attr   s   
r   spanr.   Optional[Span]c                 C  s(   | j }|r|jtjkr|S |j }|sdS )z=Return the nearest LLMObs-type ancestor span of a given span.N)_parent	span_typer   LLM)r   parentrM   rM   rN   _get_nearest_llmobs_ancestor   s   r   c                 C  sn   | j tv r| jdkr| jS | j tkr%| jdkr%| dpd}d|| jS t| }|tj	p6| 
t	p6| j S )N zopenai.request.providerOpenAIz{}.{})nameSTANDARD_INTEGRATION_SPAN_NAMESresourcer   get_tagformat_get_llmobs_data_metastructr]   r   r   _get_ctx_item)r   client_namellmobs_datarM   rM   rN   _get_span_name   s   r   c                 C  sh   t | }|tjp| t}|r|S t| }|r2t |}|tjp'|t}|r,|S t|}|sdS )z
    Return whether or not a span is an evaluation span by checking the span's
    nearest LLMObs span ancestor. Default to 'False'
    F)r   r]   r   r   r   r   )r   r   is_evaluation_spanllmobs_parentparent_llmobs_datarM   rM   rN   _is_evaluation_span   s   r   Optional[str]c                 C  s   t | }|tjp| t}|r|S t| }|r4t |}|tjp'|t}|dur.|S t|}|s|pB| jjtpBt	j
pBt	jS )z
    Return the ML app name for a given span, by checking the span's nearest LLMObs span ancestor.
    Default to the global config LLMObs ML app name otherwise.
    N)r   r]   r   r   r   r   rC   _metar!   r   r\   service)r   r   rW   r   r   rM   rM   rN   _get_ml_app   s   r   c                 C  sl   t | }|tjp| t}|r|S t| }|r4t |}|tjp'|t}|dur.|S t|}|s|S )z\Return the session ID for a given span, by checking the span's nearest LLMObs span ancestor.N)r   r]   r   r"   r   r   )r   r   
session_idr   r   rM   rM   rN   _get_session_id  s   r   c                 C  s6   zt | W S  ty   td dt|  Y S w )NzaI/O object is neither JSON serializable nor string-able. Defaulting to placeholder value instead.z[Unserializable object: {}])rI   	Exceptionlogwarningr   repr)objrM   rM   rN   _unserializable_default_repr  s   

r   Tc                 C  sb   t | tr| S zt| drt| jr|  } tj| |dtdW S  ty0   t	j
ddd Y d S w )N
model_dumpT)ensure_asciiskipkeysr   z#Failed to serialize object to JSON.)exc_info)rH   rI   hasattrcallabler   jsondumpsr   r   r   error)r   r   rM   rM   rN   	safe_json%  s   
r   valuec              	   C  s8   zt | }W |S  t jtfy   dt| i}Y |S w )Nr   )r   loadsJSONDecodeErrorrY   rI   )r   loaded_valuerM   rM   rN   safe_load_json1  s   r   c                 C  s   t | tttfrdd | D S t | trdd |  D S t | tr%| jS t| dr0| j	ddS t
| r8t| S t | ttttfsE| d u rG| S t| }zt|W S  tjy]   | Y S w )Nc                 S  s   g | ]}t |qS rM   )load_data_value)rK   itemrM   rM   rN   
<listcomp>;  s    z#load_data_value.<locals>.<listcomp>c                 S  s   i | ]\}}t |t|qS rM   )rI   r   )rK   rR   vrM   rM   rN   
<dictcomp>=      z#load_data_value.<locals>.<dictcomp>r   T)exclude_none)rH   r_   tuplesetrX   itemsrZ   r[   r   r   r   r   intfloatrI   r3   r   r   r   r   )r   	value_strrM   rM   rN   r   9  s$   


r   	tool_argsc              	   C  s>   t | }ztjt| dd}W |S  tjtfy   Y |S w )z
    Format tool call arguments as a JSON string with no unnecessary whitespace.

    This is used to ensure that tool call arguments are properly formatted for span linking purposes.
    ),:)
separators)rI   r   r   r   r   rY   )r   formatted_tool_argsrM   rM   rN   format_tool_call_argumentsN  s   r   span_idtrace_idfrom_ioto_ioNonec                 C  s2   t | }|t||||dd | t| d S )N)fromto)r   r   
attributes)get_span_linksrb   r-   _set_ctx_itemr$   )r   r   r   r   r   current_span_linksrM   rM   rN   add_span_link\  s   r   list[_SpanLink]c                 C  s&   t | }|tjp| tpg }|S rF   )r   r]   r   r$   r   )r   r   r   rM   rM   rN   r   h  s   r   Optional[Prompt]c                 C  sb   t | }|d u r
d S t|}|r*|tji tji }|r&|tj}|S d }|S |t}|S rF   )	r   r   r]   r   METAINPUTPROMPTr   r   )r   parent_spanr   parent_llmobs_inputparent_promptrM   rM   rN   _get_parent_promptp  s   
r   c                 C  s*   t | }d|tj< | tjtt| dS )z7Mark a span as an evaluation span in span._meta_struct.TN)r   r   r   _set_struct_tagKEYr   rX   )r   r   rM   rM   rN   mark_as_evaluation_span~  s   
r   r/   c                 C  s   t d| tj}|pi S )z@Get the llmobs data from span._meta_struct or return empty dict.r/   )r   _get_struct_tagr   r   )r   llmobs_span_datarM   rM   rN   r     s   r   c                 C  s>   t | }|tji }|tji tj}|r|S | tS )zLGet the span kind, checking meta_struct first then falling back to ctx_item.)r   r]   r   r   SPANKINDr   r#   )r   r   llmobs_metakindrM   rM   rN   _get_span_kind  s   
r   c                 C  &   t | }|tj}|r|S | tS )zSGet the LLMObs parent ID, checking meta_struct first then falling back to ctx_item.)r   r]   r   	PARENT_IDr   r    )r   r   	parent_idrM   rM   rN   _get_llmobs_parent_id  
   
r   c                 C  r   )zRGet the LLMObs trace ID, checking meta_struct first then falling back to ctx_item.)r   r]   r   TRACE_IDr   r   )r   r   r   rM   rM   rN   _get_llmobs_trace_id  r   r   r   r   rW   
model_namemodel_providermetadataOptional[dict[str, Any]]metricsr:   Optional[dict[str, str]]input_messagesOptional[list[Message]]input_valueinput_documentsOptional[list[Document]]output_messagesoutput_valueoutput_documentstool_definitionsOptional[list[ToolDefinition]]r   
span_linksOptional[list[_SpanLink]]agent_manifestr   expected_outputOptional[Any]experiment_inputexperiment_outputintentr   c                 C  sF  t | }zzQ|tjt }|tjt  |tjt  |tjt	|p(dd |tj
i  |tji  |tji  |durJ||tj< |durS||tj< |dur\||tj< |dure||tj< |durq||tj tj< |durz||tj< |dur||tj< |dur|tj
 | |dur||tj
 tj< |dur|tj | |dur|tj | |dur||tj< |dur||tj< |dur||tj< |	dur|	|tj tj< |
dur|
|tj tj< |dur||tj tj< |dur||tj tj< |dur||tj tj< |dur||tj tj< |dur%||tj tj< |dur/||tj< |dur9||tj< |durC||tj< |durM||tj< |durW||tj< W n t yq } zt!"d| W Y d}~nd}~ww W | #tj$t%t&t't(f | dS W | #tj$t%t&t't(f | dS | #tj$t%t&t't(f | w )zAnnotate llmobs data on span meta_struct field.

    metadata, metrics, and tags are updated on any existing metadata/metrics/tags
    instead of being overwritten.
    r   )r   Nz%Error auto-annotating llmobs data: %s))r   
setdefaultr   r   r*   r   r+   OUTPUTr   r,   METADATATAGSMETRICSr   r   r   r   r   
MODEL_NAMEMODEL_PROVIDERupdateAGENT_MANIFESTr"   r$   CONFIGMESSAGESVALUE	DOCUMENTSr   TOOL_DEFINITIONSEXPECTED_OUTPUTINTENTr   r   r   r   r   r   rX   rI   r   )r   r   r   rW   r   r   r   r   r:   r   r   r   r0   r   r  r  r  r   r  r  r   r  r
  r  r  r   r   r   metaerM   rM   rN   _annotate_llmobs_span_data  s   "





















 >r  messageslist[Message]c                 C  s   | D ]}| dd q| S )zFEnforce that each message includes a role (empty "" by default) field.rU   r   )r  )r   messagerM   rM   rN   enforce_message_role  s   r#  dict[str, str]	list[str]c                 C  s   | sg S dd |   D S )Nc                 S  s   g | ]\}}| d | qS )r   rM   )rK   keyr   rM   rM   rN   r     r   z-convert_tags_dict_to_list.<locals>.<listcomp>)r   )r:   rM   rM   rN   convert_tags_dict_to_list  s   r'  iterabler
   nr   Iterator[Sequence]c                 c  s.    t dt| |D ]}| |||  V  q	d S )Nr   )rangelen)r(  r)  rL   rM   rM   rN   _batched  s   r-  c                   @  sJ   e Zd ZU dZded< ded< ded< ded< dZd	ed
< dZded< dS )TrackedToolCallzn
    Holds information about a tool call and its associated LLM/Tool spans
    for span linking purposes.
    rI   tool_id	tool_name	argumentsr$  llm_span_contextNr   tool_span_contextfunction	tool_kind)r[   r   r   __doc____annotations__r3  r5  rM   rM   rM   rN   r.     s   
 r.  c                   @  sZ   e Zd ZdZd ddZd!ddZ	d"d#ddZd$ddZd%ddZd%ddZ	d ddZ
dS )&LinkTrackerz
    This class is used to create span links across integrations.

    The primary use cases are:
    - Linking LLM spans to their associated tool spans and vice versa
    - Linking LLM spans to their associated guardrail spans and vice versa
    r4   r   c                 C  s   i | _ i | _t | _d | _d S rF   )_tool_calls_lookup_tool_idr   _active_guardrail_spans_last_llm_spanru   rM   rM   rN   rr   8  s   
zLinkTracker.__init__r/  rI   r0  r1  r2  r$  c                 C  sH   t |}| j||frdS t||||d}|| j|< || j||f< dS )a  
        Called when an llm span finishes. This is used to save the tool choice information generated by an LLM
        for future span linking purposes.

        We make the assumption that there is only one ongoing tool call per (tool_name, arguments) pair possible.
        This is to avoid some issues with parsing tool calls from ReAct agents which format tool calls as plain text.
        N)r/  r0  r1  r2  )r   r:  r]   r.  r9  )rq   r/  r0  r1  r2  formatted_arguments	tool_callrM   rM   rN   on_llm_tool_choice>  s   

zLinkTracker.on_llm_tool_choiceNtool_argr5  	tool_spanr.   r   c                 C  s   t |}|p| j||f}|sdS | j|}|sdS t||jd |jd dd t|jt|j	d| j| _
|| j| _| j||fd dS )a  
        Called when a tool span finishes. This is used to link the input of the tool span to the output
        of the LLM span responsible for generating its input. We also save the span/trace id of the tool call
        so that we can link to the input of an LLM span if its output is used in an LLM call.

        If possible, we use the tool_id provided to lookup the tool call; otherwise, we perform a best effort
        lookup based on the tool_name and tool_arg.
        Nr   r   outputinput)r   r   )r   r:  r]   r9  r   r2  rI   r   r   r   r3  r5  pop)rq   r0  r@  r5  rA  r/  formatted_tool_argr>  rM   rM   rN   on_tool_callU  s&   zLinkTracker.on_tool_callllm_spanc                 C  s<   | j |d}|r|jsdS t||jd |jd dd dS )aH  
        Called when an LLM span finishes. This is used to link the output of a tool call to the input of an
        LLM span.

        The tool call is removed from the tracker since we only want to link the output of a tool call to the FIRST
        LLM call that has that tool call as an input to reduce noisy links.
        Nr   r   rB  rC  )r9  rD  r3  r   )rq   r/  rG  r>  rM   rM   rN   on_tool_call_output_usedu  s   
z$LinkTracker.on_tool_call_output_usedr   c                 C  s`   || _ t }| jD ]}t|t|kr&t|t|jt|jdd |	| q	|  j|8  _dS )z
        Called when an LLM span event is created. If the LLM span is the first LLM span,
        it will consume all active guardrail links.
        rB  rC  N)
r<  r   r;  r   r   rI   r   r   r   add)rq   r   spans_to_removeguardrail_spanrM   rM   rN   on_llm_span_finish  s   

zLinkTracker.on_llm_span_finishc                 C  sT   | j | | jdur&t|t| jkr(t|t| jjt| jjdd dS dS dS )z
        Called when a guardrail span starts. This is used to track the active guardrail
        spans and link the output of the last LLM span to the input of the guardrail span.
        NrB  rC  )	r;  rI  r<  r   r   rI   r   r   r   )rq   r   rM   rM   rN   on_guardrail_span_start  s   

z#LinkTracker.on_guardrail_span_startc                 C  s
   d| _ dS )z
        Called when an OpenAI agent span finishes. This is used to reset the last LLM span
        since output guardrails are only linked to the last LLM span for a particular agent.
        N)r<  ru   rM   rM   rN   on_openai_agent_span_finish  s   
z'LinkTracker.on_openai_agent_span_finish)r4   r   )
r/  rI   r0  rI   r1  rI   r2  r$  r4   r   rF   )r0  rI   r@  rI   r5  rI   rA  r.   r/  r   r4   r   )r/  rI   rG  r.   r4   r   r   r.   r4   r   )r[   r   r   r6  rr   r?  rF  rH  rL  rM  rN  rM   rM   rM   rN   r8  /  s    


 

r8  )r0   r1   r2   r3   r4   r5   )r   r   r   rI   r   r   )r   r.   r4   r   )r   r.   r4   rI   )r   r.   r4   r3   )r   r.   r4   r   )T)r   rI   )r   rI   r4   rI   )r   r.   r   rI   r   rI   r   rI   r   rI   r4   r   )r   r.   r4   r   )r   r.   r4   r   rO  )r   r.   r4   r/   )NNNNNNNNNNNNNNNNNNNNNNNNNN)8r   r.   r   r   r   r   rW   r   r   r   r   r   r   r   r   r   r:   r   r   r   r   r   r   r   r0   r   r   r   r  r   r  r   r  r  r   r   r  r  r  r   r   r   r  r	  r
  r   r  r   r  r   r   r   r   r   r4   r   )r   r!  r4   r!  )r:   r$  r4   r%  )r(  r
   r)  r   r4   r*  )\
__future__r   dataclassesr   r   r   r   typingr   r   r   r	   r
   r   r   ddtracer   ddtrace.extr   ddtrace.internal.loggerr   ddtrace.internal.utils.formatsr   ddtrace.llmobs._constantsr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   ddtrace.llmobs.typesr&   r'   r(   r)   r*   r+   r,   r-   ddtrace.tracer.   ddtrace.llmobs._writerr/   r[   r   rX   rI   r_   r5   r   rm   rn   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r#  r'  r-  r.  r8  rM   rM   rM   rN   <module>   s    6

s



















	
g

