Ë
    tPêj
I  ã                   ó¸  — U d Z ddlm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mZ ddlZddlmZ dd	lmZmZmZmZ dd
lmZ ddlmZmZmZmZmZ ddlmZ ddlm Z  ddl!m"Z" de#dee$e$f   fd„Z% ed¬«       G d„ d«      «       Z&dede&fd„Z'ee$df   Z(e	e)d<   dZ*ee)d<    e+d«      Z,ee+e$      e)d<   dZ-ee.e$df      e)d <   d!ee$ef   de$fd"„Z/ G d#„ d$«      Z0y)%a  GenAI client metrics: the six ``gen_ai.client.*`` histograms plus the
recorder that builds attributes, applies the shared cardinality filter, and
records a request's metrics on both the success and the failure path.

The instrument names/units/descriptions and the recording + timing math mirror
the v1 :mod:`litellm.integrations.opentelemetry` integration so both engines emit
identical metrics. The attribute cardinality filter is reused from v1 by import
(no duplication of the valid-name set or its validation).
é    )ÚMapping)Ú	dataclass)Údatetime)ÚAnyÚFinalÚ	TypeAlias)Ú	HistogramÚMeterN)Úverbose_logger)ÚMETRIC_METADATA_KEYSÚTOKEN_TYPE_ATTRIBUTEÚ_build_metric_attribute_filterÚ _resolve_metric_attribute_filter)Útime_to_first_chunk_seconds)ÚErrorÚGenAIÚMetricÚresolve_operationÚresolve_provider)Ú
to_seconds)Ú*is_unbilled_non_inference_call_from_params)Ú
safe_dumpsÚcustom_llm_providerÚreturnc                 ó€   — t        | t        «      r| si S t        j                  t	        | «      t        j
                  | iS )a¤  The provider labels for one call's metrics.

    ``gen_ai.provider.name`` carries the semconv-mapped value; the deprecated
    ``gen_ai.system`` spelling is dual-emitted with the raw litellm provider
    string it has always carried, so a dashboard already querying it keeps
    matching. A call with no provider gets neither label: a placeholder value
    would mint a permanent series that no operator can act on.
    )Ú
isinstanceÚstrr   ÚPROVIDER_NAMEr   ÚSYSTEM)r   s    úo/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/integrations/otel/plumbing/metrics.pyÚ_provider_attributesr!   '   s=   € ô Ð)¬3Ô/Ñ7JØˆ	ä×ÑÔ-Ð.AÓBÜ�‰Ð)ðð ó    T)Úfrozenc                   óJ   — e Zd ZU eed<   eed<   eed<   eed<   eed<   eed<   y)ÚGenAIMetricsÚoperation_durationÚtoken_usageÚ
token_costÚtime_to_first_tokenÚtime_per_output_tokenÚresponse_durationN)Ú__name__Ú
__module__Ú__qualname__r	   Ú__annotations__© r"   r    r%   r%   8   s&   … à!Ó!ØÓØÓØ"Ó"Ø$Ó$Ø Ô r"   r%   Úmeterc                 ó¤  — t        | j                  t        j                  dd¬«      | j                  t        j                  dd¬«      | j                  t        j
                  dd¬«      | j                  t        j                  dd¬«      | j                  t        j                  dd	¬«      | j                  t        j                  dd
¬«      ¬«      S )NÚszGenAI operation duration)ÚnameÚunitÚdescriptionz{token}zGenAI token usageÚUSDzGenAI request costz*Time to first token for streaming requestszCAverage time per output token (generation time / completion tokens)z9Total LLM API generation time (excludes LiteLLM overhead))r&   r'   r(   r)   r*   r+   )	r%   Úcreate_histogramr   ÚOPERATION_DURATIONÚTOKEN_USAGEÚ
TOKEN_COSTÚTIME_TO_FIRST_TOKENÚTIME_PER_OUTPUT_TOKENÚRESPONSE_DURATION)r1   s    r    Úcreate_genai_metricsr?   B   så   € ÜØ ×1Ñ1Ü×*Ñ*ØØ2ð 2ó 
ð
 ×*Ñ*Ü×#Ñ#ØØ+ð +ó 
