o
    ưi                  
   @   s  d dl Z d dlZd dlmZ d dlmZmZmZmZmZm	Z	m
Z
mZ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mZmZ d dlmZ d d	lmZmZ d d
lmZ d dlm Z  d dl!T d dl"m#Z#m$Z$ d dl%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z- erd dl.m/Z/m0Z0 d dl1m2Z2 neZ2eZ0eZ/de3fddZ4G dd dZ5de6de6de	e+ dede6f
ddZ7dd Z8de6fddZ9dS )     N)datetime)	TYPE_CHECKINGAnyCallableDictListOptionalTupleUnioncast)Version)verbose_logger) MAX_LANGFUSE_INITIALIZED_CLIENTS)safe_deep_copyreconstruct_model_namefilter_exceptions_from_params)redact_user_api_key_info)create_mock_langfuse_clientshould_use_langfuse_mock)_get_httpx_client)str_to_bool)*)HttpxBinaryResponseContentResponsesAPIResponse)EmbeddingResponseImageResponseModelResponseRerankResponseStandardLoggingPayload'StandardLoggingPromptManagementMetadataTextCompletionResponseTranscriptionResponse)LangfuseStatefulTraceClient)DynamicLoggingCachereturnc                 C   sh   |  dpd}t| dr2t| dd}|dur2t|dr2t|dd}|dur2t|ttfr2|dkr2|}|S )a  
    Extract cache_read_input_tokens from usage object.

    Checks both:
    1. Top-level cache_read_input_tokens (Anthropic format)
    2. prompt_tokens_details.cached_tokens (Gemini, OpenAI format)

    See: https://github.com/BerriAI/litellm/issues/18520

    Args:
        usage_obj: Usage object from LLM response

    Returns:
        int: Number of cached tokens read, defaults to 0
    cache_read_input_tokensr   prompt_tokens_detailsNcached_tokens)gethasattrgetattr
isinstanceintfloat)Z	usage_objr&   r'   r(    r/   ]/home/app/Keep/.python/lib/python3.10/site-packages/litellm/integrations/langfuse/langfuse.py _extract_cache_read_input_tokens:   s   

r1   c                   @   s@  e Zd Z				dCddZdedefddZed	ed
edefddZ					dDdede	dee
eeeeeeef
 dee dee dee dedee defddZdede	dee
eeeeeeef
 dededee deee ee	eeef  f fddZdd Zdd Zdd Zdee d
ed	ed ee	eeef  dee dee ded!ed"ee ded#ee defd$d%Zedefd&d'Zedefd(d)Zedefd*d+Z ed,ee! de"e fd-d.Z#d/d0 Z$d1d2 Z%d3d4 Z&d5d6 Z'd7d8 Z(ed9e)d:e*e)ge)f de)fd;d<Z+ed=e,de,fd>d?Z-d@e.d,ee! fdAdBZ/dS )ELangFuseLoggerN   c              
   C   s6  zdd l }ddl m} W n ty% } ztd| dt  dd }~ww |p,td| _|p4td| _|p=tdd	| _	| j	
d
sQ| j	
dsQd
| j	 | _	td| _td| _t|| _t rnt | _d| _n
t }|j| _d| _| j| j| j	| j| j| j| jd}	|jj| _t| jtdkrd|	d< | |	| _| jrdtjd< td nz| jjj j d j!}
|
tjd< W n ty   d }
Y nw tdd ur| j"d urt#| j"nd }td| _$td| _%td| _&td| _'td| _"|| j%| j$| j&| j'|d ur|ndd| _(d S d | _(d S )Nr   r"   zS[91mLangfuse not installed, try running 'pip install langfuse' to fix this error: 
z[0mZLANGFUSE_SECRET_KEYZLANGFUSE_PUBLIC_KEYZLANGFUSE_HOSTzhttps://cloud.langfuse.comzhttp://zhttps://ZLANGFUSE_RELEASEZLANGFUSE_DEBUGTF)
public_key
secret_keyhostreleasedebugflush_intervalZhttpx_clientz2.6.0litellmZsdk_integrationzmock-project-idZLANGFUSE_PROJECT_IDz$Langfuse Mock: Using mock project IDZUPSTREAM_LANGFUSE_SECRET_KEYZUPSTREAM_LANGFUSE_PUBLIC_KEYZUPSTREAM_LANGFUSE_HOSTZUPSTREAM_LANGFUSE_RELEASEZUPSTREAM_LANGFUSE_DEBUG)r6   r7   r8   r9   r:   ))langfuser"   	Exception	traceback
format_excosgetenvr7   r6   langfuse_host
startswithZlangfuse_releaseZlangfuse_debugr2   _get_langfuse_flush_intervalZlangfuse_flush_intervalr   r   langfuse_clientZis_mock_moder   clientversion__version__langfuse_sdk_versionr   safe_init_langfuse_clientenvironr   r:   Zprojectsr)   dataidupstream_langfuse_debugr   Zupstream_langfuse_secret_keyZupstream_langfuse_public_keyZupstream_langfuse_hostZupstream_langfuse_releaseZupstream_langfuse)selfZlangfuse_public_keyZlangfuse_secretrC   r;   r=   r"   ehttp_client
parametersZ
project_idrO   r/   r/   r0   __init___   s   


	




