Ë
    tPêjåÅ  ã                   ó
  — U d Z ddlZddlZddlZddlZddlmZ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mZ dd	lmZmZmZ dd
lmZ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dl%m&Z& ddl'm(Z( ddl)m*Z* ddl+m,Z,m-Z-m.Z.m/Z/ ddl0m1Z1 ddl2m3Z3 ddl4m5Z5 ddl6m7Z7m8Z8 erddl9m:Z: ddl;m<Z< ddl=m>Z> ddl6m?Z? dZ@eeAd<   dZBeeAd<   dZCeeAd<   d ZDeeAd!<   d"ZEeeAd#<   d$ZFeeAd%<    ei «      ZGeeeHeIf      eAd&<    eeeHeIf   «      ZJeeAd'<    eeKeeHeIf   d(f   «      ZLeeAd)<    eeKeId(f   «      ZMeeAd*<   d+eeHeIf   d,eKeeHeIf   d(f   fd-„ZNd+eeHeIf   d,eeHeIf   fd.„ZOd+eeHeIf   d/eeHeIf   d,eeHeIf   fd0„ZP eQd1«      ZReeAd2<   d+eeHeIf   d3eeHeIf   d,eeHeIf   fd4„ZSd+eeHeIf   d3eeHeIf   d,eeHeIf   fd5„ZTd6eId,eHfd7„ZUd6eId,eHfd8„ZV G d9„ d:«      ZW eWePeUd;¬<«      ZXeeAd=<    eWeSeUd>¬<«      ZYeeAd?<    eWeTeVd>¬<«      ZZeeAd@<    eQdA«      Z[eeAdB<   dCeeHeIf   d,e\fdD„Z] eQdE«      Z^eeAdF<   dGeId,e\fdH„Z_dIeWd+eeHeIf   d/eeHeIf   d6eId,eKeKeeHeIf   d(f   eeHeIf   eHf   dz  f
dJ„Z` eeXeXeYeZeZdKœ«      ZaeeeHeWf      eAdL<   dMZbeeAdN<    G dO„ dPe«      Zc e3ec«      ZdeeAdQ<   dReHdSeHdTeed,e\fdU„ZfdVegd,eHfdW„ZhdXeId,eefdY„ZidXeId,eefdZ„Zjd[eHd\e\d,eHfd]„Zkd^eHd_eHd`eHd,eHfda„ZldbeeHeIf   d,eefdc„ZmdSeHd,eHfdd„ZndeeHdfeedgeed,eefdh„ZodeeHdieed,dfdj„ZpdSeHdkdld,dfdm„ZqdneeHeIf   d,e\fdo„ZrdneeHeIf   d,eHdz  fdp„ZsdneeHeIf   d,eeHeIf   fdq„ZtdneeHeIf   d,eHdz  fdr„ZudneeHeIf   d,eefds„ZvdCeeHeIf   dteHd,e\fdu„Zw ed>d>¬v«       G dw„ dx«      «       Zx ed>d>¬v«       G dy„ dz«      «       Zy ed>d>¬v«       G d{„ d|«      «       Zz G d}„ d~e«      Z{deId€e|d�eed,e{dz  fd‚„Z} e"dƒe@¬„«      Z~eeAd…<   d†ZeeAd‡<    G dˆ„ d‰e&«      Z€ ei «      Z�eeeHeKe{d(f   f      eAdŠ<   dŒd‹„Z‚y)�ag  Shadow Eval Logger: samples a shadowed key's successful LLM requests (chat completions,
Anthropic Messages, and Responses API surfaces, each normalized to chat shape), duplicates
each against the job's other arm in a detached task (the auto-router for a forward job, the
fixed baseline model for a reverse one), blind-judges real vs shadow, and appends one
``LiteLLM_ShadowEvalAttempt`` row (verdict or error) as the feature's only hot-path write.
Counts, status, and spend derive from those rows at read time, so nothing can disagree
across pods or stop races; the hook reads active jobs through a short-TTL cache.é    N)Ú	AwaitableÚCallableÚMappingÚSequence)Ú	dataclass)ÚdatetimeÚtimezone)Úgroupby)Ú
itemgetter)ÚMappingProxyType)ÚTYPE_CHECKINGÚFinalÚLiteral)Ú	BaseModelÚ
ConfigDictÚTypeAdapterÚValidationErrorÚfield_validatorÚmodel_validator)Úverbose_logger)ÚInMemoryCache)Ú!INTERNAL_CALL_ORIGIN_METADATA_KEY)ÚCustomLogger)Ú get_litellm_metadata_from_kwargs)Ú#sanitized_forwardable_call_metadata)Údefault_router_providerÚextract_text_from_contentÚjudge_acompletionÚparse_json_verdict)Úshould_redact_message_logging)Útype_to_response_format_param)ÚShadowEvalDirection)ÚSHADOW_EVAL_JUDGE_CALL_ORIGINÚSHADOW_EVAL_ROUTER_CALL_ORIGIN)ÚShadowEvalFunnelStage)ÚPrismaClient)ÚRouter)ÚStandardLoggingPayloadé
   Ú_JOBS_CACHE_TTL_SECONDSé   Ú_MAX_CONCURRENT_SHADOW_TASKSi@  Ú_MAX_JUDGE_RESPONSE_CHARSiÀ]  Ú_MAX_JUDGE_PROMPT_CHARSiÜ  ÚJUDGE_MAX_OUTPUT_TOKENSiô  Ú_MAX_ERROR_CHARSÚ_EMPTY_METADATAÚ_CHAT_REQUEST_ADAPTER.Ú_CHAT_MESSAGES_ADAPTERÚ_MESSAGE_ITEMS_ADAPTERÚkwargsÚreturnc                 ól   — | j                  d«      }t        |t        «      rt        d„ |D «       «      S dS )NÚmessagesc              3   óB   K  — | ]  }t        |t        «      sŒ|–— Œ y ­w©N)Ú
isinstancer   ©Ú.0Úms     úl/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/integrations/shadow_eval_logger.pyú	<genexpr>z!_chat_messages.<locals>.<genexpr>M   s   è ø€ Ò:�q¤:¨a´Õ#9”Ñ:ùs   ‚˜© )Úgetr;   r   Útuple)r5   Úraws     r?   Ú_chat_messagesrE   K   s0   € Ø—‘˜JÓ'€CÜ>HÈÌhÔ>WŒ5Ñ:˜CÔ:Ó:Ð_Ð]_Ð_ó    c                 óà   — | j                  d«      }t        |t        «      r|j                  d«      nd }t        |t        «      r|j                  d«      nd }t        |t        «      r|S t        S )NÚlitellm_paramsÚproxy_server_requestÚbody©rB   r;   r   r1   )r5   rH   ÚrequestrJ   s       r?   Ú_proxy_wire_bodyrM   P   s\   € Ø"ŸJ™JÐ'7Ó8€NÜCMÈnÔ^eÔCf�^×'Ñ'Ð(>Ô?Ðlp€GÜ)3°G¼WÔ)E�'—+‘+˜fÔ%È4€DÜ˜d¤GÔ,ˆ4ÐA´/ÐArF   Úmodel_parametersc                 ó6   — t        i |¥dt        | «      i¥«      S )zQChat requests are already chat-shaped: the logged model_parameters forward as-is.r8   )r   rE   )r5   rN   s     r?   Ú_chat_request_from_chatrP   W   s"   € ô ÐTÐ/ÐT°¼^ÈFÓ=SÑTÓUÐUrF   )Ú
max_tokensÚtemperatureÚtop_pÚtop_kÚreasoning_effortÚ_ANTHROPIC_SAMPLING_PARAM_KEYSÚ_model_parametersc                 ó  ‡	— ddl m}  |«       }t        | «      }| j                  d«      xs |j                  d«      }t	        |j                  «       «      t        z  t	        d«      z
  Š	t        t        g ˆ	fd„|j                  «       D «       ¢dt        | j                  d«      xs d«      f‘dt        j                  | j                  d«      xs d	«      f‘|�d|ffnd	¢­«      «      }|j                  |«      \  }}|S )
aü  /v1/messages logs surface-native block messages with ``system`` top-level: the
    native provider path carries it in kwargs, the openai-compatible bridge path only in
    the proxy's snapshot of the client's wire body. Params come from the wire body alone,
    because the logged optional_params switch dialect per provider path (the bridge's
    inner completion rewrites them to chat shape mid-flight); the adapter translates
    them alongside the messages, and sampling params copy through untranslated.r   )ÚLiteLLMAnthropicMessagesAdapterÚsystem)r8   rZ   c              3   ó6   •K  — | ]  \  }}|‰v sŒ||f–— Œ y ­wr:   rA   )r=   ÚkÚvÚ