ð
 ×)Ñ)Ü×"Ñ"ØØ,ð *ó 
ð
 "×2Ñ2Ü×+Ñ+ØØDð 3ó 
ð
 $×4Ñ4Ü×-Ñ-ØØ]ð 5ó 
ð
  ×0Ñ0Ü×)Ñ)ØØSð 1ó 
ô5ð r"   z
str | NoneÚMetricAttributesÚ_OTHERÚERROR_TYPE_FALLBACK)zgen_ai.operation.namezgen_ai.provider.namezgen_ai.systemzgen_ai.request.modelúgen_ai.frameworkzmetadata.user_api_key_hashzmetadata.user_api_key_aliaszmetadata.user_api_key_team_idz metadata.user_api_key_team_aliaszmetadata.user_api_key_org_idzmetadata.user_api_key_user_idÚhidden_paramsÚMETRIC_ATTRIBUTE_CEILING)Úmodel_id.ÚBOUNDED_HIDDEN_PARAM_KEYSÚkwargsc                 ó2  — | j                  d«      }t        |dd«      xs |xs i j                  d«      xs i }|j                  d«      xs |j                  d«      }|rt        |«      S | j                  d«      }|�t        |«      j                  S t
        S )u  The ``error.type`` value for a failed request.

    Bounded by construction: the mapped provider exception's class name (the same
    ``error_information.error_class`` the failure span stamps), else the provider
    status code, else the raw exception's class name, else ``_OTHER`` â€” the value
    the convention reserves for a failure the instrumentation cannot classify. The
    exception *message* is unbounded and never becomes a label; it stays on the
    span and its exception event, where high cardinality is free.
    Ústandard_logging_objectÚerror_informationNÚerror_classÚ
error_codeÚ	exception)ÚgetÚgetattrr   Útyper,   rB   )rH   Ústd_logÚinforL   rN   s        r    Úresolve_error_typerT   š   s‘   € ð —Z‘ZÐ 9Ó:€GÜ˜'Ð#6¸Ó=ÒoÀ'Â-ÈR×ATÑATÐUhÓAiÒoÐmo€DØŸ™ -Ó0ÒJ°D·H±H¸\Ó4J€KÙÜ�;ÓÐØ—z‘z +Ó.€IØÐÜ�I‹×'Ñ'Ð'ÜÐr"   c                   óF  — e Zd ZdZddededz  ddfd„Zdeeef   ded	e	d
e	ddf
d„Z
deeef   d	e	d
e	ddfd„Zdeeef   defd„Zdeeef   defd„Zdd„Zdd„Zdedefd„Zdededdfd„Zdeeef   deddfd„Zdeeef   ded
e	dededdfd„Zdeeef   d
e	deddfd„Zy)ÚGenAIMetricRecorderað  Records the six GenAI histograms for one successful LLM call, and the
    duration histogram alone for one failed LLM call (see :meth:`record_failure`).

    The cardinality filter is resolved lazily on the first record: the proxy
    populates ``callback_settings.otel.attributes`` after the logger is built, so
    reading it at construction time would miss it. ``gen_ai.token.type`` is added
    to the token-usage attributes after filtering so the input/output split always
    survives.
    NÚmetricsÚcallback_namer   c                 óJ   — || _         || _        d | _        d | _        d| _        y )NF)Ú_metricsÚ_callback_nameÚ_includeÚ_excludeÚ_filter_resolved)ÚselfrW   rX   s      r    Ú__init__zGenAIMetricRecorder.__init__º   s'   € ØˆŒØ+ˆÔØ/3ˆŒØ/3ˆŒØ %ˆÕr"   rH   Úresponse_objÚ