zLangFuseLogger.__init__rS   r%   c                 C   s^   ddl m} tjtkrtdtj dt |di |}t jd7  _tdtj  |S )	aM  
        Safely init a langfuse client if the number of initialized clients is less than the max

        Note:
            - Langfuse initializes 1 thread everytime a client is initialized.
            - We've had an incident in the past where we reached 100% cpu utilization because Langfuse was initialized several times.
        r   r4   zMax langfuse clients reached: z is greater than r3   zCreated langfuse client number Nr/   )r=   r"   r<   Zinitialized_langfuse_clientsr   r>   r   r:   )rP   rS   r"   rF   r/   r/   r0   rK      s   

z(LangFuseLogger.safe_init_langfuse_clientlitellm_paramsmetadatac                 C   s   | du r|S |  ddu r|S |du ri }|  di  di p i }|D ],}|drO|ddd}||v r?td| d n	td	| d
 | |||< q#|S )at  
        Adds metadata from proxy request headers to Langfuse logging if keys start with "langfuse_"
        and overwrites litellm_params.metadata if already included.

        For example if you want to append your trace to an existing `trace_id` via header, send
        `headers: { ..., langfuse_existing_trace_id: your-existing-trace-id }` via proxy request.
        Nproxy_server_requestheadersZ	langfuse_ r3   zOverwriting Langfuse `z` from request headerzFound Langfuse `z` in request header)r)   rD   replacer   warningr:   )rU   rV   proxy_headersmetadata_param_keytrace_param_keyr/   r/   r0   add_metadata_from_header   s*   	


z'LangFuseLogger.add_metadata_from_headerDEFAULTkwargsresponse_obj
start_timeend_timeuser_idlevelstatus_messagec                 C   s  zt d|  d}d}	|di }
|dd}|
di p i }| |
|}t|di }d|di}|dd}|d	d}|d
d |durP||d< |durX||d	< | D ]\}}t|tt	t
tfs{zt|||< W q\ tyz   Y q\w q\| j|||||d\}}	t d|	 d|  d}d}|  r| j|||
|	||||||||d\}}n|dur| j|||	||||||d	 t d|  t d ||dW S  ty } zt dt| dddW  Y d}~S d}~ww )z;
        Logs a success or error event on Langfuse
        z5Langfuse Logging - Enters logging function for model NrU   litellm_call_idrV   optional_paramsmessages	functionstoolsZsecret_fields)ra   rb   promptrf   rg   zOUTPUT IN LANGFUSE: z; original: )re   rV   rU   outputrc   rd   ra   ri   inputrb   rf   rh   )	re   rV   rn   rc   rd   ra   ri   ro   rb   z0Langfuse Layer Logging - final response object: z(Langfuse Layer Logging - logging success)trace_idgeneration_idz.Langfuse Layer Error(): Exception occured - {})r   r:   r)   r_   r   popitemsr,   strr-   boolr.   r>   "_get_langfuse_input_output_content_is_langfuse_v2_log_langfuse_v2_log_langfuse_v1info	exceptionformat)rP   ra   rb   rc   rd   re   rf   rg   ro   rn   rU   rh   rV   ri   rm   rk   rl   paramvaluerp   rq   rQ   r/   r/   r0   log_event_on_langfuse   s   

