Ë
    tPêj©Ï  ã                  ó¼  — U d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	m
Z
 ddlmZmZ ddl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Zdd
lmZ ddlmZ ddl m!Z!m"Z" ddl#m$Z$m%Z%m&Z& ddl'm(Z(m)Z) ddl*m+Z+ ddl,m-Z-m.Z.m/Z/m0Z0 ddl1m2Z2 ddl3m4Z4m5Z5 ddl6m7Z7m8Z8 ddl9m:Z: erddl;m<Z= ddl>m?Z? dZ@deAd<   dZBdeAd<   dZCdeAd<   dZDdeAd<   d ZEdeAd!<   d"ZFdeAd#<   d$ZGdeAd%<   d$ZHdeAd&<   d'ZIdeAd(<   d)ZJdeAd*<   d+ZKdeAd,<   d-ZLdeAd.<   d/ZMdeAd0<   d1ZNdeAd2<    eOh d3£«      ZPdeAd4<    eOh d5£«      ZQdeAd6<    eOd7„ d8D «       «      ZRdeAd9<   dWd:„ZSdXd;„ZTdYd<„ZUdZd=„ZVd[d>„ZWd\d?„ZXd]d@„ZYd^dA„ZZd_d`dB„Z[dadC„Z\dbdD„Z]dcdE„Z^dcdF„Z_dddG„Z`dedH„ZadfdI„ZbdgdJ„ZcdhdK„ZddidL„ZedhdM„ZfdjdN„ZgdjdO„Zh	 	 	 	 	 	 dkdP„Zi	 	 	 	 	 	 dldQ„Zj	 	 	 	 	 	 dldR„Zke G dS„ dT«      «       Zl G dU„ dVe!«      Zmy)muà  Compresr guardrail â€” query-aware, recoverable context compression.

Compresses bulky message content (tool outputs by default) through the
Compresr API before the request reaches the LLM. Each compressed message
carries a hash marker; a ``compresr_retrieve`` tool is injected so the model
can fetch the original content back through the agentic loop when the
compressed version is not enough â€” making compression recoverable instead
of lossy.

Unlike gateway-side compressors that operate on whole message lists, each
target is compressed *query-aware*: the query sent to Compresr is the intent
of the tool call that produced the message (``name + arguments``, resolved
via ``tool_call_id``), falling back to the last user message.
é    )ÚannotationsN)ÚCounterÚOrderedDict)Ú	dataclassÚfield)ÚTYPE_CHECKINGÚAnyÚFinalÚLiteralÚ	TypeGuard)Úurlparse)ÚHTTPException)ÚResponse)Úverbose_proxy_logger)Ú PRE_CALL_EXECUTED_GUARDRAILS_KEY)ÚCustomGuardrailÚlog_guardrail_information)Úget_attribute_or_keyÚget_tool_calls_from_responseÚhas_tool_with_name)Úget_async_httpx_clientÚhttpxSpecialProvider)ÚUserAPIKeyAuth)Úassistant_text_from_responseÚcontent_to_textÚis_all_text_partsÚmerge_rewritten_text_parts)Úget_secret_str)ÚGuardrailEventHooksÚMode)ÚAgenticLoopPlanÚAgenticLoopRequestPatch)ÚGenericGuardrailAPIInputs)ÚLogging)ÚGuardrailConfigModelzx-compresr-bypassr
   ÚBYPASS_HEADERÚcompresr_retrieveÚCOMPRESR_RETRIEVE_TOOL_NAMEzhttps://api.compresr.aiÚDEFAULT_API_BASEÚlatte_v2ÚDEFAULT_COMPRESSION_MODELg      à?Ú DEFAULT_TARGET_COMPRESSION_RATIOéô  ÚDEFAULT_MIN_CHARS_TO_COMPRESSi„  Ú_ORIGINALS_TTL_SECONDSÚ"_NO_SCOPE_WARNING_INTERVAL_SECONDSé   Ú_MAX_TRACKED_CALLSi    Ú_DEFAULT_MAX_BYTES_PER_CALLi   Ú_MAX_TOTAL_STORE_BYTESé   Ú_MAX_RETRIEVALS_PER_LOOPg      N@Ú_COMPRESS_TIMEOUT_SECONDSzintegration:litellmÚ_SOURCE_TAG>   ÚqueryÚinputsÚcontextÚ _RESERVED_COMPRESSION_PARAM_KEYS>   úmetadata.googúmetadata.azure.comúmetadata.azure.internalúmetadata.google.internalÚ_BLOCKED_METADATA_HOSTSc              #  óF   K  — | ]  }t        j                  |«      –— Œ y ­w©N)Ú	ipaddressÚ
ip_address)Ú.0Úips     ú/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/guardrails/guardrail_hooks/compresr/compresr.pyú	<genexpr>rI   e   s    è ø€ ò )Ø!#„I×Ñ˜×ñ)ùs   ‚!)z169.254.169.254zfd00:ec2::254z100.100.100.200z168.63.129.16Ú_BLOCKED_METADATA_IPSc                ó"  — 	 t        j                  | «      }t        |t         j                  «      r|j                  �|j                  S |S # t        $ r: 	 t        j                  t        | d«      «      }n# t        t        f$ r Y Y yw xY wY Œvw xY w)zØParse ``host`` as an IP literal, covering the alternate spellings the
    socket layer accepts (decimal/hex single-integer IPv4, IPv4-mapped IPv6)
    so a blocked address cannot be smuggled past a string comparison.r   N)rD   rE   Ú
ValueErrorÚintÚ	TypeErrorÚ
isinstanceÚIPv6AddressÚipv4_mapped)ÚhostÚaddrs     rH   Ú_parse_ip_literalrT   j   s�   € ðÜ×#Ñ# DÓ)ˆô �$œ	×-Ñ-Ô.°4×3CÑ3CÐ3OØ×ÑÐØ€Køô ò ð	Ü×'Ñ'¬¨D°!«Ó5‰DøÜœ:Ð&ò 	Úð	úñ ðús5   ‚A Á	BÁA5Á4BÁ5BÂBÂBÂBÂBc                ó  — t        | «      }|j                  dvrt        d|j                  ›�«      ‚|j                  xs dj	                  «       }|st        d«      ‚t        |«      }|t        v s
|�|t        v rt        d|›d�«      ‚| S )u  Return ``url`` if it passes basic outbound-target checks, else raise.

    Best-effort defense in depth for a mis/maliciously-configured ``api_base``:
    rejects non-http(s) schemes and cloud-metadata IPs/hosts (incl. alternate IP
    encodings); private ranges are allowed for on-prem deployments. NOT a complete
    SSRF control â€” no DNS resolution, and the shared client follows redirects and
    re-resolves DNS (TOCTOU / rebinding); ``api_base`` is trusted operator config,
    so this is an accepted limitation.
    )ÚhttpÚhttpsz>Compresr guardrail api_base must be http or https, got scheme=Ú z'Compresr guardrail api_base has no hostzCompresr guardrail api_base z! is a blocked cloud-metadata host)r   ÚschemerL   ÚhostnameÚlowerrT   rA   rJ   )ÚurlÚparsedrR   Ú
ip_literals       rH   Ú_validate_api_baser_   z   s–   € ô ˜S“M€FØ‡}�}Ð-Ñ-ÜÐYÐZ`×ZgÑZgÐYjÐkÓlÐlØ—?‘?Ò( b×/Ñ/Ó1€DÙÜÐBÓCÐCÜ)¨$Ó/€JØÔ&Ñ&¨:Ð+AÀjÔTiÑFiÜÐ7¸°xÐ?`ÐaÓbÐbØ€Jó    c                ó"   — t        | t        «      S rC   )rO   Údict©Úvalues    rH   Ú_is_str_object_dictre   �   ó   € Ü�eœTÓ"Ð"r`   c                ó"   — t        | t        «      S rC   )rO   Úlistrc   s    rH   Ú_is_object_listri   ”   rf   r`   c                ón   — t        | t        «      r|S t        | «      rt        | «      rt	        | |«      S | S )až  Write ``new_text`` back into a ``content`` value, preserving shape.

    ``str`` content is replaced directly. An all-text part list collapses to a
    single part carrying the last declared cache_control breakpoint. Anything
    else is returned unchanged: breakpoints are positional, so one compressed
    string cannot be written back across a non-text part without moving text
    to the other side of it.
    )rO   Ústrri   r   r   )ÚcontentÚnew_texts     rH   Ú_replace_text_in_contentrn   ˜   s5   € ô �'œ3ÔØˆÜ�wÔÔ$5°gÔ$>Ü)¨'°8Ó<Ð<Ø€Nr`   c                ój  — t        | j                  d«      xs d«      j                  «       }| j                  d«      }t        |t        «      r	 t        j                  |d¬«      }n|�t        |«      j                  «       nd}|r	|r|› d|› �S |xs |S # t        t        f$ r t        |«      }Y Œ-w xY w)NÚnamerX   Ú	arguments)ú,ú:)Ú