param_keyss      €r?   r@   z8_chat_request_from_anthropic_messages.<locals>.<genexpr>y   s   øè ø€ ÒJ™T˜Q ¸!¸zº/�1�a”&ÑJùs   ƒ�	ÚmodelÚ r8   rA   )ÚHlitellm.llms.anthropic.experimental_pass_through.adapters.transformationrY   rM   rB   Ú	frozensetÚtranslatable_anthropic_paramsrV   r   ÚdictÚitemsÚstrr3   Úvalidate_pythonÚtranslate_anthropic_to_openai)
r5   rW   rY   ÚadapterÚ	wire_bodyrZ   rL   Ú
translatedÚ_r^   s
            @r?   Ú%_chat_request_from_anthropic_messagesrm   c   s  ø€ õñ 5Ó6€GÜ'¨Ó/€IØ—J‘J˜xÓ(ÒC¨I¯M©M¸(Ó,C€Fä�'×7Ñ7Ó9Ó:Ô=[Ñ[ÜÐ(Ó)ñ*€Jô &ÜðÛJ Y§_¡_Ó%6ÔJðàœ#˜fŸj™j¨Ó1Ò7°RÓ8Ð9ðð Ô3×CÑCÀFÇJÁJÈzÓDZÒD`Ð^`ÓaÐbðð ,2Ð+=�H˜fÐ%Ñ'À2ñ	ó	
ó	€Gð ×9Ñ9¸'ÓB�M€J�ØÐrF   c           	      óŒ  ‡— ddl m} ddlmŠ t	        | «      }| j                  d«      xs |j                  d«      }t        t        g ˆfd„|j                  «       D «       ¢|�d|ffnd¢­«      «      }t        j                  |j                  t        | j                  d«      xs d«      | j                  d	«      |¬
«      «      S )a   /v1/responses logs the raw ``input`` under ``kwargs["messages"]``, an alias
    function_setup creates for responses call types: a bare string, chat-shaped dicts,
    or item dicts; ``instructions`` is the system prompt. Params come from the wire body
    for the same reason as the messages surface; the transformer translates them with
    the input (max_output_tokens to max_tokens, Responses tools to chat tools, reasoning
    to reasoning_effort) and never reads surface-only keys like previous_response_id.r   )Ú LiteLLMCompletionResponsesConfig)Ú!ResponsesAPIOptionalRequestParamsÚinstructionsc              3   óJ   •K  — | ]  \  }}|‰j                   v sŒ||f–— Œ y ­wr:   )Ú__annotations__)r=   r\   r]   rp   s      €r?   r@   z/_chat_request_from_responses.<locals>.<genexpr>—   s'   øè ø€ Òq™T˜Q ¸!Ð?`×?pÑ?pÒ:p�1�a”&Ñqùs   ƒ#š	#rA   r_   r`   r8   )r_   ÚinputÚresponses_api_request)ÚBlitellm.responses.litellm_completion_transformation.transformationro   Úlitellm.types.llms.openairp   rM   rB   r   rd   re   r2   rg   Ú:transform_responses_api_request_to_chat_completion_requestrf   )r5   rW   ro   rj   rq   Úresponses_requestrp   s         @r?   Ú_chat_request_from_responsesrz   „   sÆ   ø€ õõ Lä'¨Ó/€IØ Ÿ*™* ^Ó4ÒU¸	¿¹ÀnÓ8U€LÜ/ÜðÛq Y§_¡_Ó%6Ôqðà7CÐ7O�N LÐ1Ñ3ÐUWñó	
ó Ðô !×0Ñ0Ø(×cÑcÜ�f—j‘j Ó)Ò/¨RÓ0Ø—*‘*˜ZÓ(Ø"3ð 	dó 	
óð rF   Úresponse_objc                 ó2  ‡— 	 t        | t        «      r| d   d   d   n| j                  d   j                  Št        ‰t        «      r‰j                  nˆfd„} |d«      s |d«      ryt         |d«      «      S # t        t
        t        t        f$ r Y yw xY w)	z~The assistant's text, or empty when the turn carries tool calls: only text-final
    turns produce a judgeable A/B comparison.Úchoicesr   Úmessager`   c                 ó   •— t        ‰| d «      S r:   )Úgetattr)Úkeyr~   s    €r?   ú<lambda>z"_chat_final_text.<locals>.<lambda>°   s   ø€ ÌwÐW^Ð`cÐeiÓOj€ rF   Ú
tool_callsÚfunction_callÚcontent)
r;   r   r}   r~   ÚAttributeErrorÚKeyErrorÚ
IndexErrorÚ	TypeErrorrB   r   )r{   Úreadr~   s     @r?   Ú_chat_final_textr‹   ¥   s–   ø€ ðô ˜,¬Ô0ð ˜Ñ# AÑ& yÒ1à×%Ñ% aÑ(×0Ñ0ð 	ô ",¨G´WÔ!=�'—+’+ÓCj€DÙˆLÔ™T /Ô2ØÜ$¡T¨)£_Ó5Ð5øô œH¤j´)Ð<ò Ùðús   ƒ4A: Á:BÂBc                 ó.  — ddl m} 	 t        | t        «      r|j	                  | «      n| }t        |dd«      }t        |t        «      syt        d„ |D «       «      }t        d„ |D «       «      ryt        t        |dd«      xs d«      S # t
        $ r Y yw xY w)	a=  The turn's aggregated output text, or empty when the turn carries tool calls. A
    dict-shaped payload is validated into the owner type first, because ``output_text``
    is a derived property rather than a serialized field, so it never exists on a dict;
    a dict the owner type rejects is unjudgeable and skipped.r   )ÚResponsesAPIResponser`   ÚoutputNc              3   ó`   K  — | ]&  }t        |t        «      r|j                  «       n|–— Œ( y ­wr:   ©r;   r   Ú
model_dump©r=   Úitems     r?   r@   z(_responses_final_text.<locals>.<genexpr>Æ   s&   è ø€ ÒgÐX\¬j¸¼yÔ.I˜Ÿ™Ô*ÈtÓSÑgùs   ‚,.c              3   óh   K  — | ]*  }t        |t        «       xs |j                  d «      dv –— Œ, y­w)Útype)r„   Úcustom_tool_callN©r;   r   rB   r’   s     r?   r@   z(_responses_final_text.<locals>.<genexpr>Ç   s6   è ø€ ò ØgkŒJ�tœWÓ%Ð%Òb¨¯©°&Ó)9Ð=bÐ)bÓbñùs   ‚02Úoutput_text)rw   r�   r;   r   Úmodel_validater   r€   r   rC   Úanyrf   )r{   r�   ÚresponserŽ   re   s        r?   Ú_responses_final_textrœ   ¶   s¢   € õ
 ?ðäAKÈLÔZaÔAbÐ ×/Ñ/°Ô=Ðhtð 	ô
 ˜H h°Ó5€FÜ�fœhÔ'ØÜÑgÐ`fÔgÓg€EÜ
ñ Øotôô ð ÜŒw�x °Ó3Ò9°rÓ:Ð:øô ò Ùðús   ˆ#B Â	BÂBc                   ód   — e Zd ZdZdZdeeeef   eeef   geeef   f   deegef   de	ddfd„Z
y)	Ú_SurfaceOpsaP  One row per sampled call_type: how its logged request becomes a chat-shaped
    request (messages plus translated generation params) and how its response yields
    the judgeable final text. Membership in this table IS the sampling allowlist;
    unknown call types fail closed. ``wire_params`` marks the surfaces whose params
    come from the proxy's wire-body snapshot, which is taken before the guardrail
    pre-call hook: those rows must not sample a request a pre-call guardrail rewrote,
    or the shadow call would replay content (tools, unmasked entities) the guardrail
    removed.©Úchat_requestÚ
final_textÚwire_paramsr    r¡   r¢   r6   Nc                 ó.   — || _         || _        || _        y r:   rŸ   )Úselfr    r¡   r¢   s       r?   Ú__init__z_SurfaceOps.__init__Ú   s   € ð )ˆÔØ$ˆŒØ&ˆÕrF   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú	__slots__r   r   rf   ÚobjectÚboolr¥   rA   rF   r?   rž   rž   Î   sq   „ ñð >€Ið'à ¨¨V¨Ñ 4°g¸cÀ6¸kÑ6JÐKÈWÐUXÐZ`ÐU`ÑMaÐaÑbð'ð ˜f˜X s˜]Ñ+ð'ð ð	'ð
 