z$LangFuseLogger.log_event_on_langfuserm   c                 C   s  d}d}|dkr|durt |tr|}|}||fS |dur3|dddks+t |tjr3|}d}||fS |durHt |tjrH|}| |}||fS |durZt |tjrZ|}d}||fS |durot |tjro|}| 	|}||fS |durt |tj
r|}|dd}||fS |durt |tjr|}|dd}||fS |durt |tjr|}|j}||fS |durt |tjr|}| |}||fS |ddur|ddkr|durt |tr|d	}|}||fS |ddur|dd
kr|durt |tr|}|dd}||fS )a  
        Get the input and output content for Langfuse logging

        Args:
            kwargs: The keyword arguments passed to the function
            response_obj: The response object returned by the function
            prompt: The prompt used to generate the response
            level: The level of the log message
            status_message: The status message of the log message

        Returns:
            input: The input content for Langfuse logging
            output: The output content for Langfuse logging
        NERROR	call_typeZ	embeddingzspeech-outputrM   textZ
_arealtimero   Zpass_through_endpointresponserY   )r,   rt   r)   r<   r   r   _get_chat_content_for_langfuser   r    )_get_text_completion_content_for_langfuser   r!   r   resultsr   '_get_responses_api_content_for_langfuselistdict)rP   ra   rb   rm   rf   rg   ro   rn   r/   r/   r0   rv   i  s   !:
4

/
*

%
 




	
z1LangFuseLogger._get_langfuse_input_output_contentc                    s   dS )z
        Langfuse SDK uses a background thread to log events

        This approach does not impact latency and runs in the background
        Nr/   )rP   ra   rb   rc   rd   re   r/   r/   r0   _async_log_event  s    zLangFuseLogger._async_log_eventc                 C   s   dd l }t|jjtdkS )Nr   z2.0.0)r=   r   rH   rI   )rP   r=   r/   r/   r0   rw     s   zLangFuseLogger._is_langfuse_v2c
                 C   s   ddl m}
m} td | j||dd|||d}tt	t
 |d}t|dd	||}||
|dd|||||||	jj|	jjd
|d	 d S )Nr   )CreateGenerationCreateTracezlPlease upgrade langfuse to v2.0.0 or higher: https://github.com/langfuse/langfuse-python/releases/tag/v2.0.1generation_namezlitellm-completion)namero   rn   ZuserIdcustom_llm_providermodelrY   )prompt_tokenscompletion_tokens)	r   Z	startTimeZendTimer   ZmodelParametersrm   
completionusagerV   )langfuse.modelr   r   r   r[   r"   tracer)   r   r   rt   r   
generationr   r   r   )rP   re   rV   rn   rc   rd   ra   ri   ro   rb   r   r   r   r   
model_namer/   r/   r0   ry     s>   
	