separatorsz: )	rk   ÚgetÚstriprO   rb   ÚjsonÚdumpsrN   rL   )Úfnrp   ÚargsÚargs_strs       rH   Ú_render_tool_intentr|   ¨   s¨   € Ü�b—f‘f˜V“nÒ*¨Ó+×1Ñ1Ó3€DØ—&‘&˜Ó%€DÜ�$œÔð	!Ü—z‘z $°:Ô>‰Hð )-Ð(8”3�t“9—?‘?Ô$¸bˆÙ‘Ø��r˜(˜Ð$Ð$ØÒ�8Ðøô œ:Ð&ò 	!Ü˜4“yŠHð	!ús   ÁB ÂB2Â1B2c                ó€  — | |   }|j                  d«      dvr|S |j                  d«      }|j                  d«      }t        |dz
  dd«      D ]î  }| |   }|j                  d«      dk7  rŒ|j                  d«      }t        |t        «      rf|D ]a  }	t        |	t        «      sŒ|sŒ|	j                  d	«      |k(  sŒ,|	j                  d
«      }
t        t        |
t        «      r|
ni «      }|sŒ]|c c S  |j                  d«      }t        |t        «      sŒÆ|sŒÉ|j                  d«      |k(  sŒÞt        |«      }|sŒì|c S  |S )a  Query used to compress ``messages[target_idx]``.

    Tool/function outputs are compressed against the intent of the tool call
    that produced them (found via ``tool_call_id`` on a prior assistant
    message); everything else uses the last user message.
    Úrole©ÚtoolÚfunctionÚtool_call_idrp   é   éÿÿÿÿÚ	assistantÚ
tool_callsÚidr�   Úfunction_call)ru   ÚrangerO   rh   rb   r|   )ÚmessagesÚ
target_idxÚfallbackÚmsgr‚   Úfn_nameÚjÚprevr†   Útcry   ÚintentÚfcs                rH   Ú_query_for_targetr”   ·   s.  € ð ˜*Ñ%€CØ
‡w�wˆvƒÐ2Ñ2ØˆàŸ'™' .Ó1€LØ—W‘W˜V“_€GÜ�: ‘> 2 rÓ*ò ˆØ˜‰{ˆØ�8‰8�FÓ˜{Ò*ØØ—X‘X˜lÓ+ˆ
Ü�j¤$Ô'Ø ò &�Ü! "¤dÔ+ØÚ B§F¡F¨4£L°LÓ$@ØŸ™ 
Ó+�BÜ0´zÀ"ÄdÔ7K±ÐQSÓT�FÚØ%œð&ð �X‰X�oÓ&ˆÜ�bœ$Õ¢G°·±°v³À'Ó0IÜ(¨Ó,ˆFÚØ’ð)ð* €Or`   c                óL   — 	 | �t        | «      S dS # t        t        f$ r Y yw xY w)aH  Parse a token-stat field defensively.

    A malformed-but-200 response must not raise here: ``_call_compress`` has
    already returned successfully, so the fail_open/fail_closed decision is
    behind us. A bare ``int()`` on a non-numeric field would surface as an
    unhandled 500 even when ``fail_open`` is configured.
    r   )rM   rN   rL   rc   s    rH   Ú	_safe_intr–   Ü   s2   € ðØ"Ð.Œs�5‹zÐ5°AÐ5øÜ”zÐ"ò Ùðús   ‚ � ‘#¢#c                ób   — 	 t        | dd«      }|xs dd| S # t        j                  $ r Y yw xY w)a  Read a response body for error logging without letting the read itself
    raise. A corrupt ``Content-Encoding`` makes ``httpx``'s ``.text`` raise a
    ``DecodingError``; if that happened while building a failure detail it would
    turn an already-handled error into an unhandled 500.ÚtextrX   z<undecodable response body>N)ÚgetattrÚhttpxÚDecodingError)ÚresponseÚlimitr˜   s      rH   Ú_safe_response_textrž   ê   sB   € ð
-Ü˜h¨°Ó3ˆð ŠJ�B˜˜ÐÐøô ×Ñò -Ù,ð-ús   ‚ ˜.­.c                ón   — t        j                  | j                  dd«      «      j                  «       d d S )Núutf-8Úsurrogatepassé   )ÚhashlibÚsha256ÚencodeÚ	hexdigest)r˜   s    rH   Ú_content_hashr§   ö   s.   € ô �>‰>˜$Ÿ+™+ g¨Ó?Ó@×JÑJÓLÈSÈbÐQÐQr`   c                óB   — t        d„ | j                  «       D «       «      S )zPUTF-8 byte size of one recovery-store entry (surrogatepass, like _content_hash).c              3  óR   K  — | ]  }t        |j                  d d«      «      –— Œ! y­w)r    r¡   N)Úlenr¥   )rF   rd   s     rH   rI   z_entry_bytes.<locals>.<genexpr>þ   s    è ø€ Ò[¸uŒs�5—<‘< ¨Ó9×:Ñ[ùs   ‚%')ÚsumÚvalues)Ú	originalss    rH   Ú_entry_bytesr®   ü   s   € äÑ[È	×HXÑHXÓHZÔ[Ó[Ð[r`   c                ób   — dj                  d„ | D «       «      }t        |«      dk  r|S |dd › d�S )aD  Bound a model-supplied hash for logs/fallback text. A real marker hash is
    24 hex chars; a prompt-injected ``compresr_retrieve`` call could pass a huge
    or control-character-laden string, so strip non-printables (no forged log
    lines / ANSI escapes) and cap length before echoing into logs and the
    conversation.rX   c              3  óB   K  — | ]  }|j                  «       sŒ|–— Œ y ­wrC   )Úisprintable)rF   Úchs     rH   rI   z _display_hash.<locals>.<genexpr>  s   è ø€ ÒK b¸"¿.¹.Õ:JœrÑKùó   ‚˜é    Nu   â€¦)Újoinrª   )Ú
hash_valueÚ	printables     rH   Ú_display_hashr¸     s<   € ð —w‘wÑK¨JÔKÓK€IÜ˜I›¨"Ò,ˆ9ÐH°Y¸sÀ°^Ð4DÀCÐ2HÐHr`   c                ó   — d| › dt         › d�S )Nz