ô'rF   rž   F)r¢   Ú	_CHAT_OPSTÚ_ANTHROPIC_OPSÚ_RESPONSES_OPS)Úduring_callÚ	post_callÚlogging_onlyÚduring_mcp_callÚpost_mcp_callÚrealtime_input_transcriptionÚ_NON_MUTATING_GUARDRAIL_MODESÚrequest_metadatac                 ó”   — | j                  d«      }t        |t        «      r|nd}t        d„ |D «       «      }t	        d„ |D «       «      S )a  Whether a guardrail that can rewrite the outbound request ran on this one, read
    from the same guardrail-information entries spend logging uses. str-enum modes
    compare equal to their plain-string values, and an entry whose mode is missing or
    unrecognized counts as mutating.Ú&standard_logging_guardrail_informationrA   c              3   ó`   K  — | ]&  }t        |t        «      sŒ|j                  d «      –— Œ( y­w)Úguardrail_modeNr—   )r=   Úentrys     r?   r@   z2_request_mutating_guardrail_ran.<locals>.<genexpr>ø   s&   è ø€ Ò"rÀ5ÔWaÐbgÔipÕWq 5§9¡9Ð-=×#>Ñ"rùs   ‚.˜.c           	   3   óv   K  — | ]1  }t        d „ t        |t        t        z  «      r|n|fD «       «       –— Œ3 y­w)c              3   ó,   K  — | ]  }|t         v –— Œ y ­wr:   )r¶   )r=   Úmodes     r?   r@   z<_request_mutating_guardrail_ran.<locals>.<genexpr>.<genexpr>ú   s   è ø€ ò 
Ø6:ˆDÔ1Ô1ñ
ùs   ‚N)Úallr;   ÚlistrC   )r=   Úmodess     r?   r@   z2_request_mutating_guardrail_ran.<locals>.<genexpr>ù   sH   è ø€ ò ð ô ñ 
ÜHRÐSXÔZ^ÔafÑZfÔHg¹uÐnsÐmuô
ó 
ô 	
ñùs   ‚79)rB   r;   r   rC   rš   )r·   rD   ÚentriesÚmodes_per_entrys       r?   Ú_request_mutating_guardrail_ranrÅ   ñ   sQ   € ð
 "×%Ñ%Ð&NÓO€CÜ& s¬HÔ5‘S¸2€GÜ"Ñ"rÈGÔ"rÓr€OÜñ ð %ô	ó ð rF   )r_   r8   ÚstreamÚstream_optionsÚmetadataÚ_UNFORWARDED_REQUEST_KEYSÚvaluec                 óN   — | d u xs  t        | t        «      xr t        | «      dk(  S )Nr   )r;   rÁ   Úlen)rÊ   s    r?   Ú_forwards_nothingrÍ     s%   € Ø�Dˆ=ÒIœZ¨¬tÓ4ÒH¼¸U»Àq¹ÐIrF   Úopsc           	      óØ  — 	 | j                  ||«      }t        j                  |j                  d«      «      }t        j                  t        d„ |D «       «      «      }| j                  |«      }|r|syt        |j                  «       D �	�
ci c]  \  }	}
|	t        vsŒt        |
«      rŒ|	|
“Œ c}
}	«      }|||fS # t        $ r }t        j                  d|«       Y d}~yd}~ww xY wc c}
}	w )zèThe normalized chat conversation, the forwardable generation params, and the
    judgeable final text; None when this request's shapes cannot be sampled (tool-final
    turn, empty text, or a shape the owner transformations reject).r8   c              3   ód   K  — | ](  }t        |t        «      r|j                  d ¬«      n|–— Œ* y­w)T)Úexclude_noneNr�   r<   s     r?   r@   z$_judgeable_sample.<locals>.<genexpr>  s+   è ø€ ÒdÐYZ´ZÀÄ9Ô5M�!—,‘,¨D�,Ô1ÐSTÓTÑdùs   ‚.0z7shadow_eval: request normalization failed, skipping: %sN)r    r4   rg   rB   r3   rC   Ú	Exceptionr   Údebugr¡   r   re   rÉ   rÍ   )rÎ   r5   rN   r{   rL   re   r8   ÚeÚ	real_textr\   r]   Úparamss               r?   Ú_judgeable_sampler×   
  sÜ   € ðØ×)Ñ)¨&Ð2BÓCˆÜ-×=Ñ=¸g¿k¹kÈ*Ó>UÓVˆÜ0×@Ñ@ÜÑdÐ^cÔdÓdó
ˆð —~‘~ lÓ3€IÙ™9ØÜ$Ø!Ÿ-™-›/×m‘$�!�Q¨QÔ6OÒ-OÔXiÐjkÕXlˆˆA‰Ómó€Fð �V˜YÐ&Ð&øô ò Ü×ÑÐVÐXYÔZÜûðüó 	ns*   ‚AB: ÂC&
ÂC&
Â(C&
Â:	C#ÃCÃC#)Ú
completionÚacompletionÚanthropic_messagesÚ
aresponsesÚ	responsesÚ_SURFACE_OPSar  You are an impartial quality judge comparing two responses to the same conversation.

The responses are labeled A and B in random order. You do not know which system produced which.

Criteria: correctness, completeness, clarity, conciseness.

Return ONLY valid JSON in this exact format, no other text:
{
  "preference": "A" | "B" | "tie",
  "confidence": <0.0 to 1.0>
}ÚPAIRWISE_JUDGE_SYSTEM_PROMPTc                   ó,   — e Zd ZU dZed   ed<   eed<   y)ÚPairwiseVerdictaD  The judge's blind A/B verdict: the response_format schema sent with the judge call
    and the validation contract on its reply. Both fields are required and preference is
    closed over the prompt's labels, so a malformed or truncated reply is an
    unparseable-verdict error row, never a defaulted or fabricated verdict.)ÚAÚBÚtieÚ
preferenceÚ
confidenceN)r¦   r§   r¨   r©   r   rs   ÚfloatrA   rF   r?   rà   rà   <  s   … ñOð
 ˜Ñ(Ó(ØÔrF   rà   ÚPAIRWISE_JUDGE_RESPONSE_FORMATÚ
request_idÚjob_idÚ
percentagec                 óÈ   — t        j                  |› d| › �j                  «       «      j                  «       }t        j                  |dd d«      t        d«      z  }|dz  |k  S )z­Deterministically decide whether a request falls in the shadowed slice: hash-based
    rather than random so retries sample the same way and pods agree without coordination.ú:Né   Úbigl            g      Y@)ÚhashlibÚsha256ÚencodeÚdigestÚintÚ
from_bytesræ   )rè   ré   rê   rò   Úbuckets        r?   Ú_sample_hitsrö   I  s]   € ô —N‘N f X¨Q¨z¨lÐ#;×#BÑ#BÓ#DÓE×LÑLÓN€FÜ—N‘N 6¨"¨1 :¨uÓ5¼¸e»ÑD€FØ�E‰>˜JÑ&Ð&rF   rÔ   c                 óê   — t        j                  | j                  «      }|r5d|d   j                  j	                  dd«      d   › d|d   j
                  › �nd}t        | «      j                  › |› d| › �S )z�Exception class, message, and the raising frame, so an attempt's error row names
    the faulty code path without needing debug logs on the pod.z at éÿÿÿÿú/é   rì   r`   ú: )Ú	tracebackÚ
extract_tbÚ__traceback__ÚfilenameÚrsplitÚlinenor•   r¦   )rÔ   ÚframesÚlocations      r?   Ú_failure_detailr  Q  sw   € ô ×(Ñ(¨¯©Ó9€FÙ]c˜˜V B™Z×0Ñ0×7Ñ7¸¸QÓ?ÀÑCÐDÀAÀfÈRÁj×FWÑFWÐEXÑYÐik€HÜ�1‹g×ÑÐ ˜z¨¨A¨3Ð/Ð/rF   r›   c                 ó´   — t        t        | dd«      dd«      }t        |«      r |d«      nd}t        |t        t        f«      rt	        |«      S t        | «      S )a   Price one eval-arm call with the figure the spend pipeline bills: the router client
    stamps _hidden_params.response_cost from the deployment's own pricing, which the public
    price map lookup below cannot see (it reads 0 for deployment-priced models).Ú_hidden_paramsNrB   Úresponse_cost)r€   Úcallabler;   ró   ræ   Ú_price_map_cost)r›   ÚgetterÚstampeds      r?   Ú
_call_costr  Y  sR   € ô œG HÐ.>ÀÓEÀuÈdÓS€FÜ08¸Ô0@‘V˜OÔ,Àd€GÜ�'œC¤˜<Ô(Ü�W‹~ÐÜ˜8Ó$Ð$rF   c                 óV   — ddl }	 |j                  | ¬«      xs dS # t        $ r Y yw xY w)zPPublic price map fallback, treating an unmapped model as free rather than fatal.r   N)Úcompletion_responseç        )ÚlitellmÚcompletion_costrÒ   )r›   r  s     r?   r	  r	  d  s5   € ãðØ×&Ñ&¸8Ð&ÓDÒKÈÐKøÜò Ùðús   † œ	(§(Úraw_preferenceÚ	real_is_ac                 ól   — | j                  «       j                  «       }|dk(  r|rdS dS |dk(  r|rdS dS y)z>Map the judge's blind A/B/tie verdict back to real/shadow/tie.ÚaÚrealÚshadowÚbrã   )ÚstripÚlower)r  r  Ú
normalizeds      r?   Ú_unmask_preferencer  n  sF   € à&×,Ñ,Ó.×4Ñ4Ó6€JØ�SÒÙ"ˆvÐ0¨Ð0Ø�SÒÙ$ˆxÐ0¨&Ð0ØrF   ÚconversationÚ
response_aÚ
response_bc                 ó‚   — |dt          }|dt          }t        t        |«      z
  t        |«      z
  }d| | d › d|› d|› d�S )z—The judge prompt under one total character budget: each response is capped, and
    the conversation tail gets whatever budget the responses left over.NzConversation:
z

Response A:
z

Response B:
z

Which response is better?)r-   r.   rÌ   )r  r  r  r  r  Úconversation_budgets         r?   Ú_judge_user_promptr"  x  so   € ð Ð4Ô4Ð5€AØÐ4Ô4Ð5€AÜ!8¼3¸q»6Ñ!AÄCÈÃFÑ!JÐà
˜,Ð(;Ð';Ð'<Ð=Ð>ð ?Ø�sð Ø�sð $ð	$ðrF   Úsumsc                 ó0   ‡ ‡— t        ˆˆ fd„dD «       «      S )Nc              3   ó†   •K  — | ]8  }t        ‰j                  |«      xŠt        t        f«      rt        ‰«      nd –— Œ: y­w)r  N)r;   rB   ró   ræ   )r=   ÚcolumnrD   r#  s     €€r?   r@   z"_leg_eval_spend.<locals>.<genexpr>‡  s=   øè ø€ ò àô !¨¯©°Ó(8Ð!8 ¼3Ä¸,ÔGŒˆcŒ
ÈSÓPñùs   ƒ>A©Ú
judge_costÚshadow_costÚshadow_classifier_cost)Úsum)r#  rD   s   `@r?   Ú_leg_eval_spendr,  †  s   ù€ Üô àMôó ð rF   c                 ó   — d| › �S )Nzspend:shadow_eval:rA   )ré   s    r?   Ú_job_spend_counter_keyr.  �  s   € Ø ˜xÐ(Ð(rF   Úcounter_keyÚfallback_spendÚ
max_budgetc              ƒ   ó@   K  — ddl m}  || ||¬«      ƒ d{  –—† S 7 Œ­w)zçThe leg's spend through the cross-pod counter the key budget gates read. The owner
    degrades internally to the fill-time DB floor and raises only under fail-closed
    enforcement, which the caller honors by skipping the sample.r   )Úget_current_spend)r/  r0  r1  N)Úlitellm.proxy.proxy_serverr3  )r/  r0  r1  r3  s       r?   Ú_job_spend_from_counterr5  ‘  s!   è ø€ õ =á"¨{È>ÐfpÔq×qÐqÐqús   ‚—˜Úcostc              ƒ   óœ   K  — 	 ddl m}  || |¬«      ƒ d{  –—†  y7 Œ# t        $ r!}t        j                  d| |«       Y d}~yd}~ww xY w­w)aF  Advance the counter the moment a cost is known, so even a lost row closes the gate.
    Known failure mode: a Redis outage freezes the counter (the owner invalidates it), the
    gate degrades to the fill floor, and overshoot grows to in-flight plus one TTL of
    samples, the same degradation the key budget counters accept.r   )Úincrement_spend_counter)r/  Ú	incrementNz6shadow_eval: spend counter increment failed for %s: %s)r4  r8  rÒ   r   Úwarning)r/  r6  r8  rÔ   s       r?   Ú_add_job_spend_to_counterr;  š  sH   è ø€ ð
