o
    ưi                     @   s   d Z ddlZddlZddlZddlmZ ddlmZ ddlmZm	Z	m
Z
mZmZmZ ddlZddlZddlmZ ddlmZ ddlmZmZ dd	lmZmZmZ dd
lmZ ddlmZ ddlm Z m!Z! ddl"T ddl#m$Z$m%Z%m&Z&m'Z' G dd deZ(dS )z
Implements logging integration with Datadog's LLM Observability Service


API Reference: https://docs.datadoghq.com/llm_observability/setup/api/?tab=example#api-standards

    N)uuid)datetime)AnyDictListLiteralOptionalUnion)verbose_logger)CustomBatchLogger)should_use_datadog_mockcreate_mock_datadog_client)get_datadog_serviceget_datadog_tagsget_datadog_base_url_from_env)tracer)>handle_any_messages_to_chat_completion_str_messages_conversion)get_async_httpx_clienthttpxSpecialProvider)*)	CallTypes#StandardLoggingGuardrailInformationStandardLoggingPayload&StandardLoggingPayloadErrorInformationc                   @   s  e Zd Zdd ZdefddZdd Zdefd	d
Zdd Z	dd Z
dd ZdedededefddZdee fddZdedee fddZdedefddZdedee dee fddZ	 d7dee d!ee ded" fd#d$Zd%eeeee eeef f  dee fd&d'Zdedeeef fd(d)Zdedefd*d+Zdede fd,d-Z!dede"fd.d/Z#d%ee deeeef  fd0d1Z$e%d2eeeef  deeef fd3d4Z&dedeeef fd5d6Z'd S )8DataDogLLMObsLoggerc              
   K   s0  zt d t | _| jrt  t d td}ttj	d| _
td| _|r0| j|d ntdd d u r<tdtdd d u rHtd	|   t }|rW| d
| _t|   t | _g | _|  }|| tj| fi |d| ji W d S  ty } zt dt|  |d }~ww )Nz"DataDogLLMObs: Initializing loggerz<[DATADOG MOCK] DataDogLLMObs logger initialized in mock modeZLITELLM_DD_AGENT_HOST)Zllm_provider
DD_API_KEY)dd_agent_host*DD_API_KEY is not set, set 'DD_API_KEY=<>'DD_SITEzGDD_SITE is not set, set 'DD_SITE=<>', example sit = `us5.datadoghq.com`"/api/intake/llm-obs/v1/trace/spans
flush_lockz$DataDogLLMObs: Error initializing - )r
   debugr   is_mock_moder   osgetenvr   r   ZLoggingCallbackasync_clientr   _configure_dd_agent	Exception_configure_dd_direct_apir   
intake_urlasynciocreate_taskZperiodic_flushLockr    	log_queue_get_datadog_llm_obs_paramsupdater   __init__	exceptionstr)selfkwargsr   Zdd_base_urldict_datadog_llm_obs_paramse r7   c/home/app/Keep/.python/lib/python3.10/site-packages/litellm/integrations/datadog/datadog_llm_obs.pyr0   0   sD   




"zDataDogLLMObsLogger.__init__r   c                 C   s<   t dd}d| _d| d| d| _td| j  dS )	zK
        Configure the Datadog logger to send traces to the Agent.
        ZLITELLM_DD_LLM_OBS_PORTZ8126	localhostzhttp://:r   z!DataDogLLMObs: Using DD Agent at N)r#   r$   r   r)   r
   r!   )r3   r   Z
agent_portr7   r7   r8   r&   c   s
   z'DataDogLLMObsLogger._configure_dd_agentc                 C   s<   | j stdtd| _| jstdd| j d| _dS )zZ
        Configure the Datadog logger to send traces directly to the Datadog API.
        r   r   zHDD_SITE is not set, set 'DD_SITE=<>', example site = `us5.datadoghq.com`zhttps://api.r   N)r   r'   r#   r$   r   r)   )r3   r7   r7   r8   r(   r   s   z,DataDogLLMObsLogger._configure_dd_direct_apireturnc                 C   sL   i }t jdur$tt jtrt j }|S tt jtr$tdi t j }|S )z
        Get the datadog_llm_observability_params from litellm.datadog_llm_observability_params

        These are params specific to initializing the DataDogLLMObsLogger e.g. turn_off_message_logging
        Nr7   )litellmZ datadog_llm_observability_params