[compresr hash=zV: parts of this content were compressed away. If you need the full original, call the z tool with this hash.]©r(   )r¶   s    rH   Ú_recovery_markerr»     s!   € à
˜j˜\ð *9ä&Ð
'Ð'=ð	?ðr`   c                 ó0   — dt         ddddddœidgdœd	œd
œS )Nr�   z Retrieve the original, uncompressed content behind a Compresr compression marker. Call this when a compression marker's hash points at content you need in full.ÚobjectÚhashÚstringz6The 24-character hex hash from the compression marker.)ÚtypeÚdescription)rÀ   Ú
propertiesÚrequired)rp   rÁ   Ú
parameters)rÀ   r�   rº   © r`   rH   Ú_build_compresr_retrieve_toolrÆ     s@   € àä/ð6ð
 !àØ (Ø'_ñðð $˜Hñ	ñ
ñð r`   c                ó"   — t        | t        «      S rC   )r   r(   )Útoolss    rH   Úhas_compresr_retrieve_toolrÉ   +  s   € Ü˜eÔ%@ÓAÐAr`   c                ó�   — | �t        | t        «      syt        «       }| €|gS t        | «      rt        | «      S t        | «      |gz   S )z The request's tools plus the retrieve tool, or None when the incoming
    shape is not a list (leave the caller's tools untouched; markers stay
    inert text).N)rO   rh   rÆ   rÉ   )Úexisting_toolsÚretrieve_tools     rH   Ú_merge_retrieve_toolrÍ   /  sQ   € ð Ð!¬*°^ÄTÔ*JØÜ8Ó:€MØÐØˆÐÜ! .Ô1Ü�NÓ#Ð#Ü�Ó = /Ñ1Ð1r`   c           
     óÔ   — t        | «      D �cg c]O  }|j                  d«      t        k(  r5|j                  d«      d|j                  d«      |j                  di «      dœ‘ŒQ c}S c c}w )Nrp   r‡   r�   rq   )r‡   rÀ   rp   rq   )r   ru   r(   )rœ   r‘   s     rH   Ú_extract_compresr_tool_callsrÏ   =  sc   € ô /¨xÓ8öàØ�6‰6�&‹>Ô8Ò8ð �v‰v�d‹| Z¸¿¹À»ÐVX×V\ÑV\Ð]hÐjlÓVmÓnòð ùò s   ŽAA%c                óF   — t        | dd«      }t        |t        «      r|r|S y)u�  The call id from the framework logging object.

    This value ultimately derives from the client-settable ``x-litellm-call-id``
    header and is echoed back in responses, so it is NOT a trust boundary on its
    own â€” ``_scoped_store_key`` prefixes it with the caller's virtual-key hash to
    partition the recovery store per tenant. Request-body/kwargs call ids are
    deliberately not consulted here.
    Úlitellm_call_idN)r™   rO   rk   )Úlogging_objÚlogging_call_ids     rH   Ú_resolve_call_idrÔ   E  s(   € ô % [Ð2CÀTÓJ€OÜ�/¤3Ô'©OØÐØr`   c                ó|  — t        | dd«      }t        |«      sy|j                  d«      }||fD ]‹  }t        |«      sŒdD ]x  }|j                  |«      }t        |«      sŒ |j                  d«      }t        |t        «      sŒBt        |j
                  t        «      sŒ]|j
                  sŒj|j
                  c c S  Œ� y)aó  The caller's virtual-key hash, used to partition the recovery store.

    Trust is anchored on the ``UserAPIKeyAuth`` object the proxy sets
    server-side (``metadata.user_api_key_auth``, litellm_pre_call_utils). Its
    ``api_key`` is the hash of the authenticated key. Both metadata spellings
    are scanned (``/v1/messages`` and ``/v1/responses`` carry it under
    ``litellm_metadata``), but the bare ``user_api_key`` *string* is never
    trusted on its own: a JSON request body can place one in the client-supplied
    ``metadata`` field, which is only sanitized on the route's canonical
    container. Returns "" when the proxy runs without per-key auth, in which case
    all traffic is a single trust domain and the call id alone suffices.
    Úmodel_call_detailsNrX   Úlitellm_params©ÚmetadataÚlitellm_metadataÚuser_api_key_auth)r™   re   ru   rO   r   Úapi_keyrk   )rÒ   Údetailsr×   Ú	containerÚmeta_keyrÙ   Úauths          rH   Ú_caller_scoperá   T  s²   € ô ˜[Ð*>ÀÓE€GÜ˜wÔ'ØØ#ŸK™KÐ(8Ó9€NØ$ gÐ.ò 	$ˆ	Ü" 9Ô-ØØ8ò 	$ˆHØ —}‘} XÓ.ˆHÜ& xÔ0ØØ—<‘<Ð 3Ó4ˆDÜ˜$¤Õ/´J¸t¿|¹|ÌSÕ4QÐVZ×VbÓVbØ—|‘|Ô#ñ	$ð	$ð r`   c                óJ   — t        | «      }|€yt        | «      }|r|› d|› �S |S )aÕ  Key for the recovery store: caller identity plus framework call id.

    Keying on the call id alone is unsafe: it comes from the client-settable
    ``x-litellm-call-id`` header and is echoed back in responses, so one caller
    could read or evict another's originals by reusing the id. Prefixing the
    unforgeable virtual-key hash binds each entry to the tenant that created it.
    Returns None when there is no call id, which disables recovery for the call.
    Nú )rÔ   rá   )rÒ   Úcall_idÚscopes      rH   Ú_scoped_store_keyræ   r  s8   € ô & kÓ2€GØ€ØÜ  Ó-€EÙ&+ˆeˆW�D˜˜	Ð"Ð8°Ð8r`   c                ó8   — t        t        | dd «      t        «      S )NÚoutput©rO   r   rh   ©rœ   s    rH   Ú_is_responses_api_responserë   ‚  s   € ÜÔ*¨8°X¸tÓDÄdÓKÐKr`   c                ó8   — t        t        | dd «      t        «      S )Nrl   ré   rê   s    rH   Ú_is_anthropic_messages_responserí   †  s   € ÜÔ*¨8°YÀÓEÄtÓLÐLr`   c                óâ   — dt        | «      |D ��cg c]O  \  }}|j                  d«      d|j                  d«      t        j                  |j                  di «      «      dœdœ‘ŒQ c}}dœS c c}}w )	a´  Rebuild the chat-completions assistant turn for the retrieval follow-up.

    Only the ``compresr_retrieve`` calls are echoed, each answered by a tool
    result below. Other tool calls made in the same turn are omitted on purpose:
    the follow-up re-runs the model with the recovered content so it re-plans
    them. Echoing them would leave tool_calls with no matching tool result and
    the provider would reject the request.
    r…   r‡   r�   rp   rq   )rp   rq   )r‡   rÀ   r�   )r~   rl   r†   )r   ru   rw   rx   )rœ   Ú	retrievedÚ	tool_callÚ_s       rH   Ú&_build_assistant_message_from_responserò   Š  su   € ð Ü/°Ó9ð !*÷

ñ �	˜1ð  —m‘m DÓ)Ø"à%ŸM™M¨&Ó1Ü!%§¡¨I¯M©M¸+ÀrÓ,JÓ!Kñóó

ñð ùó

s   ‘AA+
c           	     óì   — g }t        | «      }|r|j                  d|dœ«       |j                  d„ |D «       «       d|dœ}d|D ��cg c]  \  }}d|j                  d«      |d	œ‘Œ c}}dœ}||gS c c}}w )
a  Anthropic requires the tool_use block echoed back in an assistant
    message paired with a tool_result block keyed by the same tool_use_id. The
    assistant text is preserved; non-retrieve tool calls are re-planned by the
    follow-up (see _build_assistant_message_from_response).r˜   )rÀ   r˜   c              3  óŒ   K  — | ]<  \  }}d |j                  d«      |j                  d«      |j                  di «      dœ–— Œ> y­w)Útool_user‡   rp   rq   )rÀ   r‡   rp   ÚinputN)ru   )rF   rð   rñ   s      rH   rI   z5_build_anthropic_followup_messages.<locals>.<genexpr>³  sI   è ø€ ò ñ ˆI�qð Ø—-‘- Ó%Ø—M‘M &Ó)Ø—]‘] ;°Ó3õ		
ñùs   ‚AAr…   ©r~   rl   ÚuserÚtool_resultr‡   )rÀ   Útool_use_idrl   )r   ÚappendÚextendru   )rœ   rï   Úassistant_contentr˜   Úassistant_messagerð   rl   Úuser_messages           rH   Ú"_build_anthropic_followup_messagesr   §  sŸ   € ð 9;ÐÜ.¨xÓ8€DÙØ× Ñ ¨&¸$Ñ!?Ô@Ø×Ññ ð &ôô ð <GÐSdÑ2eÐàð '0÷
á"�	˜7ð #°9·=±=ÀÓ3FÐSZÓ[ó
ñ.€Lð ˜|Ð,Ð,ùó
s   ÁA0c                ó<  — g }t        | «      }|r|j                  d|dœ«       |D ]s  \  }}|j                  d«      }|j                  d||j                  d«      t        j                  |j                  di «      «      dœ«       |j                  d||d	œ«       Œu |S )
zàThe Responses API requires the model's function_call echoed back paired
    with a function_call_output keyed by the same call_id. The assistant text is
    preserved; non-retrieve tool calls are re-planned by the follow-up.r…   r÷   r‡   rˆ   rp   rq   )rÀ   rä   rp   rq   Úfunction_call_output)rÀ   rä   rè   )r   rû   ru   rw   rx   )rœ   rï   Úitemsr˜   rð   rl   rä   s          rH   Ú_build_responses_followup_itemsr  Ç  s£   € ð -/€EÜ.¨xÓ8€DÙØ�‰˜k°dÑ;Ô<Ø'ò 