iÝFá%°+ÈÔN×NÒNùÜò iÜ×ÑÐWÐYdÐfg×hÑhûðiüs5   ‚A„ —˜ œA� Ÿ	A	¨A¿AÁA	Á	AÚstager%   c                 ó~   — 	 ddl m}  || |«       y # t        $ r!}t        j                  d| |«       Y d }~y d }~ww xY w)Nr   )Úrecord_shadow_eval_funnel_eventz/shadow_eval: funnel increment failed for %s: %s)Ú#litellm.proxy.db.shadow_eval_funnelr>  rÒ   r   rÓ   )ré   r<  r>  rÔ   s       r?   Ú_record_funnel_eventr@  §  s?   € ð[ÝWá'¨°Õ6øÜò [Ü×ÑÐNÐPVÐXY×ZÑZûð[ús   ‚ ’	<›7·<rÈ   c              ƒ   ó¸  K  — 	 ddl m} ddlm} ddlm}m}m} ddlm	}m
}m} | j                  d«      }	t        |	|«      sy	  ||	|¬«      ƒ d	{  –—†  |	j                  r1 ||	j                  ||d
¬«      ƒ d	{  –—† }
 ||
|	|¬«      ƒ d	{  –—†  y# t        $ r Y yw xY w7 ŒQ7 Œ)7 Œ# |$ r Y y
t         $ r }t#        j$                  d|«       Y d	}~yd	}~ww xY w­w)a˜  Whether the shadowed key or its team is over budget, decided by the same owners
    the request path uses, so counter keys and thresholds can never drift from auth's.

    Advisory and fail-open: real traffic on an over-budget key is already rejected at
    auth (so nothing reaches the success hook), and this gate only closes the race
    where the key crosses its budget while a request is in flight.
    r   )ÚBudgetExceededError)ÚUserAPIKeyAuth)Ú_team_max_budget_checkÚ_virtual_key_max_budget_checkÚget_team_object)Úprisma_clientÚproxy_logging_objÚuser_api_key_cacheFÚuser_api_key_auth)Úvalid_tokenrH  NT)Úteam_idrG  rI  Úcheck_cache_only)Úteam_objectrK  rH  z#shadow_eval: budget read failed: %s)Úlitellm.exceptionsrB  Úlitellm.proxy._typesrC  Úlitellm.proxy.auth.auth_checksrD  rE  rF  r4  rG  rH  rI  ÚImportErrorrB   r;   rL  rÒ   r   rÓ   )rÈ   rB  rC  rD  rE  rF  rG  rH  rI  ÚauthÚteamrÔ   s               r?   Ú_key_or_team_is_over_budgetrU  °  só   è ø€ ð
Ý:Ý7÷	
ñ 	
÷
 	dÑcð —,‘,Ð2Ó3€DÜ�d˜NÔ+ØðGÙ+¸ÐPaÔb×bÐbØ�<Š<Ù /ØŸ™Ø+Ø#5Ø!%ô	!÷ ˆDñ )°TÀtÐ_pÔq×qÐqð
 øô) ò Ùðúð 	cøðøð rùØò ÙÜò GÜ×ÑÐBÀA×FÐFØûðGüs“   ‚C„ B ¤CÁB( ÁB"Á)B( Á:B$Á;B( ÂB&ÂB( ÂCÂ	BÂCÂBÂCÂ"B( Â$B( Â&B( Â(CÂ-CÂ/CÂ7CÃCÃCÃCc                 óP   — | j                  d«      }t        |t        «      r|r|S dS )zúThe shadowed key's team, the identity the judge call already carries in its metadata
    and the router already selects deployments with. Read here too so the arm choice, which
    happens before the router sees the call, is made under the same team.Úuser_api_key_team_idN)rB   r;   rf   )rÈ   rL  s     r?   Ú_forwarded_team_idrX  Ø  s)   € ð —\‘\Ð"8Ó9€GÜ  ¬#Ô.±7ˆ7ÐDÀÐDrF   c                 óT   — | j                  d«      }t        |t        «      r|S t        S )zâThe routing decision a pre-routing strategy wrote to a call's metadata, empty when
    a plain model served it. Read off the sampled request for the control arm, and off the
    shadow call's own write-back for the shadow arm.Úrouting_decisionrK   )rÈ   Údecisions     r?   Ú_routing_decisionr\  à  s'   € ð —l‘lÐ#5Ó6€HÜ! (¬GÔ4ˆ8ÐI¼/ÐIrF   c                 ó~   — t        | «      }|j                  d«      xs |j                  d«      }|�t        |«      S d S )NÚ