start_timeÚend_timec                 ó"  — | j                  | j                  |«      «      }||z
  j                  «       }t        |j	                  d«      |j	                  d«      |«      }| j
                  j                  j                  ||¬«       |s| j                  ||«       |j	                  d«      }|r'| j
                  j                  j                  ||¬«       | j                  ||«       |s| j                  |||||«       | j                  |||«       y )NÚ	call_typeÚlitellm_params©Ú
attributesÚresponse_cost)Ú_filter_attributesÚ_bounded_attributesÚtotal_secondsr   rO   rZ   r&   ÚrecordÚ_record_token_usager(   Ú_record_time_to_first_tokenÚ_record_time_per_output_tokenÚ_record_response_duration)	r_   rH   ra   rb   rc   Úcommon_attrsÚ
duration_sÚusage_is_replayedÚcosts	            r    rm   zGenAIMetricRecorder.recordÁ   só   € ð #×5Ñ5°d×6NÑ6NÈvÓ6VÓWˆØ%¨
Ñ2×AÑAÓCˆ
Ü#MØ�J‰J�{Ó# V§Z¡ZÐ0@Ó%AÀ<ó$
Ðð 	�‰×(Ñ(×/Ñ/°
À|Ð/ÔTÙ Ø×$Ñ$ \°<Ô@à—j‘j Ó1ˆÙØ�M‰M×$Ñ$×+Ñ+¨D¸\Ð+ÔJà×(Ñ(¨°Ô>Ù Ø×.Ñ.¨v°|ÀXÈzÐ[gÔhØ×&Ñ& v¨x¸ÕFr"   c                 óî   — i | j                  | j                  |«      «      ¥t        j                  t	        |«      i¥}| j
                  j                  j                  ||z
  j                  «       |¬«       y)a�  Record the one metric a failed request can honestly report: the
        operation's duration, tagged with ``error.type``.

        The other five instruments all describe a completed generation and have
        nothing to measure here. litellm hands the failure callback no
        ``response_obj`` at all, so there is no usage to split into input/output
        tokens and no completion-token count to divide generation time by; it also
        zeroes ``response_cost`` on failure. Recording them anyway would put a
        fabricated zero into series that dashboards average.

        The attribute set is :data:`METRIC_ATTRIBUTE_CEILING`, the same cap the
        success path uses. A failure needs no provider spend, so a caller who can put
        a unique value into a client-supplied attribute could mint one histogram
        series per request for free; the cap is what makes that impossible on either
        path.

        ``error.type`` is stamped after both filters, exactly like
        ``gen_ai.token.type``, so an operator's include/exclude list cannot strip
        the discriminator and silently merge failures back into the success series.
        rg   N)	rj   rk   r   ÚTYPErT   rZ   r&   rm   rl   )r_   rH   rb   rc   rh   s        r    Úrecord_failurez"GenAIMetricRecorder.record_failureÛ   si   € ð4
Ø×%Ñ% d×&>Ñ&>¸vÓ&FÓGð
ä�J‰JÔ*¨6Ó2ñ
ˆ
ð 	�‰×(Ñ(×/Ñ/°¸JÑ1F×0UÑ0UÓ0WÐdnÐ/Õor"   c                 ó  — |j                  d«      xs i }t        j                  t        |j                  d«      «      j                  it        |j                  d«      «      ¥t        j                  |j                  d«      ddi¥}|j                  d«      }t        |dd «      xs |xs i j                  di «      }t        D ]O  }|j                  |«      }|€Œt        |t        t        f«      rt        |«      |d	|› �<   Œ?t        |«      |d	|› �<   ŒQ t        |d
d «      xs |xs i j                  d
i «      }t        D �ci c])  }t        |t        «      r|j                  |«      �|||   “Œ+ }	}|	rt        |	«      |d
<   |S c c}w )Nrf   re   r   ÚmodelrC   ÚlitellmrJ   Úmetadataz	metadata.rD   )rO   r   ÚOPERATION_NAMEr   Úvaluer!   ÚREQUEST_MODELrP   r   r   ÚdictÚlistr   r   rG   r   )
r_   rH   Úparamsrr   rR   ÚmdÚkeyr~   rD   Úbounded_hidden_paramss
             r    Ú_common_attributesz&GenAIMetricRecorder._common_attributesÿ   s”  € ØŸ