^Ñˆ	�7Ø—-‘- Ó%ˆØ�‰à'Ø"Ø!Ÿ™ fÓ-Ü!ŸZ™Z¨	¯©°kÀ2Ó(FÓGñ	ô	
ð 	�‰Ð4ÀÐT[Ñ\Õ]ð
^ð €Lr`   c                  ó¶   — e Zd ZU dZded<    ee¬«      Zded<    ee¬«      Zded<    ee¬«      Z	ded	<    ee
¬«      Zd
ed<   dZded<   dZded<   dZded<   y)Ú_CompressionResultz:Outcome of applying compression results to a message list.úlist[dict[str, object]]Úcompressed_messages)Údefault_factoryúdict[str, str]r­   Útext_replacementszdict[str, int]Úreplaced_text_countszset[str]Úambiguous_textsr   rM   Úmessages_compressedÚtokens_beforeÚtokens_afterN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__r   rb   r­   r  r  Úsetr  r  r  r  rÅ   r`   rH   r  r  à  sj   … áDà0Ó0Ù %°dÔ ;€Iˆ~Ó;ñ ).¸dÔ(CÐ�~ÓCÙ+0ÀÔ+FÐ˜.ÓFÙ %°cÔ :€O�XÓ:Ø Ð˜Ó Ø€M�3ÓØ€L�#Ôr`   r  c                  ó  ‡ — e Zd Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dˆ fd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Z	dd„Z
dd	„Zdd
„Z	 	 	 	 	 	 d d„Z	 	 	 	 	 	 d!d„Zd"d„Ze	 	 	 	 d#d„«       Z	 d$	 	 	 	 	 	 	 	 	 	 	 	 	 d%d„Zed&d„«       Ze	 d$	 	 	 	 	 	 	 	 	 d'd„«       Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d(d„Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d)d„Zd*d„Zed+d„«       Zˆ xZS ),ÚCompresrGuardrailc                ó&  •— |xs t        d«      xs t        j                  d«      }t        |«      | _        |xs t        d«      | _        | j
                  st        d«      ‚|xs t        | _        |€t        n|| _
        |€dn|| _        |€t        n|| _        |€dn|| _        |€dn|| _        |	€dn|	| _        |
€dn|
| _        |€dn|| _        |dk(  rdnd| _        |€t(        n|| _        | j*                  d	k  rt        d
«      ‚|€dn|| _        |€dn|| _        || _        || _        t4        j7                  |xs i «      }|rt9        j:                  dt=        |«      «       |xs i j?                  «       D ��ci c]  \  }}|t4        vsŒ||“Œ c}}| _         tC        tD        jF                  ¬«      | _$        tK        «       | _&        d	| _'        d| _(        | j$                  rt9        j:                  d«       tR        ‰| �©  |||¬«       y c c}}w )NÚCOMPRESR_API_BASEú/ÚCOMPRESR_API_KEYznCompresr guardrail requires an API key. Set `api_key` in the guardrail config or the COMPRESR_API_KEY env var.TFÚ	fail_openÚfail_closedr   zPmax_bytes_per_call must be >= 0 (0 disables the cap; positive values enforce it)z6Compresr: ignoring reserved compression_params keys %s)Úllm_providerç        z–Compresr: enable_retrieval is on; the recovery store is per-process. For multi-worker deployments, set enable_retrieval=false or run with --workers 1.)Úguardrail_nameÚ
event_hookÚ
default_on)+r   r)   Úrstripr_   Úcompresr_api_baseÚcompresr_api_keyrL   r+   Úcompression_modelr,   Útarget_compression_ratioÚcoarser.   Úmin_chars_to_compressÚcompress_tool_outputsÚcompress_systemÚcompress_historyÚcompress_last_userÚenable_retrievalÚunreachable_fallbackr3   Úmax_bytes_per_callÚallow_bypass_headerÚdynamicÚdynamic_min_ratioÚdynamic_max_ratior<   Úintersectionr   ÚwarningÚsortedr  Úcompression_paramsr   r   ÚGuardrailCallbackÚasync_handlerr   Ú_originals_by_call_idÚ_store_total_bytesÚ_no_scope_warning_expiryÚsuperÚ__init__)ÚselfÚapi_baserÜ   Úmodelr(  r)  r*  r+  r,  r-  r.  r/  r!  r"  r#  r0  r1  r2  r3  r4  r5  r9  Úraw_api_baseÚreserved_keysÚkÚvÚ	__class__s                             €rH   r@  zCompresrGuardrail.__init__ñ  sT  ø€ ð0  (Òb¬>Ð:MÓ+NÒbÔRb×jÑjÐknÓoˆÜ!3°LÓ!AˆÔØ 'Ò M¬>Ð:LÓ+MˆÔØ×$Ò$ÜðDóð ð "'Ò!CÔ*CˆÔà0HÐ0PÕ,ÐVnð 	Ô%ð %˜n‘d°&ˆŒà-BÐ-JÕ)ÐPeð 	Ô"ð .CÐ-J¡TÐPeˆÔ"Ø(7Ð(?™uÀ_ˆÔØ)9Ð)A¡ÐGWˆÔØ+=Ð+E¡%ÐK]ˆÔØ(8Ð(@¡ÐFVˆÔà/°;Ò>‰KÀMð 	Ô!ð BTÐA[Õ"=ÐasˆÔØ×"Ñ" QÒ&ÜÐoÓpÐpØ,?Ð,G¡5ÐM`ˆÔ ð  '˜‘t°GˆŒØ!2ˆÔØ!2ˆÔô  @×LÑLÐM_ÒMeÐceÓfˆÙÜ ×(Ñ(ØHÌ&ÐQ^ÓJ_ôð 1Ò6°B×=Ñ=Ó?÷6
Ù�Q˜À1ÔLlÒClˆAˆq‰Dó6
ˆÔô 4Ü-×?Ñ?ô
ˆÔô VaÓUbˆÔ"à"#ˆÔð ),ˆÔ%Ø× Ò Ü ×(Ñ(ðdôô 	‰ÑØ)Ø!Ø!ð 	õ 	
ùó#6
s   Å>HÆHc                óî   — | j                   sy|j                  d«      }t        |«      sy|j                  d«      }t        |«      syt        |j                  t        «      «      j                  «       dk(  S )NFÚproxy_server_requestÚheadersÚtrue)r2  ru   re   rk   r&   r[   )rA  Úrequest_dataÚpsrrK  s       rH   Ú_should_bypassz CompresrGuardrail._should_bypassI  se   € Ø×'Ò'ØØ!×%Ñ%Ð&<Ó=ˆÜ" 3Ô'ØØŸ™ Ó+ˆÜ" 7Ô+ØÜ�7—;‘;œ}Ó-Ó.×4Ñ4Ó6¸&Ñ@Ð@r`   c                ó(   — d| j                   xs ddœS )Nzapplication/jsonrX   )zContent-Typez	X-API-Key)r&  )rA  s    rH   Ú_request_headersz"CompresrGuardrail._request_headersT  s   € à.Ø×.Ñ.Ò4°"ñ
ð 	
r`   c                óœ   — | j                   dk(  rt        j                  d||«       yt        j                  d||«       t	        dd|i¬«      ‚)a\  fail_open logs and returns (caller forwards uncompressed);
        fail_closed raises. ``log_detail`` may include upstream response bodies
        and is written only to server logs; the raised ``HTTPException`` carries
        a generic message so a malicious ``api_base`` cannot exfiltrate response
        bytes through the client-visible error.r  zNCompresr: %s; fail_open configured, forwarding request uncompressed. detail=%sNzCompresr: %s. detail=%siö  Úerror)Ústatus_codeÚdetail)r0  r   r7  rS  r   )rA  rS  Ú
log_details      rH   Ú_handle_compress_failurez*CompresrGuardrail._handle_compress_failureZ  sS   € ð ×$Ñ$¨Ò3Ü ×(Ñ(Ø`ØØôð
 Ü×"Ñ"Ð#<¸eÀZÔPÜ¨°W¸eÐ4DÔEÐEr`   c                ó„   — | j                   j                  d¬«      \  }\  }}| xj                  t        |«      z  c_        y)zCDrop the front (oldest) entry and decrement the running byte total.F)ÚlastN)r<  Úpopitemr=  r®   )rA  Ú_keyÚevictedÚ_expirys       rH   Ú_evict_oldestzCompresrGuardrail._evict_oldestj  s;   € à#'×#=Ñ#=×#EÑ#EÈ5Ð#EÓ#QÑ ˆÑ ˆw˜Ø×Ò¤<°Ó#8Ñ8Ör`   c                óø  — t        j                  «       }| j                  }|rM|t        t	        |«      «         d   |k  r0| j                  «        |r|t        t	        |«      «         d   |k  rŒ0t        |«      t        kD  r#| j                  «        t        |«      t        kD  rŒ#t        |«      dkD  rH| j                  t        kD  r4| j                  «        t        |«      dkD  r| j                  t        kD  rŒ2y y y y )Nrƒ   )
ÚtimeÚ	monotonicr<  ÚnextÚiterr^  rª   r2   r=  r4   )rA  ÚnowÚstores      rH   Ú_prune_originalsz"CompresrGuardrail._prune_originalso  sÒ   € ä—^‘^Ó%ˆØ×1Ñ1ˆÙ˜œd¤4¨£;Ó/Ñ0°Ñ3°sÒ:Ø×ÑÔ ñ ˜œd¤4¨£;Ó/Ñ0°Ñ3°sÓ:ä�%‹jÔ-Ò-Ø×ÑÔ ô �%‹jÔ-Ó-ô �%‹j˜1Šn ×!8Ñ!8Ô;QÒ!QØ×ÑÔ ô �%‹j˜1Šn ×!8Ñ!8Ô;QÔ!QˆnÐ!Qˆnr`   c                óL   — |€i S | j                   j                  |i df«      d   S )z‡Originals already stored under this key, so the per-call byte budget
        can account for an earlier turn that reused the store key.r   r   )r<  ru   )rA  Ú	store_keys     rH   Ú_existing_originalsz%CompresrGuardrail._existing_originals|  s1   € ð ÐØˆIØ×)Ñ)×-Ñ-¨i¸"¸c¸ÓCÀAÑFÐFr`   c                ón  — | j                   j                  |i df«      \  }}| j                  i |¥|¥«      }| xj                  t	        |«      t	        |«      z
  z  c_        |t        j                  «       t        z   f| j                   |<   | j                   j                  |«       | j                  «        y )Nr   )