tier_labelÚtier)r\  rB   rf   )rÈ   r[  rD   s      r?   Ú_routed_tierr`  è  s;   € Ü'¨Ó1€HØ—‘˜lÓ+ÒC¨x¯|©|¸FÓ/C€CØ�Œ3ˆs‹8Ð0¨DÐ0rF   c                 ó|   — t        | «      j                  d«      }t        |t        t        f«      rt	        |«      S dS )z What the arm's own routing decision says its classifier call billed: the money a
    completion cost alone omits, and 0 for a plain model that never classifies.Úclassifier_costr  )r\  rB   r;   ró   ræ   )rÈ   rD   s     r?   Ú_decision_classifier_costrc  î  s6   € ô # 8Ó,×0Ñ0Ð1BÓC€CÜ# C¬#¬u¨Ô6Œ5�‹:Ð?¸CÐ?rF   Úrouter_namec                 ó<   — t        | «      j                  d«      |k(  S )aN  Whether the router under evaluation served this request, which is what decides
    the direction it belongs to. A forward job skips its own router's traffic, since
    duplicating it would compare the router to itself: guaranteed ties, judge spend for
    zero information. A reverse job samples exactly that traffic and nothing else.Úrouter_model_name)r\  rB   )r·   rd  s     r?   Ú_request_was_routed_byrg  õ  s!   € ô
 Ð-Ó.×2Ñ2Ð3FÓGÈ;ÑVÐVrF   )ÚfrozenÚslotsc                   ó8   — e Zd ZU dZeed<   dZeed<   dZeed<   y)Ú_CallFailurezžA shadow or judge call that produced no usable response. cost carries any spend
    the failed call still billed, so job-level spend figures never undercount.Úerrorr  r6  rb  N)	r¦   r§   r¨   r©   rf   rs   r6  ræ   rb  rA   rF   r?   rk  rk  ý  s#   … ñRð ƒJØ€Dˆ%ÓØ €O�UÔ rF   rk  c                   óJ   — e Zd ZU dZeed<   eed<   edz  ed<   eed<   eed<   y)Ú_ShadowResponsez<A successful shadow call, with what the attempt row records.Útextr_   Nr_  r6  rb  ©r¦   r§   r¨   r©   rf   rs   ræ   rA   rF   r?   rn  rn    s$   … áFà
ƒIØƒJØ
�‰*ÓØ
ƒKØÔrF   rn  c                   ó0   — e Zd ZU dZeed<   eed<   eed<   y)Ú_JudgeVerdictz9A parsed judge verdict, unmasked back to real/shadow/tie.rä   rå   r6  Nrp  rA   rF   r?   rr  rr    s   … áCàƒOØÓØ
„KrF   rr  c                   ó  — e Zd ZU dZ edd¬«      Zeed<   eed<   dZe	ed<   dZ
edz  ed	<   eed
<   eed<   eed<   dZedz  ed<   eed<   dZeed<   dZeed<    ed«      ededefd„«       «       Z ed¬«      dd„«       Zedefd„«       Zy)ÚActiveShadowEvalJobaQ  One active job as the sampling path needs it, validated straight off the untyped
    job row: immutable config plus the attempt count as of the cache fill (the turn
    budget's staleness is bounded by the cache TTL). Every way a row can be unsamplable
    is a validation error here, so a bad row is skipped rather than sampled wrongly.T)rh  Úfrom_attributesÚidrd  ÚforwardÚ	directionNÚbaseline_modelÚshadow_percentageÚjudge_modelÚ	max_turnsr1  Úends_atr   Úattemptsr  ÚspendrÊ   r6   c                 ó^   — |j                   € |j                  t        j                  ¬«      S |S )N)Útzinfo)r�  Úreplacer	   Úutc)ÚclsrÊ   s     r?   Ú_as_utczActiveShadowEvalJob._as_utc/  s'   € ð 6;·\±\Ð5Iˆu�}‰}¤H§L¡Lˆ}Ó1ÐTÈuÐTrF   Úafter)r¿   c                 óX   — | j                   d u| j                  dk(  k7  rt        d«      ‚| S )NÚreversez2baseline_model is set for exactly the reverse jobs)ry  rx  Ú
ValueError©r¤   s    r?   Ú!_baseline_model_matches_directionz5ActiveShadowEvalJob._baseline_model_matches_direction4  s/   € à×Ñ tÐ+°·±À9Ñ1LÒMÜÐQÓRÐRØˆrF   c                 ó6   — | j                   xs | j                  S )zÔThe model the duplicated arm calls: the router itself for a forward job, the
        fixed baseline for a reverse one. Total because the validator above pins
        baseline_model to reverse jobs and only those.)ry  rd  rŠ  s    r?   Úshadow_targetz!ActiveShadowEvalJob.shadow_target:  s   € ð
 ×"Ñ"Ò6 d×&6Ñ&6Ð6rF   )r6   rt  )r¦   r§   r¨   r©   r   Úmodel_configrf   rs   rx  r"   ry  ræ   ró   r1  r   r~  r  r   Úclassmethodr…  r   r‹  Úpropertyr�  rA   rF   r?   rt  rt    sØ   … ñXñ
  T¸4Ô@€LàƒGØÓØ%.€IÐ"Ó.Ø!%€N�C˜$‘JÓ%ØÓØÓØƒNØ#€J�˜‘Ó#ØÓØ€HˆcÓØ€Eˆ5Óá�YÓØðU˜Hð U¨ò Uó ó  ðUñ ˜'Ô"òó #ðð
 ð7˜sò 7ó ñ7rF   rt  Úrecordr~  r  c                 ó°   — 	 t         j                  | «      }|j                  ||dœ¬«      S # t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)zãThe sampling path's view of one job row, or None for a row it cannot sample: an
    unknown direction, or a reverse job with no baseline model to duplicate against.
    Failing closed here is what keeps the dispatch path total.z-shadow_eval: skipping unsamplable job row: %sN)r~  r  )Úupdate)rt  r™   r   r   rÓ   Ú
model_copy)r‘  r~  r  ÚjobrÔ   s        r?   Ú_as_active_jobr–  B  sU   € ðÜ(×7Ñ7¸Ó?ˆð �>‰>¨hÀÑ!Gˆ>ÓHÐHøô ò Ü×ÑÐLÈaÔPÜûðús   ‚, ¬	AµAÁAé   )Úmax_size_in_memoryÚdefault_ttlÚ_jobs_cachezshadow_eval:active_jobsÚ_JOBS_CACHE_KEYc            !       ó€  — e Zd ZdZ	 	 	 	 	 	 d8deg df   dz  deg df   dz  dedz  deeeegee   f   dz  d	eeeged   f   dz  d
eedgdf   dz  ddfd„Z	de
eeedf   f   fd„Zdee   de
eef   dedeedf   fd„Zde
eef   dedededdf
d„Zd9d„Zdededee
eef      dedededed ed!edz  d"e
eef   d#e
eef   ddfd$„Zdd%dd&d&d&dd'œd(ddeded!edz  d)ededed ed*edz  ded+edz  d,ed-ed.ed/edz  ddf d0„Zd1edee
eef      d"e
eef   d#e
eef   dd2f
d3„Zd4edee
eef      ded5ed#e
eef   dd6fd7„Zy):ÚShadowEvalLoggerzPFires blind pairwise shadow evaluations for keys with an active shadow-eval job.NÚrouter_providerzRouter | NoneÚprisma_providerúPrismaClient | NoneÚ
jobs_cacheÚjob_spend_readerÚjob_spend_writerÚfunnel_recorderr%   r6   c                 óÔ   — |xs t         | _        |xs t        | _        |xs t        | _        |xs t
        | _        |xs t        | _        |xs t        | _
        d| _        i | _        y)zóProviders are callables so the proxy's lazily-initialized globals are resolved
        at call time, not at logger construction. The spend reader and writer wrap the
        proxy's cross-pod spend counter; tests inject a plain in-memory pair.r   N)r   Ú_router_providerÚ_default_prisma_providerÚ_prisma_providerrš  r5  Ú_read_job_spendr;  Ú_write_job_spendr@  Ú_record_funnelÚ_inflight_shadow_tasksÚ_job_starts)r¤   rž  rŸ  r¡  r¢  r£  r¤  s          r?   r¥   zShadowEvalLogger.__init__U  si   € ð !0Ò JÔ3JˆÔØ /Ò KÔ3KˆÔØ%Ò4¬ˆÔØ/ÒJÔ3JˆÔØ 0Ò MÔ4MˆÔØ-ÒEÔ1EˆÔØ+,ˆÔ#ð ,.ˆÕrF   .c              ƒ   ób  ‡‡K  — | j                   j                  t        «      ƒ d{  –—† }|�|S | j                  «       }|€t        S 	 |j
                  j                  j                  ddt        j                  t        j                  «      idœ¬«      ƒ d{  –—† }|r[|j
                  j                  j                  dgdddddœdd|D �cg c]  }t        |j                  «      ‘Œ c}ii¬	«      ƒ d{  –—† nd
}|xs g D �ci c]6  }t        |d   «      t!        |d   d   «      t#        |d   xs t$        «      f“Œ8 c}Št'        t)        ˆˆfd„|xs g D «       t+        d«      ¬«      «      }t-        t/        |t+        d«      ¬«      D ��	ci c]  \  }}	|t'        d„ |	D «       «      “Œ c}	}«      }
| j                   j1                  t        |
«      ƒ d{  –—†  i | _        |
S 7 �Œ¼7 �ŒNc c}w 7 Œøc c}w c c}	}w 7 Œ%# t4        $ r&}t7        j8                  d|«       t        cY d}~S d}~ww xY w­w)zèActive jobs by api_key_id, cache-first. A key holds at most one job per
        direction, so the value is a collection. A DB fault returns empty without
        caching, so sampling pauses for that request and the next one retries.NÚgt)Ú
stopped_atr}  )Úwhereré   Tr'  Úin)ÚbyÚcountr+  r±  rA   Ú_countÚ_allÚ_sumc           	   3   ó®   •K  — | ]L  }t        |g‰j                  t        |j                  «      d «      ¢­Ž xŠ�t        |j                  «      ‰f–— ŒN y­w))r   r  N)r–  rB   rf   rv  Ú
api_key_id)r=   r‘  Úattempt_statsr•  s     €€r?   r@   z0ShadowEvalLogger._active_jobs.<locals>.<genexpr>‘  sW   øè ø€ ò à"Ü#1°&Ð#g¸=×;LÑ;LÌSÐQW×QZÑQZË^Ð]eÓ;fÒ#gÐg˜CÐtô ˜V×.Ñ.Ó/°Ô5ñùs   ƒAAr   )r�   c              3   ó&   K  — | ]	  \  }}|–— Œ y ­wr:   rA   )r=   rl   r•  s      r?   r@   z0ShadowEvalLogger._active_jobs.<locals>.<genexpr>š  s   è ø€ Ò4¡F A sœCÑ4ùs   ‚z'shadow_eval: active-job read failed: %s)rš  Úasync_get_cacher›  r¨  Ú_EMPTY_JOBSÚdbÚlitellm_shadowevaljobÚ	find_manyr   Únowr	   rƒ  Úlitellm_shadowevalattemptÚgroup_byrf   rv  ró   r,  r1   rC   Úsortedr   r   r
   Úasync_set_cacher­  rÒ   r   rÓ   )r¤   ÚcachedÚprismaÚrecordsr‘  ÚgroupedÚrowÚby_keyr�   ÚgroupÚjobsrÔ   rº  r•  s               @@r?   Ú_active_jobszShadowEvalLogger._active_jobsl  s1  ùè ø€ ð #×.Ñ.×>Ñ>¼ÓO×OˆØÐØˆMØ×-Ñ-Ó/ˆØˆ>ÜÐð+	Ø#)§9¡9×#BÑ#B×#LÑ#Là"&Ø $¤h§l¡l´8·<±<Ó&@ÐAñð $Mó $÷ ˆGñ ð —i‘i×9Ñ9×BÑBØ �zØà'+¸DÐ\`ÑaØ# dÈ'Ö,RÀ¬S°·±­^Ò,RÐ%SÐTð Có ÷ ð ð ð ð  #š= bö$ð
 ô	 �C˜‘MÓ"Ü˜˜H™ fÑ-Ó.Ü# C¨¡KÒ$B´?ÓCð%ñ ò$ˆMô "Üôà&-¢m°ôô
 # 1›ôó	ˆFô +ÜGNÈvÔ[eÐfgÓ[hÔGi×j¹¸¸e�”eÑ4¨eÔ4Ó4Ñ4ÓjóˆDð ×"Ñ"×2Ñ2´?ÀDÓI×IÐIØ!ˆDÔØˆKð] Pùðûò -Sðúò$ùó$ kàIùô ò 	Ü× Ñ Ð!JÈAÔNÜÕûð	üs›   „#H/§G#¨ H/Á	AG= ÂG&Â3G= ÃG)Ã(G= Ã5G.Ã6G= Ä;G0Ä?AG= ÆG5
Æ*+G= ÇG;ÇG= Ç"H/Ç&G= Ç)G= Ç0G= Ç=	H,ÈH'È!H,È"H/È'H,È,H/Úactive_jobsr·   rè   c                 óD  — g }t        j                  t        j                  «      }|D ]ì  }||j                  k\  s‹|j
                  | j                  j                  |j                  d«      z   |j                  k\  sK|j                  �|j                  |j                  k\  s&t        ||j                  «      |j                  dk(  k7  rŒžt        ||j                  |j                   «      s| j#                  |j                  d«       ŒÜ|j%                  |«       Œî t'        |«      S )ac  The jobs that sample this request. A key can hold one job per direction, and a
        request routed by one job's router while bypassing the other's qualifies for both;
        each is separately budgeted, so both fire. An admitting job that loses the sampling
        dice is counted, so results can weigh judged rows against the traffic they stand for.r   rˆ  Únot_sampled)r   rÁ  r	   rƒ  r}  r~  r­  rB   rv  r|  r1  r  rg  rd  rx  rö   rz  r«  ÚappendrC   )r¤   rÏ  r·   rè   ÚeligiblerÁ  r•  s          r?   Ú_sampled_jobszShadowEvalLogger._sampled_jobs¥  sä   € ð /1ˆÜ—\‘\¤(§,¡,Ó/ˆØò 	!ˆCà�s—{‘{Ò"Ø—<‘< $×"2Ñ"2×"6Ñ"6°s·v±v¸qÓ"AÑAÀSÇ]Á]ÒRØ—N‘NÐ.°3·9±9ÀÇÁÒ3NÜ)Ð*:¸C¿O¹OÓLÐQT×Q^ÑQ^ÐbkÑQkÒlàÜ 
¨C¯F©F°C×4IÑ4IÔJØ×#Ñ# C§F¡F¨MÔ:ØØ�O‰O˜CÕ ð	!ô �X‹ÐrF   r5   r{   Ú
start_timeÚend_timec              ƒ   ó,  K  — 	 |j                  d«      }|€y t        t        |«      «      }t        |t        «      r|nt
        }|j                  t        «      ry t        t        |«      «      ry |j                  d«      xs t
        }|j                  d«      }	|	sy |j                  d«      xs d}
|
sy t        j                  t        |j                  d«      xs d«      «      }|€y |j                  rt        |«      ry | j                  | j                  «       ƒ d {  –—† j                  t        |	«      d«      ||
«      }|sy t        ||t        t        |j                  d«      xs i «      «      |«      }|€$|D ]  }| j!                  |j"                  d	«       Œ  y |\  }}}t%        |«      }t'        |j                  d
«      xs d«      }|j                  d«      du }t)        |«      }|D ]ô  }| j*                  t,        k\  r| j!                  |j"                  d«       Œ3| j.                  j                  |j"                  d«      dz   | j.                  |j"                  <   | xj*                  dz  c_        t1        j2                  | j5                  ||
|||j                  d«      xs d|||||t        t        |«      «      ¬«      «      j7                  | j8                  «       Œö y 7 �ŒÇ# t:        $ r }t=        j>                  d|«       Y d }~y d }~ww xY w­w)NÚstandard_logging_objectrÈ   Úuser_api_key_hashrv  r`   Ú	call_typerA   rN   Úunjudgeabler  r  Ú	cache_hitTÚshedr   rú   r_   )r•  rè   r8   rÕ   Ú
real_modelÚ	real_costÚreal_classifier_costÚreal_cache_hitÚcontrol_tierÚshadow_paramsÚparent_metadataz(shadow_eval: failed to schedule task: %s) rB   r   rd   r;   r   r1   r   r    rÝ   rf   r¢   rÅ   rÔ  rÎ  r×   r   r«  rv  r`  ræ   rc  r¬  r,   r­  ÚasyncioÚcreate_taskÚ_run_shadow_evalÚadd_done_callbackÚ_release_shadow_slotrÒ   r   rÓ   )r¤   r5   r{   rÕ  rÖ  ÚpayloadÚraw_metar·   rÈ   Úapi_key_hashrè   rÎ   rÓ  Úsampler•  r8   rã  rÕ   râ  rß  rá  rà  rÔ   s                          r?   Úasync_log_success_eventz(ShadowEvalLogger.async_log_success_event¿  sà  è ø€ ðB	PØ<B¿J¹JÐG`Ó<aˆGØˆØÜ>¼tÀF»|ÓLˆHÜ2<¸XÄwÔ2O¡hÔUdÐØ×#Ñ#Ô$EÔFØô -¬T°&«\Ô:ØØ%Ÿk™k¨*Ó5ÒH¼ˆHØ"*§,¡,Ð/BÓ"CˆLÙØØ '§¡¨DÓ 1Ò 7°RˆJÙØÜ%×)Ñ)¬#¨g¯k©k¸+Ó.FÒ.LÈ"Ó*MÓNˆCØˆ{ØØ�ŠÔ#BÐCSÔ#TØØ"×0Ñ0Ø×(Ñ(Ó*×*×/Ñ/´°LÓ0AÀ2ÓFÐHXÐZdóˆHñ ØÜ-ØØÜ ¤ g§k¡kÐ2DÓ&EÒ&KÈÓ!LÓMØó	ˆFð ˆ~Ø#ò ?�CØ×'Ñ'¨¯©°Õ>ð?àØ17Ñ.ˆH�m YÜ".Ð/?Ó"@ˆLÜ$ W§[¡[°Ó%AÒ%HÀSÓIˆIØ$+§K¡K°Ó$<ÀÐ$DˆNÜ*CÐDTÓ*UÐ Øò ?�Ø×.Ñ.Ô2NÒNØ×'Ñ'¨¯©°Ô7ØØ+/×+;Ñ+;×+?Ñ+?ÀÇÁÈÓ+JÈQÑ+N�× Ñ  §¡Ñ(Ø×+Ò+¨qÑ0Õ+Ü×#Ñ#Ø×)Ñ)ØØ#-Ø!)Ø"+Ø#*§;¡;¨wÓ#7Ò#=¸2Ø"+Ø-AØ'5Ø%1Ø&3Ü(8¼Ð>NÓ9OÓ(Pð *ó ó÷ $Ñ# D×$=Ñ$=Õ>ñ)?ð' +úôP ò 	PÜ× Ñ Ð!KÈQ×OÑOûð	Püs­   ‚L„K( —L˜AK( ÁLÁK( Á.LÁ/,K( ÂLÂK( Â3LÂ43K( Ã'LÃ(K( Ã?LÄ K( ÄK%Ä%K( ÅLÅAK( ÆLÆEK( Ë$LË%K( Ë(	LË1LÌLÌLÌLc                 ó.   — | xj                   dz  c_         y )Nrú   )r¬  )r¤   Ú_tasks     r?   ré  z%ShadowEvalLogger._release_shadow_slot
  s   € Ø×#Ò# qÑ(Ö#rF   r•  r8   rÕ   rÞ  rß  rà  rá  râ  rã  rä  c              ƒ   óÒ  K  — | j                  «       }	 |€| j                  |j                  d«       yt        |«      ƒ d{  –—† r| j                  |j                  d«       y|j                  �o	 | j                  t        |j                  «      |j                  |j                  «      ƒ d{  –—† }||j                  k\  r| j                  |j                  d«       y| j                  |j                  ||