isinstanceZDatadogLLMObsInitParamsZ
model_dumpr   )r3   r5   r7   r7   r8   r.      s   
z/DataDogLLMObsLogger._get_datadog_llm_obs_paramsc              
         z6t d|dd  | |||}t d|  | j| t| j| jkr5|  I d H  W d S W d S  t	yT } zt 
dt|  W Y d }~d S d }~ww )Nz/DataDogLLMObs: Logging success event for model modelunknownDataDogLLMObs: Payload: z-DataDogLLMObs: Error logging success event - r
   r!   getcreate_llm_obs_payloadr-   appendlenZ
batch_sizeasync_send_batchr'   r1   r2   r3   r4   response_obj
start_timeend_timepayloadr6   r7   r7   r8   async_log_success_event   "   z+DataDogLLMObsLogger.async_log_success_eventc              
      r>   )Nz/DataDogLLMObs: Logging failure event for model r?   r@   rA   z-DataDogLLMObs: Error logging failure event - rB   rH   r7   r7   r8   async_log_failure_event   rN   z+DataDogLLMObsLogger.async_log_failure_eventc              
      s  z| j sW d S tdt| j  d | jrtd dtdtt t g| j ddi}dd	l	m
} z
td
|| W n tyW } ztdt| W Y d }~nd }~ww ||}ddi}| jrh| j|d< | jj| j||dI d H }|jdkrtd|j d|j | jrtdt| j  d n	td|j  | j   W d S  tjy } ztd|jj  W Y d }~d S d }~w ty } ztdt|  W Y d }~d S d }~ww )NzDataDogLLMObs: Flushing z eventsz@[DATADOG MOCK] Mock mode enabled - API calls will be intercepteddataspan)Zml_apptagsspans)type
attributesr   )
safe_dumpsz
payload %sz payload serialization failed: %szContent-Typezapplication/jsonz
DD-API-KEY)urlcontentheaders   z2DataDogLLMObs: Unexpected response - status_code: z, text: z[DATADOG MOCK] Batch of z events successfully mockedz6DataDogLLMObs: Successfully sent batch - status_code: z%DataDogLLMObs: Error sending batch - )r-   r
   r!   rF   r"   ZDDIntakePayloadZDDSpanAttributesr   r   Z*litellm.litellm_core_utils.safe_json_dumpsrV   r'   r2   r   r%   postr)   status_codetextclearhttpxZHTTPStatusErrorr1   response)r3   rL   rV   Zdebug_errorZjson_payloadrY   r`   r6   r7   r7   r8   rG      sr   



"z$DataDogLLMObsLogger.async_send_batchr4   rJ   rK   c                 C   s  | d}|d u rtd|d }| j|d}| di  di }tt|d}t| j|| ddd}| |}	d }
t|t	rF| d	}
t
| | d|
||| ||	d
}tt| ddt| ddt| ddt| dd| |d}t|
r|
nd| dtt | dtt | dd|t| d t||  d ||	rdndt|dgd
}|  }|d ur||d< |S )Nstandard_logging_objectz1DataDogLLMObs: standard_logging_object is not setmessages)rb   Zlitellm_paramsmetadata	call_type)standard_logging_payloadrd   	parent_id)kindinputoutputrc   errorZprompt_tokensr   Zcompletion_tokenstotal_tokensresponse_cost)Zinput_tokensZoutput_tokensrk   Z
total_costZtime_to_first_token	undefinedtrace_idspan_idnameZlitellm_llm_callg    eArj   ok)ra   )
rf   rn   ro   rp   metastart_nsdurationmetricsstatusrR   Zapm_id)rC   r'   _ensure_string_contentZ	InputMetar   Z
OutputMeta_get_response_messages_assemble_error_infor=   dictZMeta_get_datadog_span_kind _get_dd_llm_obs_payload_metadataZ
LLMMetricsfloat _get_time_to_first_token_secondsLLMObsPayloadr2   r   uuid4int	timestamptotal_secondsr   _get_apm_trace_id)r3   r4   rJ   rK   re   rb   rc   Z
input_metaZoutput_meta
error_infoZmetadata_parent_idrr   ru   rL   Zapm_trace_idr7   r7   r8   rD      sr   








