Ë
    uPêjÊ!  ã                  óÂ  — U d Z ddlmZ ddlZddlZddlmZ ddlmZ ddl	Z	dZ
ded<   d	Zded
<   dZded<   dZded<   dZded<    ej                   d«      Zded<    ej                   d«      Zded<    ee
j)                  «       f«      Zded<   d#d„Zd$d„Zd%d„Zd&d„Z	 	 	 	 	 	 d'd„Zd(d„Z	 	 	 	 	 	 	 	 	 	 d)d„Z	 d*	 	 	 	 	 d+d„Zd,d „Z	 	 	 	 d-d!„Z	 	 	 	 	 	 	 	 	 	 d.d"„Z y)/at  
Resolve AWS Bedrock ``requestMetadata`` from LiteLLM proxy identity and caller metadata.

Bedrock attaches request metadata to CloudTrail records and to the dimension AWS Cost
Explorer groups on, so everything here is opt-in: nothing is forwarded unless the operator
sets ``litellm.bedrock_request_metadata_fields`` (``litellm_settings`` on the proxy).

Two properties are load-bearing for that billing record and are asserted by the tests:
proxy identity is resolved first so it can never be evicted by caller-supplied pairs, and the
whole ``user_api_key_`` prefix is reserved so a caller cannot write a proxy-authoritative
looking key. Values that break Bedrock's constraints are dropped rather than sanitised or
rejected, because an operator flipping this setting on must not turn a working request into a
400 and a silently rewritten attribution key is worse than an absent one.
é    )ÚannotationsN)ÚMapping)ÚFinalzX-Amzn-Bedrock-Request-Metadatar   ÚBEDROCK_REQUEST_METADATA_HEADERé   Ú"BEDROCK_REQUEST_METADATA_MAX_PAIRSÚuser_api_key_Ú(BEDROCK_REQUEST_METADATA_IDENTITY_PREFIXÚspend_logs_metadataÚ%BEDROCK_REQUEST_METADATA_CLIENT_FIELD)ÚmetadataÚlitellm_metadatazFinal[tuple[str, ...]]Ú_METADATA_PARAM_NAMESz!^[a-zA-Z0-9\s:_@$#=/+,.-]{1,256}$Ú_KEY_PATTERNz!^[a-zA-Z0-9\s:_@$#=/+,.-]{0,256}$Ú_VALUE_PATTERNzFinal[frozenset[str]]Ú_OWNED_HEADER_NAMESc                ób   — t         j                  | «      d uxr t        j                  |«      d uS ©N)r   Úmatchr   )ÚkeyÚvalues     új/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/llms/bedrock/request_metadata.pyÚ_is_forwardabler   $   s.   € Ü×Ñ˜cÓ"¨$Ð.ÒZ´>×3GÑ3GÈÓ3NÐVZÐ3ZÐZó    c                ód   — t        | t        «      syt        d„ | j                  «       D «       «      S )N© c              3  ón   K  — | ]-  \  }}t        |t        «      sŒt        |t        «      sŒ(||f–— Œ/ y ­wr   )Ú
isinstanceÚstr©Ú.0r   r   s      r   ú	<genexpr>z_text_pairs.<locals>.<genexpr>+   s.   è ø€ Òr¡* # uÄ*ÈSÔRUÕBVÔ[eÐfkÔmpÕ[q�#�u”Ñrùs   ‚5›5¬	5)r   r   ÚtupleÚitems)Úsources    r   Ú_text_pairsr&   (   s&   € Ü�fœgÔ&ØÜÑr°·±³ÔrÓrÐrr   c                 ó°   ‡— t         j                  } t        | t        t        f«      syt	        d„ | D «       «      Št	        ˆfd„t        ‰«      D «       «      S )zó
    The operator allow-list, deduplicated so a field repeated in config cannot consume a second
    reserved slot and shrink the client budget for nothing. First occurrence wins, which keeps
    the operator's declared precedence intact.
    r   c              3  ó2   K  — | ]  }t        |«      –— Œ y ­wr   )r   )r!   Úfields     r   r"   z"_allowed_fields.<locals>.<genexpr>7   s   è ø€ Ò=¨œ#˜eŸ*Ñ=ùs   ‚c              3  ó8   •K  — | ]  \  }}|‰d | vsŒ|–— Œ y ­wr   r   )r!   Úindexr)   Úfieldss      €r   r"   z"_allowed_fields.<locals>.<genexpr>8   s$   øè ø€ Ò\™<˜5 %ÀÈVÐTZÐUZÈ^Ò@[”Ñ\ùs   ƒ“)ÚlitellmÚbedrock_request_metadata_fieldsr   Úlistr#   Ú	enumerate)Ú