r<  ru   Ú_bound_call_bytesr=  r®   r`  ra  r/   Úmove_to_endrf  )rA  rh  r­   Úexistingrñ   Úmergeds         rH   Ú_store_originalsz"CompresrGuardrail._store_originalsƒ  s¤   € Ø×0Ñ0×4Ñ4°YÀÀSÀ	ÓJ‰ˆ�!Ø×.Ñ.Ð/H°(Ð/H¸iÐ/HÓIˆà×Ò¤<°Ó#7¼,ÀxÓ:PÑ#PÑPÕàÜ�N‰NÓÔ5Ñ5ð1
ˆ×"Ñ" 9Ñ-ð 	×"Ñ"×.Ñ.¨yÔ9Ø×ÑÕr`   c                óJ  — | j                   dk  r|S t        |«      }|| j                   k  r|S t        |«      }t        |j	                  «       «      D ]N  }|| j                   k  r |S |t        ||   j                  dd«      «      z  }||= t        j                  d|«       ŒP |S )zÒDrop oldest entries (dict insertion order) until the aggregate byte
        size fits ``self.max_bytes_per_call``. Prevents one call with many
        large tool outputs from growing proxy memory without bound.r   r    r¡   z7Compresr: originals-store byte cap hit, evicted hash=%s)	r1  r®   rb   rh   Úkeysrª   r¥   r   r7  )rA  rn  ÚtotalÚboundedÚkeys        rH   rk  z#CompresrGuardrail._bound_call_bytes�  s³   € ð ×"Ñ" aÒ'ØˆMÜ˜VÓ$ˆØ�D×+Ñ+Ò+ØˆMÜ˜f›ˆÜ˜Ÿ™›Ó'ò 	iˆCØ˜×/Ñ/Ò/Øð ˆð ”S˜ ™×,Ñ,¨W°oÓFÓGÑGˆEØ˜�Ü ×(Ñ(Ð)bÐdgÕhð	ið ˆr`   c                óÈ   — |r`| j                   j                  |i df«      \  }}|t        j                  «       kD  r	||v r||   S t	        j
                  dt        |«      «       y)z…Stored original for a marker hash, or None if not issued for this
        request (unknown, expired, or from another caller's scope).r   zNCompresr retrieve: rejecting hash=%s (not issued for this request, or expired)N)r<  ru   r`  ra  r   r7  r¸   )rA  rh  r¶   r­   Úexpirys        rH   Ú_retrieve_originalz$CompresrGuardrail._retrieve_original¡  sg   € ñ Ø $× :Ñ :× >Ñ >¸yÈ2ÈsÈ)Ó TÑˆI�vØœŸ™Ó(Ò(¨Z¸9Ñ-DØ  Ñ,Ð,Ü ×(Ñ(Ø`Ü˜jÓ)ôð r`   c                óÚ  — g }t        «       }d}t        |«      D ]Ì  \  }}|j                  di «      }t        |t        «      rt        |j                  dd«      «      nd}	|t        k\  rd}
n@|	|v rd}
n9| j                  ||	«      }|€dt        |	«      › d�}
n|j                  |	«       d	}|}
t        j                  d
t        |	«      t        |
«      «       |j                  ||
f«       ŒÎ ||fS )zêResolve compresr_retrieve calls to (call, result_text) pairs, deduping
        repeated hashes and capping the count so the follow-up cannot be amplified.
        The bool is True iff at least one call resolved to real stored content.Frq   r¾   rX   z1[compresr: retrieval limit reached for this turn]z1[compresr: already retrieved above for this hash]z[compresr: hash=z4 not found, expired, or not issued for this request]Tz&Compresr retrieve: hash=%s -> %d chars)r  Ú	enumerateru   rO   rb   rk   r6   rw  r¸   Úaddr   Údebugrª   rû   )rA  rh  r†   rï   ÚseenÚresolved_anyÚidxr‘   rq   r¶   Úresultrl   s               rH   Ú_resolve_retrievalsz%CompresrGuardrail._resolve_retrievals®  sþ   € ð ACˆ	Ü #£ˆØˆÜ  Ó,ò 	+‰GˆC�ØŸ™˜{¨BÓ/ˆIÜ;EÀiÔQUÔ;Vœ˜YŸ]™]¨6°2Ó6Ô7Ð\^ˆJØÔ.Ò.ØL‘Ø˜tÑ#ØL‘à×1Ñ1°)¸ZÓH�Ø�?Ø/´¸jÓ0IÐ/JÐJ~Ð‘Fà—H‘H˜ZÔ(Ø#'�LØ$�FÜ ×&Ñ&Ð'OÔQ^Ð_iÓQjÔloÐpvÓlwÔxØ×Ñ˜b &˜\Õ*ð!	+ð" ˜,Ð&Ð&r`   c           	   ƒ  óv  K  — i | j                   ¥| j                  | j                  | j                  | j                  t
        dœ¥}| j                  �| j                  |d<   | j                  �| j                  |d<   t        |«      dk(  r| j                  › d�}|d   |d   dœ|¥}n5| j                  › d	�}d
t        ||«      D ��cg c]
  \  }}||dœ‘Œ c}}i|¥}	 | j                  j                  ||| j                  «       t        ¬«      ƒ d{  –—† }|�d|j4                  cxk  rdk  s,n | j)                  dt'        |dd«      t+        |«      dœ«       y	 |j7                  «       }t?        |«      rt?        |jA                  d«      «      s| j)                  ddt+        |«      i«       y|d   }t        |«      dk(  r|gS |jA                  d«      }tC        |«      r)t        |«      t        |«      k7  stE        d„ |D «       «      s5| j)                  dt        |«      tC        |«      rt        |«      nddœ«       y|S c c}}w 7 �Œ4# t        j                   $ r ‚ t"        j$                  $ r@}	t'        |	dd«      }
| j)                  dt'        |
dd«      t+        |
«      dœ«       Y d}	~	yd}	~	wt"        j,                  t.        j0                  f$ r'}	| j)                  ddt3        |	«      i«       Y d}	~	yd}	~	ww xY w# t8        t"        j:                  t<        f$ r  | j)                  ddt+        |«      i«       Y yw xY w­w)zˆCompress ``contexts`` (query-aware). Returns one result dict per
        context, or None when the service failed and fail_open applies.)Úcompression_model_namer(  r)  r3  ÚsourceNr4  r5  rƒ   z /api/compress/question-specific/r   )r;   r9   z%/api/compress/question-specific/batchr:   )r\   rw   rK  Útimeoutrœ   z.Compresr compression service returned an errorrT  )rT  Úbodyz+Compresr compression service request failedrU  éÈ   i,  z<Compresr compression service returned an unreadable responser…  Údataz?Compresr compression service returned unexpected response shapeÚresultsc              3  ó2   K  — | ]  }t        |«      –— Œ y ­wrC   )re   )rF   Úrs     rH   rI   z3CompresrGuardrail._call_compress.<locals>.<genexpr>/  s   è ø€ Ò?°!Ô*¨1×-Ñ?ùs   ‚z7Compresr batch response missing or mismatched 'results')ÚexpectedÚgot)#r9  r'  r(  r)  r3  r8   r4  r5  rª   r%  Úzipr;  ÚpostrQ  r7   ÚasyncioÚCancelledErrorrš   ÚHTTPStatusErrorr™   rW  rž   ÚRequestErrorÚlitellmÚTimeoutrk   rT  rw   rL   r›   ÚRecursionErrorre   ru   ri   Úall)rA  ÚcontextsÚqueriesÚcommonr\   ÚpayloadÚctxÚqÚraw_responseÚeÚrespr…  r‡  rˆ  s                 rH   Ú_call_compressz CompresrGuardrail._call_compressÊ  sV  è ø€ ð,
à×%Ñ%ð,
ð '+×&<Ñ&<Ø(,×(EÑ(EØ—k‘kØ—|‘|Ü!ò,
ˆð ×!Ñ!Ð-Ø*.×*@Ñ*@ˆFÐ&Ñ'Ø×!Ñ!Ð-Ø*.×*@Ñ*@ˆFÐ&Ñ'Üˆx‹=˜AÒØ×+Ñ+Ð,Ð,LÐMˆCà# A™;Ø  ™ñ*ð ð*‰Gð ×+Ñ+Ð,Ð,QÐRˆCàÄcÈ(ÐT[ÓF\×]¹F¸CÀ s°QÓ7Ó]ðàðˆGð
	Ø7;×7IÑ7I×7NÑ7NØØØ×-Ñ-Ó/Ü1ð	 8Oó 8÷ 2ˆLð< Ð s¨l×.FÑ.FÔ'LÈÔ'LØ×)Ñ)Ø@ä#*¨<¸ÈÓ#MÜ/°Ó=ñôð ð		Ø".×"3Ñ"3Ó"5ˆDô # 4Ô(Ô0CÀDÇHÁHÈVÓDTÔ0UØ×)Ñ)ØQØÔ,¨\Ó:Ð;ôð Ø"& v¡,ˆäˆx‹=˜AÒØ�6ˆMØŸ™ )Ó,ˆä Ô(Ü�7‹|œs 8›}Ò,ÜÑ?°wÔ?Ô?ð ×)Ñ)ØIÜ  ›]Ä?ÐSZÔC[´3°w´<ÐaeÑfôð Øˆùó] ^ð