™
Ð#3Ó4Ò:¸ˆä× Ñ Ô"3°F·J±J¸{Ó4KÓ"L×"RÑ"Rð%
ä" 6§:¡:Ð.CÓ#DÓEð%
ô ×Ñ §¡¨GÓ!4Ø 	ñ	%
ˆð  Ÿ™Ð$=Ó>ˆÜ˜G Z°Ó6Ò]¸7º=Àb×:MÑ:MÈjÐZ\Ó:]ˆÜ'ò 	=ˆCØ—F‘F˜3“KˆEØˆ}ØÜ˜%¤$¬ Ô.Ü2<¸UÓ2C�˜y¨¨Ð.Ò/ä25°e³*�˜y¨¨Ð.Ò/ð	=ô  ' w°ÀÓFÒrÈ7Ê=ÐVX×J]ÑJ]Ð^mÐoqÓJrˆô 1ö(
àÜ˜-¬Ô1°m×6GÑ6GÈÓ6LÐ6Xð �˜sÑ#Ñ#ð(
Ðð (
ñ
 !Ü,6Ð7LÓ,MˆL˜Ñ)àÐùò(
s   Å.Fc                 ó†   — | j                  |«      j                  «       D ��ci c]  \  }}|t        v sŒ||“Œ c}}S c c}}w )aD  The datapoint attributes, capped at :data:`METRIC_ATTRIBUTE_CEILING`.

        The cap runs BEFORE the operator's include/exclude filter so the filter can
        only narrow it. An operator who names an excluded attribute in an include
        list gets nothing for it rather than reintroducing an unbounded label.
        )r†   ÚitemsrE   )r_   rH   ÚkÚvs       r    rk   z'GenAIMetricRecorder._bounded_attributes  s<   € ð "&×!8Ñ!8¸Ó!@×!FÑ!FÓ!H×j™˜˜AÈAÔQiÒLi��1‘ÓjÐjùÓjs   £=´=c                 óJ  — | j                   ry d }| j                  dv rWt        j                  xs i j	                  d«      xs i }t        |t        «      r|j	                  d«      nd }|�t        |«      }t        |«      \  | _	        | _
        d| _         | j                  «        y )N)NÚotelrŒ   rh   T)r^   r[   r{   Úcallback_settingsrO   r   r€   r   r   r\   r]   Ú#_warn_about_metric_ineligible_names)r_   rh   Úotel_settingsÚraws       r    Ú_ensure_filterz"GenAIMetricRecorder._ensure_filter'  s˜   € Ø× Ò ØØˆ
Ø×Ñ .Ñ0Ü$+×$=Ñ$=Ò$CÀ×#HÑ#HÈÓ#PÒ#VÐTVˆMÜ<FÀ}ÔVZÔ<[˜×*Ñ*¨<Ô8ÐaeˆCØˆÜ;¸CÓ@�
ô (HÈ
Ó'SÑ$ˆŒ�t”}Ø $ˆÔØ×0Ñ0Õ2r"   c           	      ó$  — | j                   xs
 t        «       | j                  xs
 t        «       z  }t        |t        z
  t
        hz
  «      }|rCt        j                  ddj                  |«      dj                  t        t        «      «      «       yy)a¹  Say so when the operator's filter names an attribute the ceiling removes.

        The shared validator accepts every span attribute name, so a name that is
        legal on a span but metric-ineligible would otherwise be a silent no-op: an
        ``include_list`` naming it emits nothing for it and an ``exclude_list`` naming
        it looks like it worked. Logged once, when the filter resolves, rather than
        per request.
        zÜOTel metrics: %s cannot be a metric attribute and is being ignored; it varies per request or is client-supplied, so it would make one time series per request. It is still on the span. Metric attributes are limited to: %sz, N)	r\   Ú	frozensetr]   ÚsortedrE   r   r   ÚwarningÚjoin)r_   ÚnamedÚ