z*DataDogLLMObsLogger.create_llm_obs_payloadc                 C   sh   z)t tdd}t|r!| }|dur$t |dd}|dur't|W S W dS W dS W dS  ty3   Y dS w )z/Retrieve the current APM trace ID if available.current_spanNrn   )getattrr   callabler2   r'   )r3   Zcurrent_span_fnr   rn   r7   r7   r8   r   C  s$   
z%DataDogLLMObsLogger._get_apm_trace_idre   c                 C   sR   d}| ddkr'| d}|r't| dp| dpd| d| d	d
}|S )z_
        Assemble error information for failure cases according to DD LLM Obs API spec
        Nrv   Zfailureerror_informationerror_messageZ	error_strzUnknown errorZerror_class	traceback)messagerT   stack)rC   DDLLMObsError)r3   re   r   r   r7   r7   r8   ry   Q  s   
z(DataDogLLMObsLogger._assemble_error_infoc                 C   sR   | d}| d}| d}|dur|dur|| S |dur'|dur'|| S dS )z
        Get the time to first token in seconds

        CompletionStartTime - StartTime = Time to first token

        For non streaming calls, CompletionStartTime is time we get the response back
        Z	startTimeZcompletionStartTimeZendTimeN        )rC   )r3   re   rJ   Zcompletion_start_timerK   r7   r7   r8   r~   j  s   