2úô ×%Ñ%ò 	ØÜ×$Ñ$ò 	ô " ! Z°Ó6ˆDØ×)Ñ)Ø@ä#*¨4°ÀÓ#EÜ/°Ó5ñôô ûÜ×"Ñ"¤G§O¡OÐ4ò 	ð ×)Ñ)Ø=Øœ3˜q›6Ð"ôô ûð	ûô* œE×/Ñ/´Ð@ò 	ð ×)Ñ)ØNØÔ,¨\Ó:Ð;ôñ ð	üs†   ‚CL9ÃH>ÃL9Ã4I ÄIÄI ÄAL9ÅK8 Å)CL9ÉI É&K5É-6J(Ê#L9Ê(&K5ËK0Ë+L9Ë0K5Ë5L9Ë8;L6Ì3L9Ì5L6Ì6L9c                ó¢  — g }t        |«      D ]¾  \  }}||k(  r| j                  sŒ|j                  d«      }|dv r| j                  s+Œ:|dk(  r| j                  sŒL|dk(  r||k7  r| j
                  sŒcŒd|j                  d«      }t        |«      rt        |«      sŒŒt        t        |«      «      | j                  k  rŒ®|j                  |«       ŒÀ |S )z<Indices of messages whose text content should be compressed.r~   r   Úsystemrø   rl   )ry  r.  ru   r+  r,  r-  ri   r   rª   r   r*  rû   )rA  rŠ   Ú	query_idxÚtargetsr~  r�   r~   rl   s           rH   Ú_select_targetsz!CompresrGuardrail._select_targets:  sÔ   € à$&ˆÜ! (Ó+ò 	 ‰HˆC�Ø�iÒ¨×(?Ò(?ØØ—7‘7˜6“?ˆDØÐ+Ñ+Ø×1Ò1ØØ˜Ò!Ø×+Ò+ØØ˜’Ø˜)Ò#¨D×,AÒ,AØàØ—g‘g˜iÓ(ˆGÜ˜wÔ'Ô0AÀ'Ô0JØÜ”? 7Ó+Ó,¨t×/IÑ/IÒIØØ�N‰N˜3Õð)	 ð* ˆr`   c                ó°   — t        t        | «      dz
  dd«      D ]:  }| |   j                  d«      dk(  sŒt        | |   j                  d«      «      |fc S  y)Nrƒ   r„   r~   rø   rl   )rX   N)r‰   rª   ru   r   )rŠ   r~  s     rH   Ú_extract_fallback_queryz)CompresrGuardrail._extract_fallback_queryT  sb   € ô œ˜X›¨Ñ*¨B°Ó3ò 	JˆCØ˜‰}× Ñ  Ó(¨FÓ2Ü& x°¡}×'8Ñ'8¸Ó'CÓDÀcÐIÒIð	Jð r`   c                ó  — t        t        |«      ¬«      }|xs i }| j                  }	t        |«      }
t	        |||«      D �]Ä  \  }}}|j                  d«      }t        |t        «      r|r||k(  rŒ1|xj                  dz  c_        |rmt        |«      }||v xs ||j                  v }|rdnt        |j                  dd«      «      }|	dk  s|
|z   |	k  r"|
|z  }
||j                  |<   |t        |«      z  }|j                  j                  |«      }|�!||k7  r|j                  j!                  |«       n||j                  |<   |j"                  j                  |d«      dz   |j"                  |<   |j$                  |   }i |¥dt'        |j                  d«      |«      i¥|j$                  |<   |xj(                  t+        |j                  d«      «      z  c_        |xj,                  t+        |j                  d	«      «      z  c_        �ŒÇ |S )
a#  Write each compression result into a copy of ``messages``.

        A result is a real compression only when it is a non-empty string that
        differs from the original; identical text is treated as a no-op so an
        untouched request is not needlessly rewritten downstream.
        )r  Úcompressed_contextrƒ   r   r    r¡   rl   Úoriginal_tokensÚcompressed_tokens)r  rh   r1  r®   r�  ru   rO   rk   r  r§   r­   rª   r¥   r»   r  r  rz  r  r  rn   r  r–   r  )rA  rŠ   r¤  r—  rˆ  Úrecovery_enabledÚexisting_originalsÚoutrm  ÚcapÚrecovery_bytesr‹   Úoriginal_textr  Úcompressed_textr¶   Úalready_storedÚ	new_bytesÚpreviousÚoriginal_msgs                       rH   Ú_apply_compression_resultsz,CompresrGuardrail._apply_compression_results]  s  € ô (¼DÀ»NÔKˆØ,Ò2°ˆØ×,Ñ,ˆô & hÓ/ˆÜ14°W¸hÈÓ1Pó 	KÑ-ˆJ˜ vØ$Ÿj™jÐ)=Ó>ˆOÜ˜o¬sÔ3¹?ÈoÐanÒNnØØ×#Ò# qÑ(Õ#ÙÜ*¨=Ó9�
Ø!+¨xÐ!7Ò!V¸:ÈÏÉÐ;V�Ù!/™A´S¸×9MÑ9MÈgÐWfÓ9gÓ5h�	Ø˜!’8˜~°	Ñ9¸SÒ@Ø" iÑ/�NØ0=�C—M‘M *Ñ-Ø#Ô'7¸
Ó'CÑC�OØ×,Ñ,×0Ñ0°Ó?ˆHØÐ#¨°OÒ(Cð ×#Ñ#×'Ñ'¨Õ6à7F�×%Ñ% mÑ4Ø69×6NÑ6N×6RÑ6RÐS`ÐbcÓ6dÐghÑ6hˆC×$Ñ$ ]Ñ3Ø×2Ñ2°:Ñ>ˆLð3Øð3àÔ3°L×4DÑ4DÀYÓ4OÐQ`Óañ3ˆC×#Ñ# JÑ/ð ×Ò¤¨6¯:©:Ð6GÓ+HÓ!IÑIÕØ×Ò¤	¨&¯*©*Ð5HÓ*IÓ JÑJ×ð7	Kð8 ˆ
r`   c                ó°  — |j                   rt        | t        «      syt        d„ | D «       «      }|j                   j	                  «       D ��ci c]C  \  }}||j
                  vr0|j                  |«      |j                  j                  |«      k(  r||“ŒE }}}|sy| D �cg c]&  }t        |t        «      r|j                  ||«      n|‘Œ( c}S c c}}w c c}w )af  Compressed content mirrored into the Responses `texts` channel.

        The chat/Anthropic handlers round-trip ``structured_messages``; the
        Responses translation cannot rebuild its input from chat messages and
        instead writes back through ``texts``. This matches by value, so a
        replacement is applied only when it is unambiguous: one compression per
        text, and every occurrence in ``texts`` accounted for by a compressed
        target. Anything else is left uncompressed rather than risk a wrong or
        out-of-policy replacement. Returns None when nothing safe applies.
        Nc              3  óB   K  — | ]  }t        |t        «      sŒ|–— Œ y ­wrC   )rO   rk   )rF   r˜   s     rH   rI   z:CompresrGuardrail._mirror_texts_channel.<locals>.<genexpr>¡  s   è ø€ ÒV¨Ä
È4ÔQTÕ@U¤ÑVùr³   )	r  rO   rh   r   r  r  ru   r  rk   )Úinput_textsÚappliedÚcountsr˜   ÚreplacementÚsafes         rH   Ú_mirror_texts_channelz'CompresrGuardrail._mirror_texts_channel“  sÍ   € ð ×(Ò(´
¸;ÌÔ0MØÜÑV°ÔVÓVˆð &-×%>Ñ%>×%DÑ%DÓ%F÷
á!��kØ˜7×2Ñ2Ñ2°v·z±zÀ$Ó7GÈ7×KgÑKg×KkÑKkÐlpÓKqÒ7qð �+Ñð
ˆñ 
ñ
 ØØT_Ö`ÈD¬
°4¼Ô(=�—‘˜˜tÔ$À4ÑGÒ`Ð`ùó
ùò as   ÁACÂ+Cc           
   ƒ  ó(  K  — |dk7  r|S | j                  |«      rt        j                  dt        «       |S |j	                  d«      }t        |«      r|s|S |D �cg c]  }t        |«      sŒ|‘Œ }}t        |«      t        |«      k7  r|S | j                  |«      \  }}	g }