configuredr,   s    @r   Ú_allowed_fieldsr2   .   sG   ø€ ô !(× GÑ G€JÜ�j¤4¬ -Ô0ØÜÑ=°*Ô=Ó=€FÜÓ\¬9°VÓ+<Ô\Ó\Ð\r   c                ó:   ‡ — ‰ €yt        ˆ fd„t        D «       «      S )zZ``metadata`` on /v1/chat/completions, ``litellm_metadata`` on the LITELLM_METADATA_ROUTES.r   c              3  ód   •K  — | ]'  }‰j                  |«      }t        |t        «      r|–— Œ) y ­wr   )Úgetr   r   )r!   Únamer%   Úlitellm_paramss      €r   r"   z$_metadata_sources.<locals>.<genexpr>?   s3   øè ø€ ò àØ%×)Ñ)¨$Ó/ˆFÜ�fœgÔ&ô 	ñùs   ƒ-0)r#   r   )r7   s   `r   Ú_metadata_sourcesr8   ;   s&   ø€ àÐØÜó ä)ôó ð r   c                ó:   ‡ — t        ˆ fd„|D «       «      d t         S )Nc              3  ó„   •K  — | ]7  }|j                  t        «      r t        ‰|«      }|�t        ||«      r||f–— Œ9 y ­wr   )Ú
startswithr
   Ú_first_textr   )r!   r)   r   Úsourcess      €r   r"   z"_identity_pairs.<locals>.<genexpr>K   sG   øè ø€ ò àØ×ÑÔDÔEÜ! '¨5Ó1ˆEØÐ¤°¸Ô!>ð	 
