o
    3im                     @   sr  d dl Z d dlZd dlmZmZmZmZmZmZ d dl	m
Z
mZmZmZmZ d dlmZmZmZmZ d dlmZmZmZ d dlmZ eh dZdeeef d	eeeef  d
efddZded
eeeef  fddZ	d9dededed
dfddZ deeef d
eeef fddZ!ded
efddZ"defddZ#dedeeef fddZ$deeef ded
ee fd d!Z%d"ee d#eded$ee d	eeeef  d%e&d&eeeef  d'ed(ed)ef ded
efd*d+Z'd"ee d#eded$ee d	eeeef  d%e&d&eeeef  d'ed,ed)ef ded
efd-d.Z(d/eded
efd0d1Z)d#ed2e&d3efd4d5Z*d#ed6efd7d8Z+dS ):    N)AnyCallableDictListOptionalcast)get_tagsidentify_contextnew_contexttagcontexts)sanitize_anthropicsanitize_geminisanitize_langchainsanitize_openai)FormattedMessageStreamingEventData
TokenUsage)Client>   $ai_total_tokens$ai_input_tokens$ai_output_tokens$ai_reasoning_tokens$ai_cache_read_input_tokens$ai_cache_creation_input_tokenssdk_tagsposthog_propertiesreturnc                    s"    rt  fddtD rdS dS )Nc                 3   s    | ]}| v V  qd S N ).0keyr   r   ^/lsinfo/ai/hellotax_ai/llm_service/venv_embed/lib/python3.10/site-packages/posthog/ai/utils.py	<genexpr>   s    
z%_get_tokens_source.<locals>.<genexpr>passthroughsdk)any_TOKEN_PROPERTY_KEYS)r   r   r   r"   r#   _get_tokens_source   s
   r)   	raw_usagec                 C   s   | du rdS t | tr| S t| dr&t| jr&z|  W S  ty%   Y nw t| dr?t| jr?z|  W S  ty>   Y nw zt| W S  tyM   Y nw zdt| iW S  ty_   Y dS w )an  
    Convert raw provider usage objects to JSON-serializable dicts.

    Handles Pydantic models (OpenAI/Anthropic) and protobuf-like objects (Gemini)
    with a fallback chain to ensure we never pass unserializable objects to PostHog.

    Args:
        raw_usage: Raw usage object from provider SDK

    Returns:
        Plain dict or None if conversion fails
    N
model_dumpto_dict_raw)	
isinstancedicthasattrcallabler+   	Exceptionr,   varsstr)r*   r   r   r#   serialize_raw_usage&   s4   



r5   incrementaltargetsourcemodec                 C   sB  |dkr| d}|dur|  dpd}|| | d< | d}|dur0|  dp)d}|| | d< | d}|durF|  dp?d}|| | d< | d}|dur\|  dpUd}|| | d< | d}|durr|  dpkd}|| | d< | d	}	|	dur|  d	pd}t||	| d	< | d
}
|
durt|
tr|  d
}t|tr|ni }i ||
| d
< dS dS dS |dkr| ddur|d | d< | ddur|d | d< | ddur|d | d< | ddur|d | d< | ddur|d | d< | d	dur|d	 | d	< | d
dur|d
 | d
< dS dS td| d)a  
    Merge streaming usage statistics into target dict, handling None values.

    Supports two modes:
    - "incremental": Add source values to target (for APIs that report new tokens)
    - "cumulative": Replace target with source values (for APIs that report totals)

    Args:
        target: Dictionary to update with usage stats
        source: TokenUsage that may contain None values
        mode: Either "incremental" or "cumulative"
    r6   input_tokensNr   output_tokenscache_read_input_tokenscache_creation_input_tokensreasoning_tokensweb_search_countr*   
cumulativezInvalid mode: z'. Must be 'incremental' or 'cumulative')getmaxr.   r/   
ValueError)r7   r8   r9   source_inputcurrentsource_outputsource_cache_readsource_cache_creationsource_reasoningsource_web_searchsource_raw_usagecurrent_raw_valuecurrent_rawr   r   r#   merge_usage_statsV   sf   








rN   kwargsc                 C   s2   i }dD ]}|| v r| | dur| | ||< q|S )z?
    Extracts model parameters from the kwargs dictionary.
    )