z4DataDogLLMObsLogger._get_time_to_first_token_secondsrd   c                 C   s*  | d}|du rg S t|tr?zddl}||}W n# ttfy>   z	tt|}W n tj	y;   g  Y  Y S w Y nw |t
jjt
jjt
jjt
jjt
jjt
jjt
jjt
jjt
jjf	v rz&t|trd|v r|d }|rt|dkrd|d v r|d d gW S g W S  tttfy   g  Y S w g S )z}
        Get the messages from the response object

        for now this handles logging /chat/completions responses
        r`   Nr   choicesr   )rC   r=   r2   astliteral_eval
ValueErrorSyntaxErrorjsonloadsJSONDecodeErrorr   
completionvalueacompletiontext_completionatext_completiongenerate_contentagenerate_contentgenerate_content_streamagenerate_content_streamanthropic_messagesrz   rF   KeyError
IndexError	TypeError)r3   re   rd   rI   r   r   r7   r7   r8   rx     sH   
	
z*DataDogLLMObsLogger._get_response_messagesNrf   )llmtooltask	embedding	retrievalc                 C   s2  |du s|du r
dS |t jjt jjfv rdS |t jjt jjt jjt jjt jjt j	jt j
jt jjt jjt jjt jjfv r=dS |t jjfv rFdS |t jjt jjt jjt jjt jjt jjt jjt jjt jjt jjt jjt jjt jjt jjt jjt jjt j jfv rdS |g t j!jt j"jt j#jt j$jt j%jt j&jt j'jt j(jt j)jt j*jt j+jt j,jt j-jt j.jt j/jt j0jt j1jt j2jt j3jt j4jt j5jt j6jt j7jt j8jt j9jt j:jt j;jt j<jt j=jt j>jt j?jt j@jt jAjt jBjt jCjt jDjR v rdS dS )z
        Map liteLLM call_type to appropriate DataDog LLM Observability span kind.

        Available DataDog span kinds: "llm", "tool", "task", "embedding", "retrieval"
        see: https://docs.datadoghq.com/ja/llm_observability/terms/
        Nr   r   r   r   r   )Er   r   r   Z
aembeddingr   r   r   r   r   r   r   r   r   	responsesZ
aresponsesZcall_mcp_toolZget_assistantsZaget_assistantsZ
get_threadZaget_threadZget_messagesZaget_messagesZafile_retrieveZfile_retrieveZ
afile_list	file_listZafile_contentZfile_contentZretrieve_batchZaretrieve_batchZretrieve_fine_tuning_jobZaretrieve_fine_tuning_jobZalist_input_itemsZcreate_batchZacreate_batchZcreate_fine_tuning_jobZacreate_fine_tuning_jobZcancel_fine_tuning_jobZacancel_fine_tuning_jobZlist_fine_tuning_jobsZalist_fine_tuning_jobsZcreate_assistantsZacreate_assistantsZdelete_assistantZadelete_assistantZcreate_threadZacreate_threadZadd_messageZa_add_messageZ
run_threadZarun_threadZrun_thread_streamZarun_thread_streamZfile_deleteZafile_deleteZcreate_fileZacreate_fileZimage_generationZaimage_generationZ
image_editZaimage_editZ
moderationZamoderationZtranscriptionZatranscriptionZspeechZaspeechZrerankZarerank)r3   rd   rf   r7   r7   r8   r{     s   
	
 !"#$
&z*DataDogLLMObsLogger._get_datadog_span_kindrb   c                 C   sT   |d u rg S t |tr|gS t |trdd |D S t |tr(t|ddgS g S )Nc                 S   s   g | ]}|qS r7   r7   ).0r   r7   r7   r8   
<listcomp>!  s    z>DataDogLLMObsLogger._ensure_string_content.<locals>.<listcomp>rX    )r=   r2   listrz   rC   )r3   rb   r7   r7   r8   rw     s   


z*DataDogLLMObsLogger._ensure_string_contentc                 C   s   | dd| dd| dd| dd| dd| dd| dd	| d
d| |d	}| |}|dt|i | |}|dt|i | |}|| t| di p^i }|| |S )zh
        Fields to track in DD LLM Observability metadata from litellm standard logging payload
        r?   r@   Zcustom_llm_provideridrn   	cache_hit	cache_keysaved_cache_costr   guardrail_informationN)	Z
model_nameZmodel_providerr   rn   r   r   r   r   Zis_streamed_requestlatency_metricsspend_metricsrc   )rC   _get_stream_value_from_payload_get_latency_metricsr/   rz   _get_spend_metrics_extract_tool_call_metadata)r3   re   	_metadatar   r   tool_call_metadataZ_standard_logging_metadatar7   r7   r8   r|   &  s4   










z4DataDogLLMObsLogger._get_dd_llm_obs_payload_metadatac           
      C   s   t  }| |}|dkr|d |d< |di }|d}|dur%||d< |d}|durNd}|D ]}|d	}	|	durC|t|	7 }q2|dkrN|d |d
< |S )zK
        Get the latency metrics from the standard logging payload
        r   i  Ztime_to_first_token_mshidden_paramsZlitellm_overhead_time_msNr   r   rt   Zguardrail_overhead_time_ms)DDLLMObsLatencyMetricsr~   rC   r}   )
r3   re   r   Ztime_to_first_token_secondsr   Zlitellm_overhead_msZguardrail_infoZtotal_durationinfoZ_guardrail_duration_secondsr7   r7   r8   r   T  s.   

z(DataDogLLMObsLogger._get_latency_metricsc                 C   sF   | d}|du rdS | di }t|tr!| d}|du r!dS dS )as  
        Extract the stream value from standard logging payload.

        The stream field in StandardLoggingPayload is only set to True for completed streaming responses.
        For non-streaming requests, it's None. The original stream parameter is in model_parameters.

        Returns:
            bool: True if this was a streaming request, False otherwise
        streamTZmodel_parametersF)rC   r=   rz   )r3   re   Zstream_valueZmodel_paramsr7   r7   r8   r   |  s   


z2DataDogLLMObsLogger._get_stream_value_from_payloadc              
   C   sx  t  }|dd|d< |di }|d}|dur t||d< |d}|durEzt||d< W n ttfyD   td|  Y nw |d}|durzHd	d
lm}m} d}	t	|t
rj|dd}
||
}	nt	||rq|}	|	dur|	jdu r|	j|jd}	|	 }
|
|d< td|
  W |S W |S  ty } ztd|  td|  W Y d}~|S d}~ww |S )zI
        Get the spend metrics from the standard logging payload
        rl   r   rc   user_api_key_max_budgetNuser_api_key_spendz"Invalid user_api_key_spend value: user_api_key_budget_reset_atr   )r   timezoneZz+00:00)tzinfoz)Converted budget_reset_at to ISO format: z(Error processing budget reset datetime: zOriginal value: )DDLLMObsSpendMetricsrC   r}   r   r   r
   r!   r   r   r=   r2   replacefromisoformatr   utc	isoformatr'   )r3   re   r   rc   r   r   r   r   r   Zbudget_reset_atZ
iso_stringr6   r7   r7   r8   r     sZ   





z&DataDogLLMObsLogger._get_spend_metricsc                 C   sh   g }|D ]-}t |tr'd|v s|ddkr|| qt|g}|| qt|g}|| q|S )z
        Process input messages while preserving tool_calls and tool message types.

        This bypasses the lossy string conversion when tool calls are present,
        allowing complex nested tool_calls objects to be preserved for Datadog.
        
tool_callsZroler   )r=   rz   rC   rE   r   extend)r3   rb   	processedmsgZ	convertedr7   r7   r8   -_process_input_messages_preserving_tool_calls  s    	
zADataDogLLMObsLogger._process_input_messages_preserving_tool_callsr   c                 C   s  i }t | D ]\}}zZ|d}|r||d| d< |d}|r)||d| d< |d}|rc|d}|r?||d| d< |d	}|rct|trT||d| d
< nddl}	|	||d| d
< W q tttfy }
 zt	
d| dt|
  W Y d}
~
qd}
~
ww |S )z
        Extract tool call information into key-value pairs for Datadog metadata.

        Similar to OpenTelemetry's implementation but adapted for Datadog's format.
        r   ztool_calls.z.idrT   z.typefunctionrp   z.function.name	argumentsz.function.argumentsr   Nz*DataDogLLMObs: Error processing tool call z: )	enumeraterC   r=   r2   r   dumpsr   r   r   r
   r!   )r   Zkv_pairsidxZ	tool_callZtool_idZ	tool_typer   Zfunction_nameZfunction_argumentsr   r6   r7   r7   r8   _tool_calls_kv_pair  sF   







z'DataDogLLMObsLogger._tool_calls_kv_pairc              
   C   sH  i }z| dg }|r:t|tr:|D ]'}t|tr9d|v r9| d}|r9| |}| D ]\}}||d| < q-q| d}	|	r~t|	tr|	 dg }
|
D ]5}t|tr}| d}|r}t|tr}| d}|r}| |}| D ]\}}||d| < qqqNW |S W |S W |S  ty } ztdt	|  W Y d	}~|S d	}~ww )
zk
        Extract tool call information from both input messages and response for Datadog metadata.
        rb   r   Zinput_r`   r   r   Zoutput_z4DataDogLLMObs: Error extracting tool call metadata: N)