zLangFuseLogger._log_langfuse_v1rn   ri   ro   rh   c           =      C   s(  t d zttt |dd }|  r| j|dng }|d u r'd }d }n|d dd }ttt |d dd }i }|d urF||d< t	|t
rw| D ]'\}}tjd urmt	tjtrm|tjv rm|| d|  |dv rrqO|||< qO| j|||d	}|d
d }ttt |dd }|dd }|d u r|d urttt |d}|d u r|}|dd }|d ur|}tt|dg }|dd }|dd}|dd}|dp|dd }|d urt|r| |	|}	| ||}t|d}|d u r|d u rd|dd }|d urgd|i}|D ]}|dd}||vr3||d } | d ur3| ||< qttdd | D ]	}||d  q@d|v rX|sT|	nd|d< d |v rf|sb|nd|d < nD||||so|	nd|d!|d"d |d#}ttd$d | D ]}||d ||dd< q|d%kr||d&< n	|s|nd|d < |d'u st	|tr| d(krd|v r||d d)< nd)|i|d< |d*d }!t d+|!  |!|d,< |d ur|d-i }"t|"|d-< tjd urt	tjtrd.tjv rtjd/d }#|#d ur|d0|#  |d1d }$|$r&|$|d1< |d2d }%|%r3|%|d2< |d3d }&|&r@|&|d3< |  rgd4|v r[|d4 d u rUd|d4< |d4 |d4< |d u rg|d5|i |d6d }'|'r|'d7d  |'d8d  |'d9d }(i })|(r|( D ]\}}| d:vr||)|< q| jjdTi |}*t |*| | j!|*|d; d }+d },d }-|
d urt"|
dr|
dd d urtj#$||
}+t%|
d<d }.|.rt%|.d=d pd>}/t%|.d?d pd>}0t%|.d@d pd>}1|.dApd>}2t&|.}3|/|0| ' r|!nd dB},|/|3 }4t(|4|0|1|2|3dC}-|dDd }5|5d u rHttt |dEd }6dtt|dd }5|6d urHdF|6 }5|
d urTt%|
dGd }7nd }7|7d ur_|7|dG< ttt |dH}8t)|dId|8|}9|5|dJ|+|||9||s|	nd|s|nd|,|-t*|||d"d dK}:|dLd };|;d ur|;|:dL< | + rt,|:||| jdM}:|d urt	|tr|d%kr||:d&< | - r|dNd |:dN< |*j.dTi |:}<t"|<dr|<j/r|<j/|krt 0dO| dP|<j/ dQ ||+fW S  t1y   t 2dRt34   Y dSS w )UNz/Langfuse Layer Logging - logging to langfuse v2standard_logging_objectr   rV   Zuser_api_key_end_user_idprompt_management_metadata:)rX   ZendpointZcaching_groupsZprevious_models)tagsra   rV   
session_id
trace_namerp   existing_trace_idupdate_trace_keysZdebug_langfuse
mask_inputFmask_outputZ_langfuse_masking_functionZlangfuse_masking_function)rV   zlitellm-r   r   rN   trace_rY   c                 S   
   |  dS Nr   rD   keyr/   r/   r0   <lambda>     
 z1LangFuseLogger._log_langfuse_v2.<locals>.<lambda>ro   zredacted-by-litellmrn   Ztrace_versionrH   )rN   r   r   ro   rH   re   c                 S   r   r   r   r   r/   r/   r0   r     r   r   rg   TtrueZmetadata_passed_to_litellmZresponse_costztrace: Zlitellm_response_costhidden_paramsproxy_base_urlZPROXY_BASE_URLzproxy_base_url:api_basevertex_locationaws_region_name	cache_hitr   rW   methodurlrX   )authorizationcookieZreferer)r   r   r   r   r   r   total_tokenscache_creation_input_tokens)r   r   Z
total_cost)ro   rn   totalr   r&   r   Zuser_api_key_aliaszlitellm:system_fingerprintr   r   rq   )r   rN   rc   rd   r   Zmodel_parametersro   rn   r   usage_detailsrV   rf   rH   parent_observation_id)generation_paramsclean_metadatar   rF   Zcompletion_start_timez Langfuse trace_id mismatch: set z, but langfuse returned z.. Using our intended trace_id for consistency.zLangfuse Layer Error - )NNr/   )5r   r:   r   r   r   r)   _supports_tags_get_langfuse_tagsr   r,   r   rs   r<   langfuse_default_tagsr   appendadd_default_langfuse_tagsrr   rt   callable_apply_masking_functionr   rZ   filterkeyslowerr   rA   rL   updater"   r   )log_provider_specific_information_as_span"_log_guardrail_information_as_spanr*   utilsZget_logging_idr+   r1   _supports_costsZLangfuseUsageDetailsr   log_requester_metadata_supports_prompt _add_prompt_to_generation_params_supports_completion_start_timer   rp   r[   r>   errorr?   r@   )=rP   re   rV   rU   rn   rc   rd   ra   ri   ro   rb   rf   rh   r   r   Zend_user_idr   r   r   r~   r   r   rp   r   r   r:   r   r   masking_functionZtrace_paramsr]   r^   Zupdated_trace_valueZcostr   r   r   r   r   rW   rX   Zclean_headersr   rq   r   r   Z
_usage_objr   r   r   r   r&   Zinput_tokensr   Z_user_api_key_aliasr   r   r   r   r   Zgeneration_clientr/   r/   r0   rx     s  

















$

















 