ineligibles      r    rŽ   z7GenAIMetricRecorder._warn_about_metric_ineligible_names9  sy   € ð Ÿ™Ò4¬«¸¿¹Ò9UÌ)Ë+ÑVˆÜ" 5Ô+CÑ#CÔG[ÐF\Ñ#\Ó]ˆ
ÙÜ×"Ñ"ðPð —	‘	˜*Ó%Ø—	‘	œ&Ô!9Ó:Ó;õð r"   Úattrsc                 ó:  — | j                  «        | j                  �3|j                  «       D ��ci c]  \  }}|| j                  v sŒ||“Œ c}}S | j                  �3|j                  «       D ��ci c]  \  }}|| j                  vsŒ||“Œ c}}S |S c c}}w c c}}w ©N)r‘   r\   rˆ   r]   )r_   r™   r‰   rŠ   s       r    rj   z&GenAIMetricRecorder._filter_attributesM  s„   € Ø×ÑÔØ�=‰=Ð$Ø%*§[¡[£]×I™T˜Q °a¸4¿=¹=Ò6H�A�q‘DÓIÐIØ�=‰=Ð$Ø%*§[¡[£]×M™T˜Q °a¸t¿}¹}Ò6L�A�q‘DÓMÐMØˆùó JùãMs   °BÁBÁ/BÂBrr   c                 ó>  — |sy |j                  d«      }|sy i |¥t        di¥}i |¥t        di¥}| j                  j                  j	                  |j                  dd«      |¬«       | j                  j                  j	                  |j                  dd«      |¬«       y )NÚusageÚinputÚoutputÚprompt_tokensr   rg   Úcompletion_tokens)rO   r   rZ   r'   rm   )r_   ra   rr   r�   Úin_attrsÚ	out_attrss         r    rn   z'GenAIMetricRecorder._record_token_usageY  s•   € ÙØØ#×'Ñ'¨Ó0ˆÙØØI˜\ÐIÔ+?ÀÑIˆØK˜lÐKÔ,@À(ÑKˆ	Ø�‰×!Ñ!×(Ñ(¨¯©°?ÀAÓ)FÐS[Ð(Ô\Ø�‰×!Ñ!×(Ñ(¨¯©Ð3FÈÓ)JÐW`Ð(Õar"   c                 ón   — t        |«      }|€y | j                  j                  j                  ||¬«       y )Nrg   )r   rZ   r)   rm   )r_   rH   rr   Útime_to_first_chunks       r    ro   z/GenAIMetricRecorder._record_time_to_first_tokend  s5   € Ü%@ÀÓ%HÐØÐ&ØØ�‰×)Ñ)×0Ñ0Ð1DÐQ]Ð0Õ^r"   rs   c                 óˆ  — d }|r$|j                  d«      x}r|j                  d«      }|�|dk  ry t        |«      }|€|}	nR|j                  d«      }
|j                  d«      }|
�t        |
«      }|€|n||z
  }	n|�t        |«      }|€|n||z
  }	n|}	|	dkD  r+| j                  j                  j	                  |	|z  |¬«       y y )Nr�   r¡   r   Úcompletion_start_timeÚapi_call_start_timerg   )rO   r   rZ   r*   rm   )r_   rH   ra   rc   rs   rr   r¡   r�   Úend_tsÚgeneration_timer§   r¨   Úcompletion_startÚapi_call_starts                 r    rp   z1GenAIMetricRecorder._record_time_per_output_tokenj  sô   € ð !ÐÙ l×&6Ñ&6°wÓ&?Ð?˜UÐ?Ø %§	¡	Ð*=Ó >ÐØÐ$Ð(9¸QÒ(>Øä" 8Ó,ˆØˆ>Ø(‰Oà+1¯:©:Ð6MÓ+NÐ!Ø)/¯©Ð4IÓ)JÐØ$Ð0Ü*4Ð5JÓ*KÐ Ø0@Ð0H¡*ÈfÐWgÑNg‘Ø$Ð0Ü(2Ð3FÓ(G�Ø0>Ð0F¡*ÈFÐUcÑLc‘à",�à˜QÒØ�M‰M×/Ñ/×6Ñ6°ÐIZÑ7ZÐgsÐ6Õtð r"   c                 ó  — |j                  d«      }|€y |j                  d«      xs |}|€t        j                  «       }t        |«      }t        |«      }|�|€y ||z
  }|dkD  r(| j                  j
                  j                  ||¬«       y y )Nr¨   rc   r   rg   )rO   r   Únowr   rZ   r+   rm   )	r_   rH   rc   rr   r¨   Ú	_end_timer¬   r©   Údurations	            r    rq   z-GenAIMetricRecorder._record_response_durationŠ  s”   € Ø%+§Z¡ZÐ0EÓ%FÐØÐ&ØØ—J‘J˜zÓ*Ò6¨hˆ	ØÐÜ Ÿ™›ˆIÜ *Ð+>Ó ?ˆÜ" 9Ó-ˆØÐ! V ^ØØ  >Ñ1ˆØ�aŠ<Ø�M‰M×+Ñ+×2Ñ2°8ÈÐ2ÕUð r"   r›   )r   N)r,   r-   r.   Ú__doc__r%   r   r`   r   r   r   rm   rx   r€   r†   r@   rk   r‘   rŽ   rj   rn   ro   Úfloatrp   rq   r0   r"   r    rV   rV   ¯   sÇ  „ ññ& ð &¸SÀ4¹Zð &ÐSWó &ðGà˜˜S˜Ñ!ðGð ðGð ð	Gð
 ðGð 
óGð4pà˜˜S˜Ñ!ðpð ðpð ð	pð
 
ópðH¨°°c°Ñ):ð ¸tó ð>k¨'°#°s°(Ñ*;ð kÐ@Pó kó3ó$ð(Ð(8ð Ð=Mó ð	b°ð 	bÀ4ð 	bÈDó 	bð_°'¸#¸s¸(Ñ2Cð _ÐSWð _Ð\`ó _ðuà˜˜S˜Ñ!ðuð ðuð ð	uð
 ðuð ðuð 
óuð@V°¸¸S¸Ñ0Að VÈXð VÐeið VÐnrô Vr"   rV   )1r±   Úcollections.abcr   Údataclassesr   r   Útypingr   r   r   Úopentelemetry.metricsr	   r
   r{   Úlitellm._loggingr   Ú"litellm.integrations.opentelemetryr   r   r   r   Ú(litellm.integrations.otel.model.metadatar   Ú'litellm.integrations.otel.model.semconvr   r   r   r   r   Ú%litellm.integrations.otel.model.utilsr   Ú1litellm.litellm_core_utils.internal_call_metadatar   Ú*litellm.litellm_core_utils.safe_json_dumpsr   Úobjectr   r!   r%   r?   r@   r/   rB   r“   rE   rG   ÚtuplerT   rV   r0   r"   r    ú<module>rÀ      s   ðòõ $Ý !Ý ß (Ñ (ç 2ã Ý +÷ó õ Q÷õ õ =Ý hÝ Að¨fð ¸ÀÀcÀÑ9Jó ñ" �$Ô÷!ð !ó ð!ð  ð  ¨,ó  ðJ & c¨<Ð&7Ñ8Ð �)Ó 8à%Ð �UÓ %ñ$ 3<ðó3Ð ˜% 	¨#¡Ñ/ó ð8 5BÐ ˜5  s¨C x¡Ñ1Ó Að˜w s¨C xÑ0ð °Só ÷*hVò hVr"   