|«      ƒ d{  –—† }t        |t         «      rC| j                  ||||	d|j"                  |j$                  |j&                  |||¬«      ƒ d{  –—†  y	 | j)                  |j*                  |||j,                  |¬	«      ƒ d{  –—† }t        |t         «      rO| j                  ||||	d|j"                  ||j$                  |j$                  |j&                  |||¬
«      ƒ d{  –—†  y| j                  ||||	|j.                  |||j0                  |j$                  |j$                  |j&                  |||¬«      ƒ d{  –—†  y7 �Œ7 �Œ™# t        $ rG}t        j                  d|j                  |«       | j                  |j                  d«       Y d}~yd}~ww xY w7 �Œ�# t        $ rG}t        j                  d||«       | j                  ||||	dd|› �|||¬«	      ƒ d{  –—†7   Y d}~yd}~ww xY w7 �Œ¡7 �Œp7 �Œ7 Œ½# t        $ r^}t        j                  d||«       | j                  ||||	dd|› �||j$                  |j&                  |||¬«      ƒ d{  –—†7   Y d}~yd}~ww xY w­w)aJ  Budget gate -> shadow call -> blind judge -> one attempt row, and every exit
        in exactly one coverage bucket: the gates that decline to spend on an admitted
        sample (no DB to record into, an over-budget key, an unverifiable or exhausted
        eval budget) count it withheld, so eligible traffic still reconciles as
        not_sampled + unjudgeable + shed + withheld + attempt rows. The prisma gate sits
        above the dispatch so no provider spend happens without a place to record the
        outcome, and the budget read lives here rather than in the success hook.NÚwithheldz;shadow_eval: budget unverifiable for %s, sample skipped: %sz'shadow_eval: pipeline failed for %s: %srl  zpipeline error: )Úoutcomerl  rß  rà  rá  )ró  rl  r)  r*  rß  rà  rá  )r{  r8   rÕ   Úshadow_texträ  )	ró  rl  r  r(  r)  r*  rß  rà  rá  )
ró  r  rÞ  rå   r(  r)  r*  rß  rà  rá  )ró  rl  r  r)  r*  rß  rà  rá  )r¨  r«  rv  rU  r1  r©  r.  r  rÒ   r   r:  Ú_call_router_shadowr�  rÓ   Ú_record_attemptr;   rk  rl  r6  rb  Ú_call_judger{  ro  rä   rå   )r¤   r•  rè   r8   rÕ   rÞ  rß  rà  rá  râ  rã  rä  rÇ  r  rÔ   r  Úverdicts                    r?   rç  z!ShadowEvalLogger._run_shadow_eval  sŠ  è ø€ ð* ×-Ñ-Ó/ˆð	Øˆ~Ø×#Ñ# C§F¡F¨JÔ7ØÜ0°ÓA×AÐAØ×#Ñ# C§F¡F¨JÔ7ØØ�~‰~Ð)ðØ)-×)=Ñ)=Ô>TÐUX×U[ÑU[Ó>\Ð^a×^gÑ^gÐil×iwÑiwÓ)x×#x�Eð
 ˜CŸN™NÒ*Ø×'Ñ'¨¯©°