�Œñùs   ƒ=A )r#   r   )r=   Úallowed_fieldss   ` r   Ú_identity_pairsr?   G   s,   ø€ ô ó à#ôó ð *Ô)ð+ð +r   c                ó2   ‡‡— t        ˆˆfd„| D «       d «      S )Nc              3  óf   •K  — | ](  }t        |j                  ‰«      xŠt        «      sŒ%‰–— Œ* y ­wr   )r   r5   r   )r!   r%   r)   r   s     €€r   r"   z_first_text.<locals>.<genexpr>U   s)   øè ø€ Ò\˜6´
ÀFÇJÁJÈuÓDUÐ;U¸5ÔWZÕ0[”Ñ\ùs   ƒ&1ª1)Únext)r=   r)   r   s    `@r   r<   r<   T   s   ù€ ÜÔ\ WÔ\Ð^bÓcÐcr   c                ó¼   ‡— t         |v rt        d„ | D «       «      nd}t        d„ g t        |«      ¢|¢­D «       «      Št        ˆfd„t        ‰«      D «       «      d | S )Nc              3  óf   K  — | ])  }t        |j                  t        «      «      D ]  }|–— Œ Œ+ y ­wr   )r&   r5   r   )r!   r%   Úpairs      r   r"   z _client_pairs.<locals>.<genexpr>_   s+   è ø€ Òt�v´[ÀÇÁÔLqÓArÓ5sÒt¨TŒdÐtˆdÑtùs   ‚/1r   c              3  ól   K  — | ],  \  }}|j                  t        «      st        ||«      r||f–— Œ. y ­wr   )r;   r
   r   r    s      r   r"   z _client_pairs.<locals>.<genexpr>c   s8   è ø€ ò áˆC�Ø�~‰~ÔFÔGÌOÐ\_ÐafÔLgð 
ˆeŒñùs   ‚24c              3  ó\   •K  — | ]#  \  }}|d    t        d„ ‰d| D «       «      vr|–— Œ% y­w)r   c              3  ó&   K  — | ]	  \  }}|–— Œ y ­wr   r   )r!   ÚearlierÚ_s      r   r"   z*_client_pairs.<locals>.<genexpr>.<genexpr>k   s   è ø€ ÒM©J¨G°Q¤ÑMùs   ‚N)r#   )r!   r+   rE   Ú
candidatess      €r   r"   z _client_pairs.<locals>.<genexpr>h   s;   øè ø€ ò áˆE�4Ø�‰7œ%ÑM¸*ÀVÀeÐ:LÔMÓMÑMô 	ñùs   ƒ),)r   r#   r&   r0   )r=   r>   Úcaller_metadataÚbudgetÚspend_logs_pairsrK   s        @r   Ú_client_pairsrO   X   s{   ø€ ô 1°NÑBô 	Ñt ÔtÔtàð ô
 ñ àLœK¨Ó8ÐLÐ;KÑLôó €Jô
 ó ä$ ZÓ0ôó ð €vð	ð r   c           	     óÔ   — t        «       }|syt        | «      }t        ||«      }t        |||t        t        |«      z
  ¬«      }g |¢|¢­D ��ci c]  \  }}||“Œ
 }}}|xs dS c c}}w )a%  
    Resolve the ``requestMetadata`` pairs to send to Bedrock, or ``None`` when the feature is
    off or nothing survives Bedrock's constraints. The result is a plain dict because it is
    written straight onto the Converse body, which Bedrock types as ``dict[str, str]``.

    ``caller_metadata`` is any ``requestMetadata`` the caller passed explicitly. It has already
    been validated (and rejected with a 400) by the Converse transformation, so it is only
    filtered here for the reserved identity prefix and the remaining slot budget.
    N)r=   r>   rL   rM   )r2   r8   r?   rO   r   Úlen)	r7   rL   r>   r=   ÚidentityÚclientr   r   Úresolveds	            r   Ú resolve_bedrock_request_metadatarU   o   s€   € ô ,Ó-€NÙØÜ& ~Ó6€GÜ% g¨~Ó>€HÜ!ØØ%Ø'Ü1´C¸³MÑAô	€Fð 5I°hÐ4HÀÑ4H×I¡j c¨5�s˜E‘zÐI€HÑIØÒ�tÐùó Js   ÁA$c                 ó(   — t        t        «       «      S )a  
    Whether the proxy OWNS the request-metadata field and header name for this request.

    Ownership follows the operator's opt-in alone, never whether anything resolved, because a
    caller can suppress the resolver by omitting the allow-listed fields or by sending values
    that all fail Bedrock's rules. Owned-but-empty has to mean "absent on the wire" rather than
    "fall back to whatever the caller supplied", or the reserved-prefix guarantee is bypassable
    by anyone who can make the resolver produce nothing.
    )Úboolr2   r   r   r   Ú!bedrock_request_metadata_is_ownedrX   ‹   s   € ô ”Ó!Ó"Ð"r   c                ó    — t        «       st        «       dfS t        | «      }|€t        dfS t        t        t        j                  |d¬«      fffS )a  
    The signed ``X-Amzn-Bedrock-Request-Metadata`` header for the Invoke paths, which have no
    body field for request metadata.

    Returns the header names the proxy OWNS and, separately, the pairs to send. Ownership is
    reported whenever forwarding is enabled, including when nothing resolves, because a caller
    can suppress the resolver (omit the allow-listed fields, or send values that all fail
    Bedrock's rules) and an owned-but-empty result must still evict the caller's header rather
    than fall back to it.
    r   )ú,ú:)Ú
separators)rX   Ú	frozensetrU   r   r   ÚjsonÚdumps)r7   rT   s     r   Ú bedrock_request_metadata_headersr`   ˜   sQ   € ô -Ô.Ü‹{˜BˆÐÜ6°~ÓF€HØÐÜ" BÐ&Ð&ÜÔ"AÄ4Ç:Á:ÈhÐcmÔCnÐ!oÐ qÐqÐqr   c                óÀ   ‡‡— |s|s‰s| S t        d„ | D «       «      Šg ˆfd„| j                  «       D «       ¢ˆfd„|D «       ¢|¢­D ��ci c]  \  }}||“Œ
 c}}S c c}}w )ab  
    Merge the ``X-Amzn-*`` headers the Invoke paths derive from params.

    ``caller_owned`` (the guardrail headers) defers to a header the caller already set, which is
    the long-standing behaviour for those. ``proxy_owned_names`` are dropped from the caller's
    headers unconditionally and re-supplied only from ``proxy_owned``, because those names carry
    proxy-authenticated identity into an AWS billing record that the caller must not be able to
    write. Names are compared case-insensitively so a caller cannot leave a second spelling in
    the dict and let the transport pick the winner.
    c              3  ó<   K  — | ]  }|j                  «       –— Œ y ­wr   ©Úlower)r!   r6   s     r   r"   z/merge_bedrock_invoke_headers.<locals>.<genexpr>¿   s   è ø€ Ò%G°t d§j¡j§lÑ%Gùs   ‚c              3  óR   •K  — | ]  \  }}|j                  «       ‰vsŒ||f–— Œ  y ­wr   rc   )r!   ÚnÚvÚproxy_owned_namess      €r   r"   z/merge_bedrock_invoke_headers.<locals>.<genexpr>Ã   s'   øè ø€ ÒW™˜˜A°A·G±G³IÐEVÒ4Vˆq�!ŒfÑWùó   ƒ'ž	'c              3  óR   •K  — | ]  \  }}|j                  «       ‰vsŒ||f–— Œ  y ­wr   rc   )r!   rf   rg   Úexisting_namess      €r   r"   z/merge_bedrock_invoke_headers.<locals>.<genexpr>Ä   s&   øè ø€ ÒQ™˜˜A°·±³À.Ò1Pˆq�!ŒfÑQùri   )r]   r$   )ÚheadersÚcaller_ownedÚproxy_ownedrh   r6   r   rk   s      `  @r   Úmerge_bedrock_invoke_headersro   ­   sv   ù€ ñ  ¡Ñ4EØˆÜ%Ñ%G¸wÔ%GÓG€Nð