temperature
max_tokensmax_completion_tokenstop_pfrequency_penaltypresence_penaltynstopstream	streamingNr   )rO   model_paramsparamr   r   r#   get_model_params   s   r\   providerc                 C   s`   |dkrddl m} || S |dkrddlm} || S |dkr*ddlm} || S tdddS )	z{
    Extract usage statistics from response based on provider.
    Delegates to provider-specific converter functions.
    	anthropicr   )%extract_anthropic_usage_from_responseopenai)"extract_openai_usage_from_responsegemini)"extract_gemini_usage_from_response)r:   r;   )(posthog.ai.anthropic.anthropic_converterr_   "posthog.ai.openai.openai_converterra   "posthog.ai.gemini.gemini_converterrc   r   )responser]   r_   ra   rc   r   r   r#   	get_usage   s   rh   c                 C   sX   |dkrddl m} || S |dkrddlm} || S |dkr*ddlm} || S g S )z4
    Format a regular (non-streaming) response.
    r^   r   )format_anthropic_responser`   )format_openai_responserb   )format_gemini_response)rd   ri   re   rj   rf   rk   )rg   r]   ri   rj   rk   r   r   r#   format_response      rl   c                 C   sX   | dkrddl m} ||S | dkrddlm} ||S | dkr*ddlm} ||S dS )	z>
    Extract available tool calls for the given provider.
    r^   r   )extract_anthropic_toolsrb   )extract_gemini_toolsr`   )extract_openai_toolsN)rd   rn   rf   ro   re   rp   )r]   rO   rn   ro   rp   r   r   r#   extract_available_tool_calls   rm   rq   c                 C   sf  |dkrddl m} | dpg }| d}|||S |dkr5ddlm} | dg }| d	}|||S |d
krddlm} | d}	| d}
||	|
}| ddurptdd |D }|sptt	d| dd}|g| }| ddurt
dd t|D d}|dur|| dd}| d| d || d< |S tt	d| dd}|g| }|S g S )zJ
    Merge system prompts and format messages for the given provider.
    r^   r   )format_anthropic_inputmessagessystemrb   )format_gemini_input_with_systemcontentsconfigr`   )format_openai_inputinputNc                 s   s    | ]
}| d dkV  qdS rolert   NrA   )r    msgr   r   r#   r$     s    z&merge_system_prompt.<locals>.<genexpr>)r{   contentinstructionsc                 s   s&    | ]\}}| d dkr|V  qdS rz   r|   )r    ir}   r   r   r#   r$   )  s   $ r~    z

)rd   rr   rA   rf   ru   re   rx   r'   r   r   next	enumerate)rO   r]   rr   rs   rt   ru   rv   rw   rx   messages_paraminput_param
has_system
system_msg
system_idxsystem_contentinstruction_msgr   r   r#   merge_system_prompt   sP   









r   posthog_distinct_id	ph_clientposthog_trace_idposthog_privacy_modeposthog_groupsbase_urlcall_method.c	                 K   s.  t   }
d}d}d}t }i }t|dd | rt|  zz	|d-i |	}W n! tyI } z|}t|dd}d| d}W Y d}~nd}~ww W t   }||
 }|du r]tt	 }| dupft
 du}|smt| |rt|d	s}|d
krt|drt||}t|	|}t||}td| td|	dpt|dd tdt|	 tdt||| tdt||t|| td| td|dd td|dd td| td| tdt| t||	}|rtd| |d}|dur|dkrtd| |d}|dur|dkrtd| |d}|dur+|dkr+td | |d!}|dur?|dkr?td"| |d#}|durNtd$| |sVtd%d |d&kro|	d'durotd(t|||	d' t|d)rt|jrt }i ||pi |pi }t|||d*< |jt
 d+||d, n_t   }||
 }|du rtt	 }| dupt
 du}|st| |rt|d	s|d
krt|drt||}t|	|}t||}td| td|	dpt|dd tdt|	 tdt||| tdt||t|| td| td|dd td|dd td| td| tdt| t||	}|rMtd| |d}|dura|dkratd| |d}|duru|dkrutd| |d}|dur|dkrtd | |d!}|dur|dkrtd"| |d#}|durtd$| |std%d |d&kr|	d'durtd(t|||	d' t|d)rt|jrt }i ||pi |pi }t|||d*< |jt
 d+||d, w w w |r|W d   |S 1 sw   Y  |S ).z
    Common usage-tracking logic for both sync and async calls.
    call_method: the llm call method (e.g. openai.chat.completions.create)
    N   Fclientcapture_exceptionsstatus_coder   Tz$ai_is_errorz	$ai_errorusagerb   usage_metadata$ai_provider	$ai_modelmodel$ai_model_parameters	$ai_input$ai_output_choices$ai_http_statusr   r:   r   r;   $ai_latency$ai_trace_id$ai_base_url	$ai_toolsr<   r   r=   r   r>   r   r?   $ai_web_search_countr*   	$ai_usage$process_person_profiler`   r   $ai_instructionscapture$ai_tokens_source$ai_generationdistinct_idevent