zLangFuseLogger._log_langfuse_v2c                 C   s0   | j rt| j dkr| d d d  }|S dS )z;
        Get the chat content for Langfuse logging
        r   choicesmessageN)r   lenjson)rb   rn   r/   r/   r0   r   x  s   z-LangFuseLogger._get_chat_content_for_langfusec                 C   s$   | j rt| j dkr| j d jS dS )zF
        Get the text completion content for Langfuse logging
        r   N)r   r   r   rb   r/   r/   r0   r     s   z8LangFuseLogger._get_text_completion_content_for_langfusec                 C   s   t | dr| jr| jS dS )zD
        Get the responses API content for Langfuse logging
        rn   N)r*   rn   r   r/   r/   r0   r     s   z6LangFuseLogger._get_responses_api_content_for_langfuser   c                 C   s   | d u rg S |  dg pg S )NZrequest_tags)r)   r   r/   r/   r0   r     s   z!LangFuseLogger._get_langfuse_tagsc                 C   s   t jdurMtt jtrMdt jv r|dd}|d|  dt jv rM|di p*i }|dd}|du rEt jdurEt jjdi |}|}|d|  |S )	z
        Helper function to add litellm default langfuse tags

        - Special LiteLLM tags:
            - cache_hit
            - cache_key

        Nr   Fz
cache_hit:	cache_keyr   z
cache_key:r/   )r<   r   r,   r   r)   r   cacheZ!_get_preset_cache_key_from_kwargs)rP   r   ra   rV   Z_cache_hit_value_hidden_paramsZ
_cache_keyZ_preset_cache_keyr/   r/   r0   r     s    	


z(LangFuseLogger.add_default_langfuse_tagsc                 C      t | jt dkS )z/Check if current langfuse version supports tagsz2.6.3r   rJ   rP   r/   r/   r0   r        zLangFuseLogger._supports_tagsc                 C   r   )z1Check if current langfuse version supports prompt2.7.3r   r   r/   r/   r0   r     r   zLangFuseLogger._supports_promptc                 C   r   )z0Check if current langfuse version supports costsr   r   r   r/   r/   r0   r     r   zLangFuseLogger._supports_costsc                 C   r   )z@Check if current langfuse version supports completion start timer   r   r   r/   r/   r0   r     r   z.LangFuseLogger._supports_completion_start_timerM   r   c              
      s   | du rdS z9t | tr | W S t | tr,i }|  D ]\}}t| ||< q|W S t | tr; fdd| D W S  | W S  ty\ } zt	d| d | W  Y d}~S d}~ww )a%  
        Apply a masking function to data, handling different data types.

        Args:
            data: The data to mask (can be str, dict, list, or None)
            masking_function: A callable that takes data and returns masked data

        Returns:
            The masked data
        Nc                    s   g | ]}t | qS r/   )r2   r   ).0itemr   r/   r0   
<listcomp>  s    
z:LangFuseLogger._apply_masking_function.<locals>.<listcomp>z"Failed to apply masking function: z. Returning original data.)
r,   rt   r   rs   r2   r   r   r>   r   r[   )rM   r   Zmasked_dictr   r~   rQ   r/   r   r0   r     s0   







z&LangFuseLogger._apply_masking_functionr;   c                 C   s   t tdp| S )a  
        Get the langfuse flush interval to initialize the Langfuse client

        Reads `LANGFUSE_FLUSH_INTERVAL` from the environment variable.
        If not set, uses the flush interval passed in as an argument.

        Args:
            flush_interval: The flush interval to use if LANGFUSE_FLUSH_INTERVAL is not set

        Returns:
            [int] The flush interval to use to initialize the Langfuse client
        ZLANGFUSE_FLUSH_INTERVAL)r-   rA   rB   )r;   r/   r/   r0   rE     s   z+LangFuseLogger._get_langfuse_flush_intervalr   c                 C   s   |du rt d dS |dd}|st d dS t|ts)t dt| dS |D ]G}t|ts;t dt| q+|jd|dd|d	d|d