ÛW §¡£ÔWð
ãQ ÔQð
ð ñ
÷áˆD�%ð 	ˆe‰óð ùó s   Á	A)r   r   r   r   ÚreturnrW   )r%   Úobjectrp   útuple[tuple[str, str], ...])rp   útuple[str, ...])r7   úMapping[str, object] | Nonerp   ú tuple[Mapping[str, object], ...])r=   ru   r>   rs   rp   rr   )r=   ru   r)   r   rp   z
str | None)
r=   ru   r>   rs   rL   rq   rM   Úintrp   rr   r   )r7   rt   rL   rq   rp   zdict[str, str] | None)rp   rW   )r7   rt   rp   z2tuple[frozenset[str], tuple[tuple[str, str], ...]])
rl   údict[str, str]rm   rr   rn   rr   rh   zfrozenset[str]rp   rw   )!Ú__doc__Ú
__future__r   r^   ÚreÚcollections.abcr   Útypingr   r-   r   Ú__annotations__r   r
   r   r   Úcompiler   r   r]   rd   r   r   r&   r2   r8   r?   r<   rO   rU   rX   r`   ro   r   r   r   ú<module>r      sƒ  ðòõ #ã Û 	Ý #Ý ã à)JÐ  Ó JØ,.Ð " EÓ .Ø2AÐ (¨%Ó AØ/DÐ % uÓ Dà0PÐ Ð-Ó PØ �b—j‘jÐ!EÓF€ˆeÓ FØ"˜Ÿ
™
Ð#GÓH€�Ó HÙ-6Ð8W×8]Ñ8]Ó8_Ð7aÓ-bÐ Ð*Ó bó[ósó
]ó	ð
+Ø-ð
+à#ð
+ð !ó
+ódðØ-ðà#ðð ðð ð	ð
 !óð2 #ðØ/ðàðð óó8
#ðrØ/ðrà7órð*Øðà-ðð -ðð &ð	ð
 ôr   