g }| j                  ||	«      D ]B  }t        |||«      }|j                  «       sŒ!|
j                  |«       |j                  |«       ŒD |
st        j                  d«       |S |
D �cg c]  }t        ||   j	                  d«      «      ‘Œ! }}t        j                  «       }| j!                  ||¬«      ƒ d {  –—† }t        j                  «       }|€|S t#        |«      }t%        |«      }| j&                  xr |d uxr t)        |«      }| j&                  rX|sVt        j                  «       | j*                  k\  r5t        j                  «       t,        z   | _        t        j.                  d«       | j1                  |«      }| j3                  ||
||||«      }|j4                  dk(  rt        j                  d	«       |S |j4                  |j6                  |j8                  |j6                  |j8                  z
  | j:                  d
œ}t        j                  d|j4                  |j6                  |j8                  «       | j=                  ||dd||||z
  ¬«       i |¥d|j>                  i¥}| jA                  |j	                  d«      |«      }|�||d<   |jB                  }|r|r|€|S | jE                  ||«       tG        |j	                  d«      «      }|�||d<   |S c c}w c c}w 7 �Œ(­w)NÚrequestz-Compresr: %s header set; skipping compressionÚstructured_messagesz.Compresr: no messages eligible for compressionrl   )r—  r˜  z¿Compresr: enable_retrieval is on but this request has no per-key auth scope; compressing without recovery (compresr_retrieve tool not injected). Configure virtual-key auth to enable recovery.r   zCCompresr: service returned no compressed content; request unchanged)r  r  r  Útokens_savedr'  z3Compresr: compressed %s message(s), %s -> %s tokensÚsuccessÚcompresr)Úguardrail_json_responserM  Úguardrail_statusÚguardrail_providerÚ
start_timeÚend_timeÚdurationÚtextsrÈ   )$rO  r   r{  r&   ru   ri   re   rª   r§  r¥  r”   rv   rû   r   r`  ra  r   ræ   rá   r/  Úboolr>  r0   r7  ri  r·  r  r  r  r'  Ú:add_standard_logging_guardrail_information_to_request_datar  r¿  r­   ro  rÍ   )rA  r:   rM  Ú
input_typerÒ   rÂ  ÚmrŠ   Úfallback_queryr£  r¤  r˜  r~  r9   r—  rÉ  rˆ  rÊ  rh  rå   r¬  r­  r»  ÚstatsÚcompressed_inputsÚmirrored_textsr­   Úmerged_toolss                               rH   Úapply_guardrailz!CompresrGuardrail.apply_guardrail«  s¢  è ø€ ð ˜Ò"ØˆMà×Ñ˜|Ô,Ü ×&Ñ&Ð'VÔXeÔfØˆMà%+§Z¡ZÐ0EÓ%FÐÜÐ2Ô3Ñ;NØˆMØ&9ÖT Ô=PÐQRÕ=Sš1ÐTˆÐTÜˆx‹=œCÐ 3Ó4Ò4ØˆMà$(×$@Ñ$@ÀÓ$JÑ!ˆ˜	Ø$&ˆØ$&ˆØ×'Ñ'¨°)Ó<ò 	"ˆCÜ% h°°^ÓDˆEð —;‘;”=ØØ�N‰N˜3ÔØ�N‰N˜5Õ!ð	"ñ Ü ×&Ñ&Ð'WÔXØˆMàT[Ö\ÈSœ?¨8°C©=×+<Ñ+<¸YÓ+GÕHÐ\ˆÐ\ä ŸN™NÓ,ˆ
Ø#×2Ñ2¸HÈgÐ2ÓV×VˆÜŸ.™.Ó*ˆØˆ?ØˆMô -¨[Ó9ˆ	Ü$ [Ó1ˆØ"&×"7Ñ"7Ò"a¸IÈTÐ<QÒ"aÔVZÐ[`ÓVaÐØ× Ò ©´4·>±>Ó3CÀt×GdÑGdÒ3dô -1¯N©NÓ,<Ô?aÑ,aˆDÔ)Ü ×(Ñ(ðAôð %)×$<Ñ$<¸YÓ$GÐØ×8Ñ8Ø�g˜x¨Ð2BÐDVó
ˆð ×&Ñ&¨!Ò+ô !×&Ñ&Ð'lÔmØˆMð $+×#>Ñ#>Ø$×2Ñ2Ø#×0Ñ0Ø#×1Ñ1°G×4HÑ4HÑHØ!%×!7Ñ!7ñ+
ˆô 	×"Ñ"ØAØ×'Ñ'Ø×!Ñ!Ø× Ñ ô		
ð 	×GÑGØ$)Ø%Ø&Ø)Ø!ØØ 
Ñ*ð 	Hô 	
ð 7u¸Ð6tÐAVÐX_×XsÑXsÑ6tÐØ $× :Ñ :¸6¿:¹:ÀgÓ;NÐPWÓ XˆØÐ%Ø)7Ð˜gÑ&à"×,Ñ,ˆ	Ù¡y°IÐ4EØ$Ð$à×Ñ˜i¨Ô3ä2°6·:±:¸gÓ3FÓGˆØÐ#Ø)5Ð˜gÑ&Ø Ð ùòu Uùò& ]ð Wûs2   ‚ANÁNÁ,NÁ0B'NÄ$N
Ä;,NÅ'NÅ(H(Nc              ƒ  óV   K  — t        |«      sdi fS t        |«      }|sdi fS dd|ifS ­w)NFTr†   )rÉ   rÏ   )	rA  rœ   rC  rŠ   rÈ   ÚstreamÚcustom_llm_providerÚkwargsr†   s	            rH   Úasync_should_run_agentic_loopz/CompresrGuardrail.async_should_run_agentic_loop  sA   è ø€ ô *¨%Ô0Ø˜"�9ÐÜ8¸ÓBˆ
ÙØ˜"�9ÐØ�l JÐ/Ð/Ð/ùs   ‚')c
              ƒ  ób  K  — |j                  dg «      }
| j                  «        t        |«      }| j                  ||
«      \  }}|st	        d¬«      S t        |«      rt        |«      t        ||«      z   }nit        |«      rt        |«      t        ||«      z   }nEt        ||«      }|D ��cg c]  \  }}d|j                  d«      |dœ‘Œ }}}t        |«      |gz   |z   }|j                  d«      }|�|n|	j                  d«      }|j                  «       D ��ci c]  \  }}|dk7  sŒ||“Œ }}}|}|�Ct        |di «      j                  d	i «      }|j                  d
|«      }t        |t        «      r|r|}t	        dt        ||||| j!                  |	«      ¬«      ddi¬«      S c c}}w c c}}w ­w)Nr†   F)Úrun_agentic_loopr€   r‡   )r~   r‚   rl   Ú
max_tokensrÖ   Úagentic_loop_paramsrC  T)rC  rŠ   rÞ  Úoptional_paramsrÚ  Ú	tool_typer'   )rÝ  Úrequest_patchrÙ   )ru   rf  ræ   r€  r!   rë   rh   r  rí   r   rò   r  r™   rO   rk   r"   Ú_sanitized_follow_up_kwargs)rA  rÈ   rC  rŠ   rœ   Ú"anthropic_messages_provider_configÚ*anthropic_messages_optional_request_paramsrÒ   rØ  rÚ  r†   rh  rï   r}  Úfollow_up_messagesrþ   r‘   rl   Útool_resultsÚanthropic_maxrÞ  rF  rG  Ú"optional_params_without_max_tokensÚfull_model_nameÚagentic_paramsÚ	candidates                              rH   Úasync_build_agentic_loop_planz/CompresrGuardrail.async_build_agentic_loop_plan*  sß  è ø€ ð /4¯i©i¸ÀbÓ.Iˆ
à×ÑÔÜ,¨[Ó9ˆ	Ø"&×":Ñ":¸9ÀjÓ"QÑˆ	�<Ùä"°EÔ:Ð:ä% hÔ/Ü!% h£Ô2QÐRZÐ\eÓ2fÑ!fÑÜ,¨XÔ6Ü!% h£Ô2TÐU]Ð_hÓ2iÑ!iÑä'MÈhÐXaÓ'bÐàfo÷#ÙWbÐWYÐ[b˜°·±¸³È'ÓRð#ˆLñ #ô "& h£Ð3DÐ2EÑ!EÈÑ!TÐàI×MÑMÈlÓ[ˆØ9FÐ9R©ÐX^×XbÑXbÐcoÓXpˆ
àG×MÑMÓO÷5
Ù�Q˜ÐSTÐXdÓSdˆAˆq‰Dð5
Ð*ñ 5
ð  ˆØÐ"Ü$+¨KÐ9MÈrÓ$R×$VÑ$VÐWlÐnpÓ$qˆNØ-×1Ñ1°'¸5ÓAˆIÜ˜)¤SÔ)©iØ"+�äØ!Ü1Ø%Ø+Ø%Ø BØ×7Ñ7¸Ó?ôð "Ð#6Ð7ô

ð 
	
ùó%#ùó5
ùs&   ‚B*F/Â,F#ÃAF/ÄF)Ä'F)Ä,BF/c                ó
  — |j                  «       D ��ci c]   \  }}|j                  d«      rŒ|dk7  sŒ||“Œ" }}}| j                  «       }dD ]¡  }|j                  |«      }t	        |t
        «      sŒ%|j                  t        «      }t	        |t        «      sŒK|D �	cg c]  }	|€Œ|	|k(  sŒ|	‘Œ }
}	|
ri |¥t        |
i¥n-|j                  «       D ��ci c]  \  }}|t        k7  sŒ||“Œ c}}||<   Œ£ |S c c}}w c c}	w c c}}w )a  Copy of the request kwargs for the retrieval follow-up with other
        guardrails' pre-call-executed markers stripped, so input guardrails
        re-inspect the restored originals; only this guardrail's own marker is
        kept, to avoid recompressing what it just retrieved.Ú	_compresrÚlitellm_logging_objrØ   )r  Ú