d|dd|ddd|dd|ddd}t d|  |  q+dS )z5
        Log guardrail information as a span
        NzQNot logging guardrail information as span because standard_logging_object is Noneguardrail_informationzPNot logging guardrail information as span because guardrail_information is emptyzYNot logging guardrail information as span because guardrail_information is not a list: %sz1Skipping guardrail entry with unexpected type: %sZ	guardrailZguardrail_requestZguardrail_responseguardrail_nameguardrail_modeZmasked_entity_count)r   r   Zguardrail_masked_entity_countrc   rd   )r   ro   rn   rV   rc   rd   z&Logged guardrail information as span: )	r   r:   r)   r,   r   typer   spanend)rP   r   r   r   Zguardrail_entryr   r/   r/   r0   r   
  sT   








z1LangFuseLogger._log_guardrail_information_as_span)NNNr3   )NNNr`   N)0__name__
__module____qualname__rT   r   r"   rK   staticmethodr_   r
   r   r   r    r   r!   r   r   r   r   r   rt   r   r	   r   rv   r   rw   ry   tuplerx   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r-   rE   r#   r   r/   r/   r/   r0   r2   ]   s   
`3
p
e	1	

  m(r2   r   r   r   rF   c              
   C   s  ddl m} ddlm}m}m}m} t||}|dd }	|	d u r(|d u r(	 | S t	|	t
r|	dddkrF|di |	}
||
d| d< | S |	ddd	kr^|di |	}||d| d< | S d
|	v rd|	v rt	|	d trt|d|j}|	d |	d |	d
 |	dd d}d|v rd|v r|	dg pg |d< |	dg pg |d< |di |}||d| d< | S t	|	d trt|d|j}|	d |	d |	d
 |	dd d}d|v rd|v r|	dg pg |d< |	dg pg |d< |di |}||d| d< | S td | S td | S |d urA|d dkrAz||d | d< W | S  ty@ } ztd|  W Y d }~| S d }~ww |	| d< | S )Nr   r4   )ChatPromptClientPrompt_ChatPrompt_TextTextPromptClientrm   r   rY   Zchat)rm   r   rH   Zmodel_fieldsr   config)r   rm   rH   r   labelsr   z5[Non-blocking] Langfuse Logger: Invalid prompt formatzS[Non-blocking] Langfuse Logger: Invalid prompt format. No prompt logged to LangfuseZprompt_integrationr=   Z	prompt_idzI[Non-blocking] Langfuse Logger: Error getting prompt client for logging: r/   )r=   r"   r   r   r   r   r   r   rr   r,   r   r)   rt   r+   Z
__fields__r   r   r   Z
get_promptr>   r:   )r   r   r   rF   r"   r   r   r   r   Zuser_promptZ_prompt_chatZ_prompt_textZprompt_text_params_dataZ_prompt_objZprompt_chat_paramsrQ   r/   r/   r0   r   C  s   
D@=
,



r   c                 C   s   | dd}|du rdS | dd}|durHt|tr?|D ]}t|tr5| D ]\}}| j||d q(q| jd|d qdS | jd|d dS dS )z
    Logs provider-specific information as spans.

    Parameters:
        trace: The tracing object used to log spans.
        clean_metadata: A dictionary containing metadata to be logged.

    Returns:
        None
    r   Nvertex_ai_grounding_metadata)r   ro   )r)   r,   r   r   rs   r   )r   r   r   r   elemr   r~   r/   r/   r0   r     s4   


r   c                 C   sF   i }|  dpi }|  D ]\}}||vr|||< q|d|i |S )Nrequester_metadata)r)   rs   r   )r   Zreturned_metadatar   kvr/   r/   r0   r     s   r   ):rA   r?   r   typingr   r   r   r   r   r   r	   r
   r   Zpackaging.versionr   r<   Zlitellm._loggingr   Zlitellm.constantsr   Z'litellm.litellm_core_utils.core_helpersr   r   r   Z*litellm.litellm_core_utils.redact_messagesr   Z2litellm.integrations.langfuse.langfuse_mock_clientr   r   Z&litellm.llms.custom_httpx.http_handlerr   Zlitellm.secret_managers.mainr   Z#litellm.types.integrations.langfuseZlitellm.types.llms.openair   r   Zlitellm.types.utilsr   r   r   r   r   r   r    r!   Zlangfuse.clientr"   r#   Z*litellm.litellm_core_utils.litellm_loggingr$   r-   r1   r2   r   r   r   r   r/   r/   r/   r0   <module>   sX   ,(#       m
Y,