Ô;ØØ"&×":Ñ":¸3×;LÑ;LÈhÐXeÐgvÓ"w×wˆFô �fœlÔ+Ø×&Ñ&ØØØØØØ—l‘lØ"ŸK™KØ'-×'=Ñ'=Ø#Ø%9Ø-ð 'ó ÷ ð ð ð8	Ø#'×#3Ñ#3ØŸO™OØ!Ø#Ø"ŸK™KØ /ð $4ó $÷ ˆGô ˜'¤<Ô0Ø×*Ñ*ØØØØ Ø#Ø!Ÿ-™-Ø!Ø&Ÿ|™|Ø &§¡Ø+1×+AÑ+AØ'Ø)=Ø#1ð +ó ÷ ð ð Ø×&Ñ&ØØØØØ×*Ñ*ØØ%Ø"×-Ñ-Ø"Ÿ<™<Ø"ŸK™KØ'-×'=Ñ'=Ø#Ø%9Ø-ð 'ó ÷ ñ ðK Bùð
 $yúÜ ò Ü"×*Ñ*Ð+hÐjm×jpÑjpÐrsÔtØ×'Ñ'¨¯©°
Ô;Üûðúð xúÜò 	Ü× Ñ Ð!JÈJÐXYÔZØ×&Ñ&ØØØØØØ(¨¨Ð,Ø#Ø%9Ø-ð 'ó 
÷ 
ñ 
ô ûð	úðùð ùðùð ùô  ò 	Ü× Ñ Ð!JÈJÐXYÔZØ×&Ñ&ØØØØØØ(¨¨Ð,ØØ"ŸK™KØ'-×'=Ñ'=Ø#Ø%9Ø-ð 'ó ÷ ö ûð	üsD  ‚M'”J ²M'³J ÁIÁ J Á"M'Á#J Á0=I	 Â-IÂ.I	 Â2+J ÃM'Ã!J Ã?JÄ J ÄAM'ÅK2ÅM'Å-K= ÆK5ÆAK= Ç#K8Ç$K= Ç(M'Ç)AK= È=K;È>K= ÉM'ÉJ ÉI	 É		JÉ=JÊJ ÊM'ÊJÊJ Ê	K/Ê(7K*ËK"Ë K*Ë%M'Ë*K/Ë/M'Ë5K= Ë8K= Ë;K= Ë=	M$ÌAMÍMÍMÍM'ÍM$Í$M'r`   r  )r  rÞ  rå   r(  r)  r*  rl  rÇ  ró  r  rå   r(  r)  r*  rl  c             ƒ   óð  K  — ||z   |z   }|dkD  r-| j                  t        |j                  «      |«      ƒ d {  –—†  |€y 	 |j                  j                  j                  |j                  |||j                  dk(  r|n|	r|	j                  nd |
xs d |	r|	j                  nd ||||||||r	|d t         nd dœ¬«      ƒ d {  –—†  y 7 ŒŒ7 Œ# t        $ r!}t        j                  d||«       Y d }~y d }~ww xY w­w)Nr   rˆ  )ré   rè   ró  r_  rÞ  Úshadow_modelrå   r(  r)  r*  rß  rà  rá  rl  )Údataz,shadow_eval: attempt write failed for %s: %s)rª  r.  rv  r¾  rÂ  Úcreaterx  r_  r_   r0   rÒ   r   rÓ   )r¤   rÇ  r•  rè   râ  ró  rß  rà  rá  r  rÞ  rå   r(  r)  r*  rl  Ú
eval_spendrÔ   s                     r?   rö  z ShadowEvalLogger._record_attempt�  s  è ø€ ð& '¨Ñ4Ð7MÑMˆ
Ø˜Š>Ø×'Ñ'Ô(>¸s¿v¹vÓ(FÈ
ÓS×SÐSØˆ>Øð	`Ø—)‘)×5Ñ5×<Ñ<à!Ÿf™fØ",Ø&Ø,/¯M©M¸YÒ,F™LÑ\bÈVÏ[Ê[ÐhlØ",Ò"4°Ù4: F§L¢LÀØ",Ø",Ø#.Ø.DØ!*Ø,@Ø&4Ù9>˜UÐ#4Ô$4Ñ5ÀDñð =ó ÷ ñ ð	 Tøðùô$ ò 	`Ü× Ñ Ð!OÐQ[Ð]^×_Ñ_ûð	`üsL   ‚6C6¸C¹C6ÁA>C	 Â?CÃ C	 ÃC6ÃC	 Ã		C3ÃC.Ã)C6Ã.C3Ã3C6Útarget_modelz_ShadowResponse | _CallFailurec           
   ƒ   ó–  K  — | j                  «       }|€t        d«      S t        |t        «      }	  |j                  d||D �cg c]  }t        |«      ‘Œ c}|dg dœ|¤Žƒ d{  –—† }t        |«      }
|
s t        dt        |«      t        |«      ¬	«      S t        |
t        t        |d
d«      xs t!        |«      j#                  d«      xs d«      t%        |«      t        |«      t        |«      ¬«      S c c}w 7 Œ•# t        $ rB}	t        j                  d|	«       t        dt        |	«      › �t        |«      ¬«      cY d}	~	S d}	~	ww xY w­w)a7  Send the prompt through the arm nobody was served: the auto-router under
        evaluation, or a reverse job's fixed baseline model. The metadata carries the
        shadowed key's identity (spend attribution) and receives a routing decision
        write-back, which a plain baseline model simply never makes.Nz no router configured on this podr   )r_   r8   rÈ   Únum_retriesÚ	fallbacksz#shadow_eval: router call failed: %szshadow router call failed: )rb  z(shadow router returned an empty response)r6  rb  r_   Úrouted_modelr`   )ro  r_   r_  r6  rb  rA   )r¦  rk  r   r$   rÙ   rd   rÒ   r   rÓ   r  rc  r‹   r  rn  rf   r€   r\  rB   r`  )r¤   rþ  r8   rã  rä  ÚrouterÚshadow_metadatar>   r›   rÔ   ro  s              r?   rõ  z$ShadowEvalLogger._call_router_shadow½  s_  è ø€ ð ×-Ñ-Ó/ˆØˆ>ÜÐ BÓCÐCä/°ÔA_Ó`ð 	ð	Ø$6 F×$6Ñ$6ð 	%Ø"à%-öØ !”D˜•Gòð )ØØñ	%ð  ñ	%÷ 	ˆHô  ' xÓ0ˆÙÜØ:Ü Ó)Ü 9¸/Ó Jôð ô
 ØÜ”g˜h¨°Ó6ÒvÔ:KÈOÓ:\×:`Ñ:`ÐaoÓ:pÒvÐtvÓwÜ˜oÓ.Ü˜HÓ%Ü5°oÓFô
ð 	
ùò+ð	ùô ò 	Ü× Ñ Ð!FÈÔJÜØ-¬o¸aÓ.@Ð-AÐBÜ 9¸/Ó Jôõ ûð	üsS   ‚.E	±C; ÁC4ÁC; Á#C9Á$C; Á(BE	Ã4C; Ã;	EÄ7EÄ;EÄ<E	ÅEÅE	r{  rô  z_JudgeVerdict | _CallFailurec           
   ƒ   ó>  K  — t        j                   «       dk  }|r|n|}|r|n|}dj                  d„ |D «       «      }	t        |t        «      }