startswithÚ_pre_call_markerru   rO   rb   r   rh   )rA  rÚ  rF  rG  r®  Ú
own_markerrß   ÚmetaÚexecutedrÐ  Úkepts              rH   rã  z-CompresrGuardrail._sanitized_follow_up_kwargsc  s  € ð $Ÿ\™\›^÷)
Ù�Q˜°1·<±<ÀÕ3LÐQRÐVkÓQkˆAˆq‰Dð)
ˆñ )
ð !×1Ñ1Ó3ˆ
Ø8ò 	ˆHØ—7‘7˜8Ó$ˆDÜ˜d¤DÔ)ØØ—x‘xÔ @ÓAˆHÜ˜h¬Ô-ØØ'ÖV˜!¨:Ñ+AÀaÈ:Ão’AÐVˆDÐVñ ð A�4Ð@Ô9¸4Ò@à'+§z¡z£|×]™t˜q !°qÔ<\Ó7\�a˜‘dÓ]ð �ŠMð	ð ˆ
ùó#)
ùò Wùó ^s-   ”C4®C4´C4ÂC:Â%C:Â+C:ÃC?Ã$C?c                 ó   — ddl m}  | S )Nr   ©ÚCompresrGuardrailConfigModel)Ú7litellm.types.proxy.guardrails.guardrail_hooks.compresrrù  rø  s    rH   Úget_config_modelz"CompresrGuardrail.get_config_model{  s   € õ	
ð ,Ð+r`   )NNNNNNNNNNNNNFNNNNNNN)*rB  ú
str | NonerÜ   rü  rC  rü  r(  úfloat | Noner)  úbool | Noner*  ú
int | Noner+  rþ  r,  rþ  r-  rþ  r.  rþ  r/  rþ  r!  rü  r"  z=GuardrailEventHooks | list[GuardrailEventHooks] | Mode | Noner#  rÍ  r0  rü  r1  rÿ  r2  rþ  r3  rþ  r4  rý  r5  rý  r9  zdict[str, object] | None)rM  rb   ÚreturnrÍ  )r   r
  )rS  rk   rV  údict[str, object]r   ÚNone)r   r  )rh  rü  r   r
  )rh  rk   r­   r
  r   r  )rn  r
  r   r
  )rh  rü  r¶   rk   r   rü  )rh  rü  r†   r  r   z0tuple[list[tuple[dict[str, object], str]], bool])r—  ú	list[str]r˜  r  r   zlist[dict[str, object]] | None)rŠ   r  r£  rÿ  r   ú	list[int])rŠ   r  r   ztuple[str, int | None]rC   )rŠ   r  r¤  r  r—  r  rˆ  r  r¬  rÍ  r­  zdict[str, str] | Noner   r  )rº  r½   r»  r  r   úlist[object] | None)
r:   r#   rM  rb   rÏ  zLiteral['request', 'response']rÒ   úLiteLLMLoggingObj | Noner   r#   )rœ   r	   rC  rk   rŠ   ú
list[dict]rÈ   zlist[dict] | NonerØ  rÍ  rÙ  rk   rÚ  rb   r   ztuple[bool, dict])rÈ   rb   rC  rk   rŠ   r  rœ   r	   rä  r	   rå  rb   rÒ   r  rØ  rÍ  rÚ  rb   r   r!   )rÚ  rb   r   r  )r   z)type[GuardrailConfigModel[object]] | None)r  r  r  r@  rO  rQ  rW  r^  rf  ri  ro  rk  rw  r€  r   r¥  Ústaticmethodr§  r·  r¿  r   rÖ  rÛ  rí  rã  rû  Ú__classcell__)rH  s   @rH   r  r  ð  sW  ø„ ð  $Ø"Ø Ø15Ø"Ø,0Ø-1Ø'+Ø(,Ø*.Ø(,Ø%)ØTXØ Ø+/Ø)-Ø+/Ø#Ø*.Ø*.Ø7;ð-V
àðV
ð ðV
ð ð	V
ð
 #/ðV
ð ðV
ð  *ðV
ð  +ðV
ð %ðV
ð &ðV
ð (ðV
ð &ðV
ð #ðV
ð RðV
ð ðV
ð  )ð!V
ð" 'ð#V
ð$ )ð%V
ð& ð'V
ð( (ð)V
ð* (ð+V
ð, 5õ-V
óp	Aó
óFó 9ó
!óGó
 óó$ð'Ø#ð'Ø1Hð'à	9ó'ð8nàðnð ðnð 
(ó	nó`ð4 ðØ)ðà	òó ðð 59ð4à)ð4ð ð4ð ð	4ð
 )ð4ð ð4ð 2ð4ð 
ó4ðl òaó ðað. ð 15ðk!à)ðk!ð ðk!ð 3ð	k!ð
 .ðk!ð 
#òk!ó ðk!ðZ0àð0ð ð0ð ð	0ð
 !ð0ð ð0ð !ð0ð ð0ð 
ó0ð"7
àð7
ð ð7
ð ð	7
ð
 ð7
ð -0ð7
ð 59ð7
ð .ð7
ð ð7
ð ð7
ð 
ó7
órð0 ò,ó ô,r`   r  )rR   rk   r   z4ipaddress.IPv4Address | ipaddress.IPv6Address | None)r\   rk   r   rk   )rd   r½   r   zTypeGuard[dict[str, object]])rd   r½   r   zTypeGuard[list[object]])rl   r½   rm   rk   r   r½   )ry   r  r   rk   )rŠ   r  r‹   rM   rŒ   rk   r   rk   )rd   r½   r   rM   )r-   )rœ   r½   r�   rM   r   rk   )r˜   rk   r   rk   )r­   r
  r   rM   )r¶   rk   r   rk   )r   r  )rÈ   r½   r   rÍ  )rË   r½   r   r  )rœ   r½   r   r  )rÒ   r½   r   rü  )rÒ   r½   r   rk   )rœ   r½   r   rÍ  )rœ   r½   rï   ú#list[tuple[dict[str, object], str]]r   r  )rœ   r½   rï   r
  r   r  )nr  Ú
__future__r   r�  r£   rD   rw   r`  Úcollectionsr   r   Údataclassesr   r   Útypingr   r	   r
   r   r   Úurllib.parser   rš   Úfastapir   r   ÚHttpxResponser“  Úlitellm._loggingr   Úlitellm.constantsr   Ú%litellm.integrations.custom_guardrailr   r   Ú3litellm.litellm_core_utils.prompt_templates.factoryr   r   r   Ú&litellm.llms.custom_httpx.http_handlerr   r   Úlitellm.proxy._typesr   Ú5litellm.proxy.guardrails.guardrail_hooks.content_textr   r   r   r   Úlitellm.secret_managers.mainr   Úlitellm.types.guardrailsr   r    Ú(litellm.types.integrations.custom_loggerr!   r"   Úlitellm.types.utilsr#   Ú*litellm.litellm_core_utils.litellm_loggingr$   ÚLiteLLMLoggingObjÚ3litellm.types.proxy.guardrails.guardrail_hooks.baser%   r&   r  r(   r)   r+   r,   r.   r/   r0   r2   r3   r4   r6   r7   r8   Ú	frozensetr<   rA   rJ   rT   r_   re   ri   rn   r|   r”   r–   rž   r§   r®   r¸   r»   rÆ   rÉ   rÍ   rÏ   rÔ   rá   ræ   rë   rí   rò   r   r  r  r  rÅ   r`   rH   ú<module>r!     sd  ðòõ #ã Û Û Û Û ß ,ß (ß @Õ @Ý !ã Ý !Ý +ã Ý 1Ý >÷÷ñ ÷
õ 0÷ó õ 8ß >÷õ :áõõð +€ˆuÓ *Ø%8Ð ˜UÓ 8Ø3Ð �%Ó 3Ø#-Ð ˜5Ó -Ø*-Ð   %Ó -Ø'*Ð ˜uÓ *Ø 'Ð ˜Ó 'Ø,3Ð " EÓ 3ØÐ �EÓ Ø%5Ð ˜UÓ 5ð !2Ð ˜Ó 1à"#Ð ˜%Ó #ð $(Ð ˜5Ó 'Ø*€ˆUÓ *ñ +4Ò4RÓ*SÐ   %Ó SÙ!*òó"Ð ˜ó ñ  )ñ )Ø'oô)ó  Ð �uó ó
ó ó,#ó#óó ó"óJô	 óRó\ó
Ióóó0Bó2óóóó<9ó LóMðØðà2ðð óð:-Øð-à2ð-ð ó-ð@Øðà2ðð óð2 ÷ð ó ðôQ
,˜õ Q
,r`   