rC   r=   r   rz   r   itemsr'   r
   r!   r2   )r3   re   r   rb   r   r   Zinput_tool_calls_kvkeyr   rI   r   choiceZresponse_tool_calls_kvr6   r7   r7   r8   r   )  sP   





z/DataDogLLMObsLogger._extract_tool_call_metadata)N)(__name__
__module____qualname__r0   r2   r&   r(   r   r.   rM   rO   rG   r   r   rD   r   r   r   r   ry   r}   r~   r   r   rx   r   r{   r	   rw   r|   r   r   boolr   r   r   r   staticmethodr   r   r7   r7   r7   r8   r   /   s    3C
H


2
g


.
(

A
!(/
r   ))__doc__r*   r   r#   Zlitellm._uuidr   r   typingr   r   r   r   r   r	   r_   r<   Zlitellm._loggingr
   Z(litellm.integrations.custom_batch_loggerr   Z0litellm.integrations.datadog.datadog_mock_clientr   r   Z,litellm.integrations.datadog.datadog_handlerr   r   r   Z%litellm.litellm_core_utils.dd_tracingr   Z8litellm.litellm_core_utils.prompt_templates.common_utilsr   Z&litellm.llms.custom_httpx.http_handlerr   r   Z*litellm.types.integrations.datadog_llm_obsZlitellm.types.utilsr   r   r   r   r   r7   r7   r7   r8   <module>   s&     