propertiesgroupsr   timer   r
   r	   r2   getattr__str__r4   uuiduuid4r   get_context_distinct_idr0   rh   r   sanitize_messagesr   rA   r\   with_privacy_moderl   rq   r1   r   r   r)   )r   r   r]   r   r   r   r   r   r   rO   
start_timerg   errorhttp_statusr   error_paramsexcend_timelatencyhas_person_distinct_idrs   sanitized_messagesavailable_tool_calls
cache_readcache_creation	reasoningr?   r*   r   merged_propertiesr   r   r#   call_llm_and_track_usageA  s~  
















































{{r   call_async_methodc	                    s6  t   }
d }d }d}t }i }t|dd | rt|  zz|d,i |	I d H }W n! tyM } z|}t|dd}d| d}W Y d }~nd }~ww W t   }||
 }|d u ratt	 }| d upjt
 d u}|sqt| |rt|ds|d	krt|d
rt||}t|	|}t||}td| td|	dpt|dd  tdt|	 tdt||| tdt||t|| td| td|dd td|dd td| td| tdt| t||	}|rtd| |d}|d ur|dkrtd| |d}|d ur|dkrtd| |d}|d ur/|dkr/td| |d }|d urC|dkrCtd!| |d"}|d urRtd#| |sZtd$d |d%krs|	d&d urstd't|||	d& t|d(rt|jrt }i ||pi |pi }t|||d)< |jt
 d*||d+ n_t   }||
 }|d u rtt	 }| d upt
 d u}|st| |rt|ds|d	krt|d
rt||}t|	|}t||}td| td|	dpt|dd  tdt|	 tdt||| tdt||t|| td| td|dd td|dd td| td| tdt| t||	}|rQtd| |d}|d ure|dkretd| |d}|d ury|dkrytd| |d}|d ur|dkrtd| |d }|d ur|dkrtd!| |d"}|d urtd#| |std$d |d%kr|	d&d urtd't|||	d& t|d(rt|jrt }i ||pi |pi }t|||d)< |jt
 d*||d+ w w w |r|W d    |S 1 sw   Y  |S )-Nr   Fr   r   r   Tr   r   rb   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   r   r   r   r   r   r   r   )r   r   r]   r   r   r   r   r   r   rO   r   rg   r   r   r   r   r   r   r   r   rs   r   r   r   r   r   r?   r*   r   r   r   r   r#   call_llm_and_track_usage_async  s  
















































{{r   datac                 C   sD   |dkrt | S |dkrt| S |dkrt| S |dkr t| S | S )zASanitize messages using provider-specific sanitization functions.r^   r`   rb   	langchain)r   r   r   r   )r   r]   r   r   r#   r   g  s   r   privacy_modevaluec                 C   s   | j s|rd S |S r   )r   )r   r   r   r   r   r#   r   t  s   
r   
event_datac                 C   sF  | dp
tt }|d |d t|d t| |d |d t| |d |d d|d	  d
d|d	  dd|d |t|d d| dpHi }|d	  d
d|d	  ddd}t|| d|d< t|d |d }|rt||d< |d dkr|d	  dd}|d	  dd}||d< ||d< n$g d}|D ]}	|d	  |	}
|
durt|
t	r|
dkr|
|d|	 < q|d	  d}|durt|t	r|dkr||d< |d	  d}|dur||d< |d d kr|d  d!durt| |d |d d! |d"< | d#du rd$|d%< t
| d&r!| j| d#p|d'|| d(d) dS dS )*a  
    Unified streaming event capture for all LLM providers.

    This function handles the common logic for capturing streaming events across all providers.
    All provider-specific formatting should be done BEFORE calling this function.

    The function handles:
    - Building PostHog event properties
    - Extracting and adding tools based on provider
    - Applying privacy mode
    - Adding special token fields (cache, reasoning)
    - Provider-specific fields (e.g., OpenAI instructions)
    - Sending the event to PostHog

    Args:
        ph_client: PostHog client instance
        event_data: Standardized streaming event data containing all necessary information
    trace_idr]   r   rO   r   formatted_inputformatted_outputr   usage_statsr:   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>   Nz$ai_r?   r   r*   r   r`   r   r   r   Fr   r   r   r   r   )rA   r4   r   r   r\   r   r)   rq   r.   intr0   r   )r   r   r   event_propertiessdk_token_tagsavailable_toolsr   r   optional_token_fieldsfieldr   r?   r*   r   r   r#   capture_streaming_eventz  s   





r   )r6   ),r   r   typingr   r   r   r   r   r   posthogr   r	   r
   r   r   posthog.ai.sanitizationr   r   r   r   posthog.ai.typesr   r   r   posthog.clientr   PostHogClient	frozensetr(   r4   r)   r5   rN   r\   rh   rl   rq   r   boolr   r   r   r   r   r   r   r   r#   <module>   s     


1
"R

B
	

 
	

 