dt        dœdt        |	||«      dœg}	 t        | j                  «       ||t        |«      dt        t        |
¬«      ƒ d	{  –—† }	 |d   d   d   d   xs d}t        j!                  t#        |«      «      }t'        t)        |j*                  |«      t-        dt/        d|j0                  «      «      t%        |«      ¬«      S 7 Œ~# t        $ r.}t        j                  d
|«       t        d|› �«      cY d	}~S d	}~ww xY w# t        $ r9}t        j                  d|«       t        d|› �t%        |«      ¬«      cY d	}~S d	}~ww xY w­w)zHBlind pairwise judge with A/B labels randomized to cancel position bias.g      à?ú
c              3   óÊ   K  — | ][  }|j                  d «      �Ht        |j                  dd«      «      j                  «       › dt        |j                  d «      «      › �–— Œ] y­w)r…   NÚroleÚuserrû   )rB   rf   Úupperr   r<   s     r?   r@   z/ShadowEvalLogger._call_judge.<locals>.<genexpr>û  s]   è ø€ ò (
àØ�u‰u�YÓÐ+ô �1—5‘5˜ Ó(Ó)×/Ñ/Ó1Ð2°"Ô5NÈqÏuÉuÐU^ÓO_Ó5`Ð4aÔbñ(
ùs   ‚A!A#rZ   )r  r…   r	  r   )rL  rR   rQ   Úresponse_formatrÈ   Nz"shadow_eval: judge call failed: %szjudge call failed: r}   r~   r…   r`   z*shadow_eval: unparseable judge verdict: %szunparseable judge verdict: )r6  r  g      ð?)rä   rå   r6  )ÚrandomÚjoinr   r#   rÞ   r"  r   r¦  rX  r/   rç   rÒ   r   rÓ   rk  rà   r™   r   r  rr  r  rä   ÚmaxÚminrå   )r¤   r{  r8   rÕ   rô  rä  r  r  r  r  Újudge_metadataÚjudge_messagesr›   rÔ   rD   rø  s                   r?   r÷  zShadowEvalLogger._call_judgeî  s¤  è ø€ ô "Ÿ=™=›?¨SÑ0ˆ	Ù)2™I¸ˆ
Ù+4™K¸)ˆ
à"Ÿi™iñ (
àô(
ó 
ˆô
 !DÀOÔUrÓ sˆàÔ*FÑGàÜ-¨l¸JÈ
ÓSñð!
ˆð	;Ü$5Ø×%Ñ%Ó'ØØÜ*¨?Ó;ØÜ2Ü >Ø'ô	%÷ 	ˆHð	^Ø! )Ñ,¨QÑ/°	Ñ:¸9ÑEÒKÈˆCÜ,×;Ñ;Ô<NÈsÓ<SÓTˆGô Ü)¨'×*<Ñ*<¸iÓHÜ˜3¤ C¨×);Ñ);Ó <Ó=Ü˜HÓ%ô
ð 	
ð'	ùô ò 	;Ü× Ñ Ð!EÀqÔIÜÐ"5°a°SÐ 9Ó:Õ:ûð	;ûô ò 	^Ü× Ñ Ð!MÈqÔQÜÐ"=¸a¸SÐ AÌ
ÐS[ÓH\Ô]Õ]ûð	^üss   ‚A%FÁ(5D ÂDÂD Â#0E ÃA	FÄD Ä	EÄ'#EÅ
EÅFÅEÅFÅ	FÅ!.FÆFÆFÆFÆF)NNNNNN)rð  zasyncio.Task[None]r6   N)r¦   r§   r¨   r©   r   r   rf   ræ   r   r¥   r   rC   rt  rÎ  r   r«   rÔ  rî  ré  r¬   rç  rn  rö  rõ  r÷  rA   rF   r?   r�  r�  R  s¼  „ ÙZð AEØFJØ+/ØSWØKOØQUñ.à! " oÐ"5Ñ6¸Ñ=ð.ð " "Ð&;Ð";Ñ<¸tÑCð.ð " DÑ(ð	.ð
 # C¨°Ð#6¸	À%Ñ8HÐ#HÑIÈDÑPð.ð # C¨ <°¸4±Ð#@ÑAÀDÑHð.ð " 3Ð(?Ð"@À$Ð"FÑGÈ$ÑNð.ð 
ó.ð.5 G¨C°Ð7JÈCÐ7OÑ1PÐ,PÑ$Qó 5ðràÐ1Ñ2ðð " # v +Ñ.ðð ð	ð
 
Ð" CÐ'Ñ	(óð4IPà˜˜V˜Ñ$ðIPð ðIPð ð	IPð
 ðIPð 
óIPóV)ð
~à ð~ð ð~ð ˜7 3¨ ;Ñ/Ñ0ð	~ð
 ð~ð ð~ð ð~ð $ð~ð ð~ð ˜D‘jð~ð ˜s F˜{Ñ+ð~ð !  f Ñ-ð~ð 
ó~ðV *.ØØ#'ØØ Ø(+Ø ò#,`à%ð,`ð !ð,`ð ð	,`ð
 ˜D‘jð,`ð ð,`ð ð,`ð $ð,`ð ð,`ð   $Ñ&ð,`ð ð,`ð ˜D‘Lð,`ð ð,`ð ð,`ð  !&ð!,`ð" �T‰zð#,`ð$ 
ó%,`ð\/
àð/
ð ˜7 3¨ ;Ñ/Ñ0ð/
ð ˜s F˜{Ñ+ð	/
ð
 !  f Ñ-ð/
ð 
*ó/
ðb2
àð2
ð ˜7 3¨ ;Ñ/Ñ0ð2
ð ð	2
ð
 ð2
ð !  f Ñ-ð2
ð 
(ô2
rF   r�  r½  c                  ó2   — 	 ddl m}  | S # t        $ r Y y w xY w)Nr   ©rG  )r4  rG  rR  r  s    r?   r§  r§  &  s&   € ðÝ<ð Ðøô ò Ùðús   ‚
 Š	•)r6   r   )ƒr©   rå  rï   r  rü   Úcollections.abcr   r   r   r   Údataclassesr   r   r	   Ú	itertoolsr
   Úoperatorr   Útypesr   Útypingr   r   r   Úpydanticr   r   r   r   r   r   Úlitellm._loggingr   Úlitellm.caching.in_memory_cacher   Úlitellm.constantsr   Ú"litellm.integrations.custom_loggerr   Ú'litellm.litellm_core_utils.core_helpersr   Ú1litellm.litellm_core_utils.internal_call_metadatar   Ú$litellm.litellm_core_utils.llm_judger   r   r   r   Ú*litellm.litellm_core_utils.redact_messagesr    Ú litellm.llms.base_llm.base_utilsr!   Ú8litellm.types.management_endpoints.auto_router_endpointsr"   Úlitellm.types.utilsr#   r$   r?  r%   Úlitellm.proxy.utilsr&   Úlitellm.routerr'   r(   r*   rs   r,   r-   r.   r/   r0   r1   rf   r«   r2   rC   r3   r4   rE   rM   rP   rb   rV   rm   rz   r‹   rœ   rž   r­   r®   r¯   r¶   r¬   rÅ   rÉ   rÍ   r×   rÝ   rÞ   rà   rç   ræ   rö   ÚBaseExceptionr  r  r	  r  r"  r,  r.  r5  r;  r@  rU  rX  r\  r`  rc  rg  rk  rn  rr  rt  ró   r–  rš  r›  r�  r½  r§  rA   rF   r?   ú<module>r)     sè  ðòTó Û Û Û ß BÓ BÝ !ß 'Ý Ý Ý "ß 0Ñ 0ç j× jå +Ý 9Ý ?Ý ;Ý TÝ a÷ó õ UÝ JÝ Xß ]áÝIÝ0Ý%Ý:ð
 "$Ð ˜Ó #ð ')Ð ˜eÓ (ð $)Ð ˜5Ó (Ø!'Ð ˜Ó 'ð "&Ð ˜Ó %àÐ �%Ó á/?ÀÓ/C€��w˜s F˜{Ñ+Ñ,Ó Cñ  +¨7°3¸°;Ñ+?Ó@Ð �uÓ @Ù +¨E°'¸#¸v¸+Ñ2FÈÐ2KÑ,LÓ MÐ ˜Ó MÙ +¨E°&¸#°+Ñ,>Ó ?Ð ˜Ó ?ð`˜7 3¨ ;Ñ/ð `°E¸'À#ÀvÀ+Ñ:NÐPSÐ:SÑ4Tó `ð
B˜W S¨& [Ñ1ð B°g¸cÀ6¸kÑ6Jó BðVØ�C˜�KÑ ðVØ4;¸CÀ¸KÑ4HðVàˆS�&ˆ[ÑóVñ )2Ð2uÓ(vÐ  Ó vðØ�C˜�KÑ ðØ5<¸SÀ&¸[Ñ5IðàˆS�&ˆ[ÑóðBØ�C˜�KÑ ðØ5<¸SÀ&¸[Ñ5IðàˆS�&ˆ[ÑóðB6 6ð 6¨có 6ð";¨ð ;°3ó ;÷0'ñ 'ñ. Ð6Ð8HÐV[Ô\€	ˆ5Ó \Ù#Ð$IÐK[ÐimÔn€�Ó nÙ#Ð$@ÐBWÐeiÔj€�Ó jñ
 (1Øtó(Ð ˜uó ð
°g¸cÀ6¸kÑ6Jð Ètó ñ$ $-Ð-jÓ#kÐ ˜5Ó kðJ˜Vð J¨ó Jð'Ø	ð'à�C˜�KÑ ð'ð ˜c 6˜kÑ*ð'ð ð	'ð
 ˆ5�˜˜f˜Ñ% sÐ*Ñ+¨W°S¸&°[Ñ-AÀ3ÐFÑGÈ$ÑNó'ñ6 2BàØ Ø,Ø$Ø#ñó2€ˆe�G˜C Ð,Ñ-Ñ.ó ð
'Ð ˜eó 
ô�iô ñ )FÀoÓ(VÐ  Ó Vð'˜Sð '¨#ð '¸5ð 'ÀTó 'ð0�}ð 0¨ó 0ð%˜ð % Eó %ð˜fð ¨ó ð sð °tð Àó ð Sð °cð Àsð Èsó ð˜' # v +Ñ.ð °5ó ð) 3ð )¨3ó )ðr¨sð rÀEð rÐW\ð rÐafó rð
i°ð 
i¸Eð 
iÀdó 
ið[ ð [Ð-Dð [Èó [ð%°¸¸V¸Ñ0Dð %Èó %ðPE ¨¨f¨Ñ!5ð E¸#À¹*ó EðJ ¨¨V¨Ñ 4ð J¸ÀÀfÀÑ9Mó Jð1˜7 3¨ ;Ñ/ð 1°C¸$±Jó 1ð@¨°°V°Ñ(<ð @Àó @ðW¨W°S¸&°[Ñ-Að WÐPSð WÐX\ó Wñ �$˜dÔ#÷!ð !ó $ð!ñ �$˜dÔ#÷ð ó $ðñ �$˜dÔ#÷ð ó $ðô$7˜)ô $7ðN	I˜6ð 	I¨Sð 	I¸ð 	IÐCVÐY]ÑC]ó 	Iñ #°aÐE\Ô]€ˆUÓ ]Ø2€�Ó 2ôN
�|ô N
ñb EUÐUWÓDX€ˆU�7˜3 Ð&9¸3Ð&>Ñ ?Ð?Ñ@ÑAÓ XôrF   