Ë
    tPêj2& ã                   óV  — U d dl Z d dlZd dlZd dlmZmZ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 d dlmZmZmZ d dlZd dlmZmZ d d	lmZ d d
lmZmZmZm Z  d dl!m"Z" d dl#m$Z$m%Z%m&Z&m'Z' d dl(­ d dl)m*Z* d dl+m,Z, d dl-m.Z. d dl/m0Z0 dÀdede1dz  de2dz  fd„Z3	 dÀde4e2   dz  dede1dz  de5e1e2dz  f   fd„Z6de7de1fd„Z8dede2de1fd„Z9de2dede.de1fd „Z:d!e2de2ded"edz  de1f
d#„Z;d$Z<ee5e2d%f      e=d&<   d'Z>ee5e2d%f      e=d(<    e?d)d*h«      Z@ee?e2      e=d+<    e?h d,£«      ZAee?e2      e=d-<   de?e2   fd.„ZBd/d0d1d2d3d4d5d6d7d8d9d:d;d<d=d>d?d@dAdBdCdDg eC eB«       «      ¢ eCe0jˆ                  j‹                  «       «      ¢­ZFee5e2d%f      e=dE<   dFe7dGe7d"edz  d!e2ddf
dH„ZGdIZHee5e2d%f      e=dJ<   dee2eIf   deeI   fdK„ZJdLeIdMeKdee2ee2eIf   z     fdN„ZLdee2eIf   dee2ee2eIf   z     fdO„ZMdLe2ddfdP„ZNde7dGe7d"edz  d!e2de1f
dQ„ZOdLede7e2ef   dz  fdR„ZPdedSe7dTe2fdU„ZQdVe2fdW„ZRdede2fdX„ZSdede2dz  fdY„ZT edZ¬[«      dTe2de2fd\„«       ZUdede1fd]„ZVd^ede1fd_„ZWd`eKfda„ZXdbe2dce2deKdz  fdd„ZYdbe2deKdz  fde„ZZdbe2deKdz  fdf„Z[	 dÁdge\dbe2dz  de7e2eKf   dz  fdh„Z]	 dÁdge\dbe2dz  de7e2eKf   dz  fdi„Z^djZ_ee=dk<   dlZ`ee=dm<    e?e_e`h«      Zaee=dn<    ee«      Zbee=do<    eee2ef   «      Zcee=dp<   dqeIdeKdz  fdr„ZddqeIdee2eKf   dz  fds„Zedtee2ef   dz  dbe2dz  deKdz  fdu„Zf	 dÁdge\dbe2dz  deKdz  fdv„Zg G dw„ dxe«      Zhdyehdzee2eIf   de5eIeIf   fd{„Zidyehdzee2eIf   dz  dge\d|ejd}   ddf
d~„Zk G d„ d€e«      Zldyeldzee2eIf   dz  dge\d|ejd}   ddf
d�„Zmdge\d‚ejdƒ   d„ejd…   de7e2eKf   dz  fd†„Zndge\de7e2eKf   dz  fd‡„Zodge\de7e2eKf   dz  fdˆ„Zpdge\de7e2eKf   dz  fd‰„Zqdge\de7e2eKf   dz  fdŠ„Zrdge\de7e2eKf   dz  fd‹„Zsdge\de7e2eKf   dz  fdŒ„Ztdge\de7e2eKf   dz  fd�„ZudŽe1d�e1de2dz  fd�„Zvdawed‘œdŽe1d�e1d’e
ddfd“„ZxdTe2de1fd”„Zyde1fd•„Zzde4dz  fd–„Z{d—e7dz  de2dz  fd˜„Z|dLede2dz  fd™„Z}dÁde7d—e7dz  de2dz  fdš„Z~d›Zee=dœ<   d�Z€ee=dž<   dŸZ�ee=d <   d¡Z‚ee=d¢<   d£Zƒee=d¤<   d¥Z„ee=d¦<   d§Z…ee=d¨<   d©Z†ee=dª<   d«e4e2   dLeddfd¬„Z‡d«e4e2   de4e2   fd­„Zˆd®ee2ef   dz  d¯e2defd°„Z‰dTe2d±e5e2d%f   de1fd²„ZŠdTe2de1fd³„Z‹	 	 dÂd´edµe2dz  d"edz  de4e2   fd¶„ZŒd·e2dz  d"edz  de2dz  fd¸„Z�	 	 	 dÃdSe7dTe2d—ee2ef   dz  d¹ee2ef   dz  d"edz  de4e2   fdº„ZŽd«e4e2   de2e4e2   z  dz  fd»„Z�dedz  de1fd¼„Z�	 	 	 	 dÄdSe7dTe2d—ee2ef   dz  d¹ee2ef   dz  d"edz  dedz  de2e4e2   z  dz  fd½„Z‘d¾e2de2fd¿„Z’y)Åé    N)Ú
CollectionÚIteratorÚMapping)Ú	lru_cache)ÚLogger)ÚAnyÚFinalÚProtocol)ÚHTTPExceptionÚRequestÚstatus)ÚPositiveIntÚTypeAdapterÚValidationError)ÚRouterÚprovider_list)Úverbose_proxy_logger)Ú'BATCH_ENQUEUED_TOKEN_LIMIT_METADATA_KEYÚEMPTY_MAPPINGÚMINIMUM_CUSTOM_KEY_LENGTHÚSTANDARD_CUSTOMER_ID_HEADERS©Úsafe_json_loads)Ú	SSRFErrorÚ"is_url_destination_allowed_by_hostÚ#provider_url_destination_candidatesÚvalidate_url)Ú*)Úextract_nested_form_metadata)Ú$LITELLM_PASS_THROUGH_ENDPOINT_MARKER)Ú#CONFIGURABLE_CLIENTSIDE_AUTH_PARAMS)ÚCustomPricingLiteLLMParamsFÚrequestÚuse_x_forwarded_forÚreturnc                 óœ   — d }|du rd| j                   v r| j                   d   }|S | j                  �| j                  j                  }|S d}|S )NTzx-forwarded-forÚ )ÚheadersÚclientÚhost)r#   r$   Ú	client_ips      úb/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/auth/auth_utils.pyÚ_get_request_ip_addressr-   %   sb   € Ø€IØ˜dÑ"Ð'8¸G¿O¹OÑ'KØ—O‘OÐ$5Ñ6ˆ	ð Ðð 
�‰Ð	#Ø—N‘N×'Ñ'ˆ	ð Ðð ˆ	àÐó    Úallowed_ipsc                 ó:   — | €yt        ||¬«      }|| vrd|fS d|fS )z)
    Returns if ip is allowed or not
    )TN)r#   r$   FT)r-   )r/   r#   r$   r+   s       r,   Ú_check_valid_ipr1   1   s;   € ð ÐØô /°wÐTgÔh€Ið ˜Ñ#Ø�iÐÐà�ˆ?Ðr.   Úrequest_bodyc                 ó–  — d}| j                  d«      }|€yd|v sd|v sd|v sd|v ry| j                  d«      }|r t        |t        «      r|j                  «       syt	        t
        d	d«      r7d
D ]2  }| j                  |«      }|rt        |t        «      sŒ'	 t        |«       Œ4 y# t        $ r}t        d|› d|›d|› d�«      ‚d}~ww xY w)aØ  
    if 'api_base' in request body. Check if complete credentials given. Prevent malicious attacks.

    Supplying an ``api_key`` is necessary but not sufficient: even with
    credentials supplied, an ``api_base`` / ``base_url`` that resolves to a
    private/internal/cloud-metadata address would still allow the proxy to
    be used as an SSRF pivot. Validate any URL fields here so the gate
    can't be bypassed with ``api_key=anything`` plus a malicious target.
    NÚmodelFÚ	sagemakerÚbedrockÚ	vertex_aiÚvertex_ai_betaÚapi_keyÚuser_url_validation)Úapi_baseÚbase_urlzRejected request: client-side ú=z  is rejected by the SSRF guard (z).T)	ÚgetÚ
isinstanceÚstrÚstripÚgetattrÚlitellmr   r   Ú
ValueError)r2   Úgiven_modelÚapi_key_valueÚ	url_fieldÚ	url_valueÚes         r,   Úcheck_complete_credentialsrJ   F   sý   € ð #€Kà×"Ñ" 7Ó+€KØÐØð 	�{Ñ"Ø˜Ñ#Ø˜+Ñ%Ø˜{Ñ*ð à'×+Ñ+¨IÓ6€MÙœj¨¼Ô<À×ATÑATÔAVØô ŒwÐ-¨uÔ5Ø1ò 		ˆIØ$×(Ñ(¨Ó3ˆIÙ¤J¨y¼#Ô$>ØðÜ˜YÕ'ð		ð øô ò Ü Ø4°Y°K¸qÀÀÐMmÐnoÐmpÐprÐsóð ûðús   ÂB%Â%	CÂ.CÃCÚrequest_body_valueÚ	regex_strc                 ó<   — t        j                  || «      s|| k(  ryy)zP
    Check if request_body_value matches the regex_str or is equal to param
    TF)ÚreÚmatch©rK   rL   s     r,   Úcheck_regex_or_str_matchrQ   v   s!   € ô 
‡x�x�	Ð-Ô.°)Ð?QÒ2QØØr.   ÚparamÚ#configurable_clientside_auth_paramsc                 ó˜   — |€y|D ]B  }t        |t        «      r| |k(  r yt        |t        «      sŒ+| dk(  sŒ1t        ||d   ¬«      sŒB y y)zd
    Check if param is a str or dict and if request_body_value is in the list of allowed values
    FTr;   rP   )r?   r@   ÚdictrQ   )rR   rK   rS   Úitems       r,   Ú_is_param_allowedrW      s^   € ð +Ð2Øà3ò ˆÜ�dœCÔ  U¨d¢]ÙÜ˜œdÕ#Ø˜
Ó"Ô'?Ø#5Ø˜zÑ*ö(ñ ðð r.   r4   Ú
llm_routerc                 ó  — |€y|j                  | ¬«      }|€@| j                  dd«      d   t        v r%|j                  | j                  dd«      d   ¬«      }|€y|�|j                  €yt	        |||j                  ¬«      S )z®
    Check if model is allowed to use configurable client-side params
    - get matching model
    - check if 'clientside_configurable_parameters' is set for model
    -
    F)Úmodel_groupú/é   r   )rR   rK   rS   )Úget_model_group_infoÚsplitr   rS   rW   )r4   rR   rK   rX   Ú
model_infos        r,   Ú5_allow_model_level_clientside_configurable_parametersr`   —   s�   € ð ÐØà×0Ñ0¸UÐ0ÓC€JØÐà�;‰;�s˜AÓ˜qÑ!¤]Ñ2Ø#×8Ñ8ÀUÇ[Á[ÐQTÐVWÓEXÐYZÑE[Ð8Ó\ˆJàÐØàÐ˜Z×KÑKÐSØäØØ-Ø,6×,ZÑ,Zôð r.   )Úlitellm_embedding_configÚ
extra_body.Ú_NESTED_CONFIG_KEYS)ÚmetadataÚlitellm_metadataÚ_NESTED_METADATA_KEYSÚlangfuse_prompt_versionÚlangsmith_sampling_rateÚ_SAFE_CLIENT_CALLBACK_PARAMS>   Úwandb_api_keyÚposthog_api_urlÚweave_project_idÚuser_api_key_auth_metadataÚ"_EXTRA_BANNED_OBSERVABILITY_PARAMSc                  ó\   — ddl m} m} t        |«      t        z
  t        | «      z  t
        z  S )aÃ  Derive the observability ban list from the canonical allowlist.

    ``_supported_callback_params`` and ``_request_blocked_callback_params`` in
    ``litellm/litellm_core_utils/initialize_dynamic_callback_params.py`` is
    the single place that enumerates every observability field integrations
    resolve from kwargs/metadata, plus fields that integration code explicitly
    blocks from request-supplied callback params. Subtract the small set of
    informational fields (``_SAFE_CLIENT_CALLBACK_PARAMS``) and union with the
    extras the canonical allowlist hasn't caught up to yet. New integrations
    added to the canonical allowlist are banned by default, which is the safe
    failure mode.
    r   ©Ú _request_blocked_callback_paramsÚ_supported_callback_params)Ú=litellm.litellm_core_utils.initialize_dynamic_callback_paramsrq   rr   Ú	frozensetri   rn   rp   s     r,   Ú"_build_banned_observability_paramsru   ð   s3   € ÷ô 
Ð-Ó	.Ô1MÑ	MÜ
Ð4Ó
5ñ	6ä
,ñ	-ðr.   r;   r<   Úuser_configÚaws_sts_endpointÚaws_web_identity_tokenÚaws_role_nameÚaws_profile_nameÚaws_session_nameÚaws_external_idÚvertex_credentialsÚazure_ad_tokenÚaws_bedrock_runtime_endpointÚaws_bedrock_project_idÚbedrock_tagsÚs3_endpoint_urlÚsagemaker_base_urlÚdeployment_urlÚnvcf_function_idÚuse_sslÚrustÚ
model_listÚvertex_ai_credentialsÚ_BANNED_REQUEST_BODY_PARAMSÚbodyÚgeneral_settingsc                 ó”   — t         D ]?  }|| vrŒ|j                  d«      du r yt        ||| |   |¬«      du rŒ2t        d|› d�«      ‚ y)zÖRaise ``ValueError`` if ``body`` carries a banned param without admin opt-in.

    Shared between the root-level check and the nested-config check so a
    new banned param only needs to be added in one place.
    Úallow_client_side_credentialsTN)r4   rR   rK   rX   zRejected Request: aL   is not allowed in request body. Clientside passthrough requires explicit admin opt-in via either `general_settings.allow_client_side_credentials = true` (proxy-wide) or `configurable_clientside_auth_params` on the deployment in your proxy config.yaml. Relevant Issue: https://huntr.com/bounties/4001e1a2-7b7a-4776-a3ae-e6692ec3d997)rŠ   r>   r`   rD   )r‹   rŒ   rX   r4   rR   s        r,   Ú_check_banned_paramsr�   L  s~   € ô -ò 
ˆØ˜ÑØØ×ÑÐ ?Ó@ÀDÑHñ äAØØØ#'¨¡;Ø%ô	ð ñð ÜØ   ð (^ð ^ó
ð 	
ñ-
r.   )Ú	fallbacksÚcontext_window_fallbacksÚcontent_policy_fallbacksÚ_FALLBACK_FIELDSc              #   óž   K  — | j                  d«      }| |fD ]1  }t        |t        «      sŒt        D ]  }|j                  |«      –— Œ Œ3 y ­w)NÚrouter_settings_override)r>   r?   r   r“   )r2   ÚoverrideÚsourceÚfields       r,   Ú_iter_fallback_field_valuesr™   ~  sS   è ø€ Ø"×&Ñ&Ð'AÓB€HØ Ð*ò (ˆÜ�fœgÕ&Ü)ò (�Ø—j‘j Ó'Ó'ñ(ñ(ùs
   ‚)A¬!AÚvalueÚdepthc              #   ó  K  — |dt         j                  z  kD  rt        d«      ‚t        | t        «      sy | D ]È  }t        |t
        «      r|–— Œt        |t        «      sŒ)t        |j                  «       «      }|rt        d„ |D «       «      s|–— t        |j                  d«      t
        «      r2t        D ](  }t        |j                  |«      |dz   «      E d {  –—†  Œ* Œ«|D ]  }t        ||dz   «      E d {  –—†  Œ ŒÊ y 7 Œ(7 Œ­w)Né   zHRejected Request: fallback nesting exceeds the allowed validation depth.c              3   ó<   K  — | ]  }t        |t        «      –— Œ y ­w©N)r?   Úlist)Ú.0Úvs     r,   ú	<genexpr>z)_iter_fallback_targets.<locals>.<genexpr>�  s   è ø€ Ò"G¸1¤:¨a´×#6Ñ"Gùs   ‚r4   r\   )rC   ÚROUTER_MAX_FALLBACKSrD   r?   r    r@   r   ÚtupleÚvaluesÚallr>   r“   Ú_iter_fallback_targets)rš   r›   rV   r¦   r˜   Útarget_lists         r,   r¨   r¨   †  sõ   è ø€ Øˆq”7×/Ñ/Ñ/Ò/ÜÐcÓdÐdÜ�eœTÔ"ØØò NˆÜ�dœCÔ Ø‹JÜ˜œgÕ&Ü˜4Ÿ;™;›=Ó)ˆFÙœsÑ"GÀÔ"GÔGØ’
Ü˜$Ÿ(™( 7Ó+¬SÔ1Ü-ò R�EÜ5°d·h±h¸u³oÀuÈqÁyÓQ×QÑQñRð $*ò N�KÜ5°kÀ5È1Á9ÓM×MÑMñNñNð Røð Nús+   ‚ADÁ A:DÃDÃDÃ9DÃ:
DÄDc              #   óX   K  — t        | «      D ]  }t        |d«      E d {  –—†  Œ y 7 Œ­w)Nr   )r™   r¨   )r2   rš   s     r,   Úiter_request_fallback_targetsr«   š  s-   è ø€ Ü,¨\Ó:ò 4ˆÜ)¨%°Ó3×3Ñ3ñ4Ø3ús   ‚* (¡*c                 óÄ   — t        t        dg «      xs g }t        | «      D ]=  }|j                  «       j	                  d«      sŒ#t        ||«      rŒ0t        d| › d�«      ‚ y )NÚ&provider_url_destination_allowed_hosts)zhttp://zhttps://z3Rejected Request: URL-valued fallback destination 'z ' is not allowed. Configure custom endpoints with api_base instead, or add the destination host to `provider_url_destination_allowed_hosts` in litellm_settings.)rB   rC   r   ÚlowerÚ
startswithr   rD   )rš   Úallowed_hostsÚ	candidates      r,   Ú"_reject_url_valued_fallback_targetr²   Ÿ  ss   € Ü"¤7Ð,TÐVXÓYÒ_Ð]_€MÜ8¸Ó?ò 	
ˆ	Ø�‰Ó ×+Ñ+Ð,CÔDØÜ-¨i¸ÔGØÜØAÀ%Àð ILð Ló
ð 	
ñ	
r.   c                 óô  ‡— d| v rt        d«      ‚t        | |||«       t        D ]-  }t        | j	                  |«      «      }|€Œ t        ||||«       Œ/ t
        D ]]  Št        | j	                  ‰«      «      }|�t        ||||«       t        ˆfd„| D «       «      sŒBt        t        | ‰› d�¬«      |||«       Œ_ t        | «      D ]j  }t        |t        «      r<t        ||||«       |j	                  d«      }t        |t        «      sŒCt        |«       ŒOt        |t        «      sŒ`t        |«       Œl t        | j	                  d«      «      }	|	�*t        |	j	                  d«      «      }
|
�t        |
|||«       y	)
u€  
    Check if the request body is safe.

    A malicious user can set the ï»¿api_base to their own domain and invoke POST /chat/completions to intercept and steal the OpenAI API key.
    Relevant issue: https://huntr.com/bounties/4001e1a2-7b7a-4776-a3ae-e6692ec3d997

    The blocklist is enforced unconditionally. Legitimate clientside
    credential / endpoint passthrough goes through one of the two
    explicit admin opt-ins (``general_settings.allow_client_side_credentials``
    proxy-wide or ``configurable_clientside_auth_params`` per deployment).
    Historically there was a third, *implicit*, *caller-controlled* path:
    ``check_complete_credentials`` returned True when the caller supplied
    any non-empty ``api_key``, which made the entire blocklist a no-op.
    That bypass turned every missing entry on the blocklist into an
    exploitable SSRF / credential-exfil hole â€” see GHSA-jh89-88fc-qrfp,
    GHSA-3frq-6r6h-7j64, and the chain of veria-admin findings (Dv_m860l,
    b_yRJeQ5, stN90yjP, LBlyOAc8, U2TD78kg). Removed: the blocklist now
    has a single, predictable failure mode for missing entries (a 400),
    not a credential leak.

    Iterative single-level descent into ``_NESTED_CONFIG_KEYS`` (rather
    than recursion) covers nested-config attacks like Milvus's
    ``litellm_embedding_config.api_base`` (VERIA-6) without exposing a
    recursion-depth DoS surface.
    rˆ   z@Rejected Request: model_list is not allowed in the request body.c              3   ój   •K  — | ]*  }t        |t        «      xr |j                  ‰› d �«      –— Œ, y­w)ú[N)r?   r@   r¯   )r¡   ÚkeyÚmetadata_keys     €r,   r£   z'is_request_body_safe.<locals>.<genexpr>Ò  s0   øè ø€ ÒcÈsŒz˜#œsÓ#ÒJ¨¯©¸,¸ÀqÐ7IÓ(JÓJÑcùs   ƒ03rµ   )Ú	form_dataÚprefixr4   Úlitellm_paramsrd   T)rD   r�   rc   Ú_coerce_metadata_to_dictr>   rf   Úanyr   r«   r?   rU   r@   r²   )r2   rŒ   rX   r4   Ú
nested_keyÚnestedrd   ÚtargetÚtarget_modelrº   Úlitellm_params_metadatar·   s              @r,   Úis_request_body_saferÂ   ­  s…  ø€ ð4 �|Ñ#ÜÐ[Ó\Ð\Ü˜Ð'7¸ÀUÔKÜ)ò Nˆ
Ü)¨,×*:Ñ*:¸:Ó*FÓGˆØÑÜ  Ð)9¸:ÀuÕMðNô .ò 
ˆÜ+¨L×,<Ñ,<¸\Ó,JÓKˆØÐÜ  Ð+;¸ZÈÔOÜÓcÐVbÔcÕcÜ Ü,°|È|ÈnÐ\]ÐL^Ô_Ø ØØõ	ð
ô 0°Ó=ò 7ˆÜ�fœdÔ#Ü  Ð)9¸:ÀuÔMØ!Ÿ:™: gÓ.ˆLÜ˜,¬Õ,Ü2°<Õ@Ü˜¤Õ$Ü.¨vÕ6ð7ô 5°\×5EÑ5EÐFVÓ5WÓX€NØÐ!Ü)AÀ.×BTÑBTÐU_ÓB`Ó)aÐØ"Ð.Ü Ø'Ø ØØô	ð r.   c                 óˆ   — t        | t        «      r| S t        | t        «      r ddlm}  || «      }t        |t        «      r|S y)aŒ  Return ``value`` as a dict, parsing it from JSON if delivered as a string.

    Multipart/form-data and ``extra_body`` callers send ``litellm_metadata``
    as a JSON-encoded string; the proxy parses it into a dict later in
    ``add_litellm_data_to_request``, but the auth-time bouncer runs first
    and would otherwise miss the banned-param check on a still-stringified
    metadata blob.
    r   r   N)r?   rU   r@   Ú*litellm.litellm_core_utils.safe_json_loadsr   )rš   r   Úparseds      r,   r»   r»   î  s<   € ô �%œÔØˆÜ�%œÔÝNá'¨Ó.ˆÜ�fœdÔ#ØˆMØr.   Úrequest_dataÚroutec           	   ƒ   ó  K  — ddl m}m}m} t	        | ¬«      ƒ d{  –—†  t        ||||j                  dd«      ¬«       t        |j                  dd«      |j                  d	d
«      | ¬«      \  }}|st        t        j                  d|› d�¬«      ‚d|v rr|d   }|dur.t        j                  dt        j                  j                  «       ||vr6t        j                  d||«       t        t        j                  d|› d�¬«      ‚yy7 Œì­w)ax  
    1. Checks if request size is under max_request_size_mb (if set)
    2. Check if request body is safe (example user has not set api_base in request body)
    3. Check if IP address is allowed (if set)
    4. Check if request route is an allowed route on the proxy (if set)

    Returns:
    - True

    Raises:
    - HTTPException if request fails initial auth checks
    r   )rŒ   rX   Úpremium_user)r#   Nr4   r'   )r2   rŒ   rX   r4   r/   r$   F)r/   r$   r#   zAccess forbidden: IP address z not allowed.©Ústatus_codeÚdetailÚallowed_routesTz?Trying to set allowed_routes. This is an Enterprise feature. %sz!Route %s not in allowed_routes=%szAccess forbidden: Route z not allowed)Úlitellm.proxy.proxy_serverrŒ   rX   rÉ   Úcheck_if_request_size_is_saferÂ   r>   r1   r   r   ÚHTTP_403_FORBIDDENr   ÚerrorÚCommonProxyErrorsÚnot_premium_userrš   )	r#   rÆ   rÇ   rŒ   rX   rÉ   Úis_valid_ipÚpassed_in_ipÚ_allowed_routess	            r,   Úpre_db_read_auth_checksr×     s+  è ø€ ÷" VÑUô (°Ô
8×8Ð8ô Ø!Ø)ØØ×Ñ˜w¨Ó+õ	ô !0Ø$×(Ñ(¨¸Ó=Ø,×0Ñ0Ð1FÈÓNØô!Ñ€K�ñ ÜÜ×1Ñ1Ø2°<°.ÀÐNô
ð 	
ð Ð+Ñ+Ø!1Ð2BÑ!CˆØ˜tÑ#Ü ×&Ñ&ØQÜ!×2Ñ2×8Ñ8ôð ˜Ñ'Ü ×&Ñ&Ð'JÈEÐSbÔcÜÜ"×5Ñ5Ø1°%°¸ÐEôð ð (ð ,ð1 9ús   ‚DœD	�C-DÚcurrent_routec                 óð   — ddl m} ddlm}m} 	 |dury|€y|j                  dg «      }| |v ry|D ]  }|j                  | |¬«      sŒ y y# t        $ r }t        j                  d	|«       Y d}~yd}~ww xY w)
ay  
    Helper to check if the user defined public_routes on config.yaml

    Parameters:
    - current_route: str - the route the user is trying to call

    Returns:
    - bool - True if the route is defined in public_routes
    - bool - False if the route is not defined in public_routes

    Supports wildcard patterns (e.g., "/api/*" matches "/api/users", "/api/users/123")

    In order to use this the litellm config.yaml should have the following in general_settings:

    ```yaml
    general_settings:
        master_key: sk-1234
        public_routes: ["LiteLLMRoutes.public_routes", "/spend/calculate", "/api/*"]
    ```
    r   )ÚRouteChecks©rŒ   rÉ   TFNÚpublic_routes)rÇ   Úpatternz$route_in_additonal_public_routes: %s)
Úlitellm.proxy.auth.route_checksrÚ   rÎ   rŒ   rÉ   r>   Úroute_matches_wildcard_patternÚ	Exceptionr   rÑ   )rØ   rÚ   rŒ   rÉ   Úroutes_definedÚroute_patternrI   s          r,   Ú route_in_additonal_public_routesrã   =  s“   € õ* <ßIðØ˜tÑ#ØØÐ#Øà 0× 4Ñ 4°_ÀbÓ Iˆð ˜NÑ*Øð ,ò 	ˆMØ×9Ñ9ÀÐWdÐ9ÕeÙð	ð øÜò Ü×"Ñ"Ð#IÈ1ÔMÜûðús2   �A •A ˜A ¯A ÁA Á
A Á	A5ÁA0Á0A5c           	      óÌ  — 	 | j                   }t        |t        «      st        | j                  j
                  «      S t        |j                  d| j                  j
                  «      «      }t        |j                  d|j                  dd«      «      «      j                  d«      }t        |t        «      st        | j                  j
                  «      S |r-||k(  s|j                  |dz   «      r|t        |«      d }|xs dS |S # t        $ rT}t        j                  d|| j                  j
                  «       t        | j                  j
                  «      cY d}~S d}~ww xY w)aÚ  
    Resolve the request route from the ASGI scope, with ``root_path`` stripped.

    Prefer this over ``request.url.path`` for any auth, ACL, routing, or
    audit-log decision: Starlette reconstructs ``url.path`` by interpolating
    the Host header into a URL string and re-parsing with ``urlsplit``, so a
    malformed Host (e.g. ``localhost/?x=1``) collapses ``url.path`` to ``"/"``
    while FastAPI continues to dispatch on ``scope["path"]``. ``scope["path"]``
    is uvicorn's parse of the HTTP request line and matches the actual
    handler, so it's the authoritative route.

    Also normalizes sub-path deployments by stripping ``scope["root_path"]``
    e.g. ``/genai/chat/completions`` -> ``/chat/completions``.
    ÚpathÚapp_root_pathÚ	root_pathr'   r[   NzAerror on get_request_route: %s, defaulting to request.url.path=%s)Úscoper?   rU   r@   Úurlrå   r>   Úrstripr¯   Úlenrà   r   Údebug)r#   rè   Úraw_pathrç   ÚstrippedrI   s         r,   Úget_request_routerï   l  s"  € ð%Ø—}‘}ˆÜ˜%¤Ô&Ü�w—{‘{×'Ñ'Ó(Ð(Ü" 5§9¡9¨V°W·[±[×5EÑ5EÓ#FÓGˆÜ # E§I¡I¨o¸u¿y¹yÈÐVXÓ?YÓ$ZÓ [× bÑ bÐcfÓ gˆ	Ü˜(¤CÔ(Ü�w—{‘{×'Ñ'Ó(Ð(ñ
 ˜( iÒ/°8×3FÑ3FÀyÐSVÁÔ3WØ&¤s¨9£~Ð'7Ð8ˆHØ’?˜sÐ"ØˆøÜò %Ü×"Ñ"ØOÐQRÐT[×T_ÑT_×TdÑTdô	
ô �7—;‘;×#Ñ#Ó$Õ$ûð	%ús0   ‚:D ½BD Ã.D ÄD Ä	E#ÄA	EÅE#ÅE#c                 óþ   — 	 | j                   }t        |t        «      sy|j                  d«      }t	        |dd«      }t        |t
        «      r|r|S dS # t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)a  
    Return the low-cardinality route template, e.g.
    ``/v1/threads/{thread_id}/runs`` (vs. the literal path from
    ``get_request_route``). FastAPI sets ``scope["route"]`` before endpoint
    dependencies run. Returns None if unavailable (unmatched path, Mount).
    NrÇ   rå   z'error on get_request_route_template: %s)	rè   r?   rU   r>   rB   r@   rà   r   rì   )r#   rè   rÇ   ÚtemplaterI   s        r,   Úget_request_route_templaterò   ’  ss   € ð	Ø—}‘}ˆÜ˜%¤Ô&ØØ—y‘y Ó)ˆÜ! %¨°Ó6ˆÜ% h´Ô4¹ˆxÐKÀtÐKøÜò Ü×"Ñ"Ð#LÈaÔPÜûðús"   ‚A Ÿ1A ÁA Á	A<ÁA7Á7A<é   )Úmaxsizec                 ób   — g d¢}|D ]%  \  }}t        j                  ||| «      }|| k7  sŒ#|c S  | S )aä  
    Normalize request routes by replacing dynamic path parameters with placeholders.

    This prevents high cardinality in Prometheus metrics by collapsing routes like:
    - /v1/responses/1234567890 -> /v1/responses/{response_id}
    - /v1/threads/thread_123 -> /v1/threads/{thread_id}

    Args:
        route: The request route path

    Returns:
        Normalized route with dynamic parameters replaced by placeholders

    Examples:
        >>> normalize_request_route("/v1/responses/abc123")
        '/v1/responses/{response_id}'
        >>> normalize_request_route("/v1/responses/abc123/cancel")
        '/v1/responses/{response_id}/cancel'
        >>> normalize_request_route("/chat/completions")
        '/chat/completions'
    ))z3^(/(?:openai/)?v1/responses)/([^/]+)(/input_items)$ú\1/{response_id}\3)z.^(/(?:openai/)?v1/responses)/([^/]+)(/cancel)$rö   )z%^(/(?:openai/)?v1/responses)/([^/]+)$ú\1/{response_id})z$^(/responses)/([^/]+)(/input_items)$rö   )z^(/responses)/([^/]+)(/cancel)$rö   )z^(/responses)/([^/]+)$r÷   )zB^(/(?:openai/)?v1/threads)/([^/]+)(/runs)/([^/]+)(/steps)/([^/]+)$z%\1/{thread_id}\3/{run_id}\5/{step_id})z:^(/(?:openai/)?v1/threads)/([^/]+)(/runs)/([^/]+)(/steps)$ú\1/{thread_id}\3/{run_id}\5)z;^(/(?:openai/)?v1/threads)/([^/]+)(/runs)/([^/]+)(/cancel)$rø   )zH^(/(?:openai/)?v1/threads)/([^/]+)(/runs)/([^/]+)(/submit_tool_outputs)$rø   )z2^(/(?:openai/)?v1/threads)/([^/]+)(/runs)/([^/]+)$z\1/{thread_id}\3/{run_id})z*^(/(?:openai/)?v1/threads)/([^/]+)(/runs)$ú\1/{thread_id}\3)z6^(/(?:openai/)?v1/threads)/([^/]+)(/messages)/([^/]+)$z\1/{thread_id}\3/{message_id})z.^(/(?:openai/)?v1/threads)/([^/]+)(/messages)$rù   )z#^(/(?:openai/)?v1/threads)/([^/]+)$z\1/{thread_id})z9^(/(?:openai/)?v1/vector_stores)/([^/]+)(/files)/([^/]+)$z \1/{vector_store_id}\3/{file_id})z1^(/(?:openai/)?v1/vector_stores)/([^/]+)(/files)$ú\1/{vector_store_id}\3)z@^(/(?:openai/)?v1/vector_stores)/([^/]+)(/file_batches)/([^/]+)$z!\1/{vector_store_id}\3/{batch_id})z8^(/(?:openai/)?v1/vector_stores)/([^/]+)(/file_batches)$rú   )z)^(/(?:openai/)?v1/vector_stores)/([^/]+)$z\1/{vector_store_id})z&^(/(?:openai/)?v1/assistants)/([^/]+)$z\1/{assistant_id})z+^(/(?:openai/)?v1/files)/([^/]+)(/content)$z\1/{file_id}\3)z!^(/(?:openai/)?v1/files)/([^/]+)$z\1/{file_id})z,^(/(?:openai/)?v1/batches)/([^/]+)(/cancel)$z\1/{batch_id}\3)z#^(/(?:openai/)?v1/batches)/([^/]+)$z\1/{batch_id})z5^(/(?:openai/)?v1/fine_tuning/jobs)/([^/]+)(/events)$ú\1/{fine_tuning_job_id}\3)z5^(/(?:openai/)?v1/fine_tuning/jobs)/([^/]+)(/cancel)$rû   )z:^(/(?:openai/)?v1/fine_tuning/jobs)/([^/]+)(/checkpoints)$rû   )z,^(/(?:openai/)?v1/fine_tuning/jobs)/([^/]+)$z\1/{fine_tuning_job_id})z"^(/(?:openai/)?v1/models)/([^/]+)$z
\1/{model})rN   Úsub)rÇ   ÚpatternsrÝ   ÚreplacementÚ
normalizeds        r,   Únormalize_request_router   ¥  sH   € ò2N€Hðb !)ò Ñˆ�Ü—V‘V˜G [°%Ó8ˆ
Ø˜ÓØÒðð €Lr.   c              ƒ   óœ  K  — ddl m}m} |j                  dd«      }|��)|dur/t	        j
                  dt        j                  j                  «       y| j                  j                  d«      }|rat        |«      }t        |¬«      }t	        j                  d	|«       ||kD  r.t        d
|› d|› d�t        j                  j                  dd¬«      ‚y| j!                  «       ƒ d{  –—† }t#        |«      }t        |¬«      }	t	        j                  d|	«       |	|kD  r.t        d
|	› d|› d�t        j                  j                  dd¬«      ‚y7 Œe­w)a  
    Enterprise Only:
        - Checks if the request size is within the limit

    Args:
        request (Request): The incoming request.

    Returns:
        bool: True if the request size is within the limit

    Raises:
        ProxyException: If the request size is too large

    r   rÛ   Úmax_request_size_mbNTzRusing max_request_size_mb - not checking -  this is an enterprise only feature. %súcontent-length©Úbytes_valuez$content_length request size in MB=%sz+Request size is too large. Request size is ú MB. Max size is ú MBé�  ©ÚmessageÚtypeÚcoderR   z"request body request size in MB=%s)rÎ   rŒ   rÉ   r>   r   ÚwarningrÒ   rÓ   rš   r(   ÚintÚbytes_to_mbrì   ÚProxyExceptionÚProxyErrorTypesÚbad_request_errorr‹   rë   )
r#   rŒ   rÉ   r  Úcontent_lengthÚheader_sizeÚheader_size_mbr‹   Ú	body_sizeÚrequest_size_mbs
             r,   rÏ   rÏ     sk  è ø€ ÷ Jà!1×!5Ñ!5Ð6KÈTÓ!RÐàÑ&à˜tÑ#Ü ×(Ñ(ØdÜ!×2Ñ2×8Ñ8ôð ð !(§¡× 3Ñ 3Ð4DÓ EˆáÜ!$ ^Ó!4ˆKÜ$/¸KÔ$HˆNÜ ×&Ñ&Ð'MÈ~Ô^àÐ 3Ò3Ü$ØIÈ.ÐIYÐYjÐk~Ðjð  @Cð  DÜ(×:Ñ:×@Ñ@ØØ*ô	ð ð* ð !(§¡£×.ˆDÜ" 4›yˆIÜ%0¸YÔ%GˆOä ×&Ñ&Ð'KÈ_Ô]ØÐ!4Ò4Ü$ØIÈ/ÐIZÐZkÐlð  lAð  ADð  EÜ(×:Ñ:×@Ñ@ØØ*ô	ð ð ð /ús   ‚C"EÃ$E
Ã%A&EÚresponsec              ƒ   óz  K  — ddl m}m} |j                  dd«      }|�›|dur/t	        j
                  dt        j                  j                  «       yt        t        j                  | «      ¬«      }t	        j                  d|«       ||kD  r.t        d	|› d
|› d�t        j                  j                  dd¬«      ‚y­w)a   
    Enterprise Only:
        - Checks if the response size is within the limit

    Args:
        response (Any): The response to check.

    Returns:
        bool: True if the response size is within the limit

    Raises:
        ProxyException: If the response size is too large

    r   rÛ   Úmax_response_size_mbNTzSusing max_response_size_mb - not checking -  this is an enterprise only feature. %sr  zresponse size in MB=%sz-Response size is too large. Response size is r  r  r  r  r	  )rÎ   rŒ   rÉ   r>   r   r  rÒ   rÓ   rš   r  ÚsysÚ	getsizeofrì   r  r  r  )r  rŒ   rÉ   r  Úresponse_size_mbs        r,   Úcheck_response_size_is_safer  U  sÌ   è ø€ ÷  Jà"2×"6Ñ"6Ð7MÈtÓ"TÐØÐ'à˜tÑ#Ü ×(Ñ(ØeÜ!×2Ñ2×8Ñ8ôð ä"-¼#¿-¹-ÈÓ:QÔ"RÐÜ×"Ñ"Ð#;Ð=MÔNØÐ2Ò2Ü ØGÐHXÐGYÐYjÐkð  kAð  ADð  EÜ$×6Ñ6×<Ñ<ØØ&ô	ð ð ùs   ‚B9B;r  c                 ó   — | dz  S )z'
    Helper to convert bytes to MB
    i   © r  s    r,   r  r  ~  s   € ð ˜+Ñ&Ð&r.   Ú
model_namer˜   c           	      ó`  — ddl m} |€y|j                  | ¬«      }|syg }|D ]f  }|j                  di «      j                  |«      }|€Œ'	 t	        |t
        t        t        t        t        f«      r|j                  t        |«      «       Œh |rt        |«      S dS # t        t        f$ r Y Œ‰w xY w)ak  
    Return the minimum value of `field` across all deployments for model_name,
    or None if no deployment has the field set.

    When multiple deployments share the same model name, taking the minimum is
    the safest choice for load-balanced setups: it ensures no deployment is
    over-consumed regardless of which one actually serves a given request.
    r   )rX   N©r!  rº   )rÎ   rX   Úget_model_listr>   r?   r  Úfloatr@   ÚbytesÚ	bytearrayÚappendrD   Ú	TypeErrorÚmin)r!  r˜   rX   ÚdeploymentsÚlimitsÚ
deploymentÚraws          r,   Ú_get_deployment_default_limitr/  †  sµ   € õ 6àÐØØ#×2Ñ2¸jÐ2ÓI€KÙØØ€FØ!ò ˆ
Ø�n‰nÐ-¨rÓ2×6Ñ6°uÓ=ˆØ‰?ðÜ˜c¤C¬´´U¼IÐ#FÔGØ—M‘M¤# c£(Ô+øðñ !Œ3ˆv‹;Ð* dÐ*øô ¤	Ð*ò Ùðús   Á?BÂB-Â,B-c                 ó   — t        | d«      S )NÚdefault_api_key_rpm_limit©r/  r#  s    r,   Ú!_get_deployment_default_rpm_limitr3  ¢  ó   € Ü(¨Ð5PÓQÐQr.   c                 ó   — t        | d«      S )NÚdefault_api_key_tpm_limitr2  r#  s    r,   Ú!_get_deployment_default_tpm_limitr7  ¦  r4  r.   Úuser_api_key_dictc                 ó”  — | j                   r| j                   j                  d«      }|r|S | j                  rSi }| j                  j                  «       D ]0  \  }}t	        |t
        «      sŒ|j                  d«      €Œ)|d   ||<   Œ2 |r|S | j                  r| j                  j                  d«      }|�|S |�t        |«      }|�||iS y)a*  
    Get the model rpm limit for a given api key.

    Priority order (returns first found):
    1. Key metadata (model_rpm_limit)
    2. Key model_max_budget (rpm_limit per model)
    3. Team metadata (model_rpm_limit)
    4. Deployment default_api_key_rpm_limit (when model_name is provided)
    Úmodel_rpm_limitÚ	rpm_limitN)rd   r>   Úmodel_max_budgetÚitemsr?   rU   Úteam_metadatar3  )r8  r!  Úresultr:  r4   ÚbudgetÚ
team_limitÚdefault_limits           r,   Úget_key_model_rpm_limitrC  ª  óä   € ð ×!Ò!Ø)×2Ñ2×6Ñ6Ð7HÓIˆÙØˆMð ×)Ò)Ø13ˆØ.×?Ñ?×EÑEÓGò 	=‰MˆE�6Ü˜&¤$Õ'¨F¯J©J°{Ó,CÑ,OØ)/°Ñ)<� Ò&ð	=ñ Ø"Ð"ð ×&Ò&Ø-×;Ñ;×?Ñ?Ð@QÓRˆ
ØÐ!ØÐð ÐÜ@ÀÓLˆØÐ$Ø Ð.Ð.àr.   c                 ó”  — | j                   r| j                   j                  d«      }|r|S | j                  rSi }| j                  j                  «       D ]0  \  }}t	        |t
        «      sŒ|j                  d«      €Œ)|d   ||<   Œ2 |r|S | j                  r| j                  j                  d«      }|�|S |�t        |«      }|�||iS y)a*  
    Get the model tpm limit for a given api key.

    Priority order (returns first found):
    1. Key metadata (model_tpm_limit)
    2. Key model_max_budget (tpm_limit per model)
    3. Team metadata (model_tpm_limit)
    4. Deployment default_api_key_tpm_limit (when model_name is provided)
    Úmodel_tpm_limitÚ	tpm_limitN)rd   r>   r<  r=  r?   rU   r>  r7  )r8  r!  r?  rF  r4   r@  rA  rB  s           r,   Úget_key_model_tpm_limitrH  Õ  rD  r.   Údefault_estimated_output_tokensÚESTIMATED_OUTPUT_TOKENS_FIELDÚ)default_estimated_output_tokens_per_modelÚ'ESTIMATED_OUTPUT_TOKENS_PER_MODEL_FIELDÚ'ESTIMATED_OUTPUT_TOKENS_METADATA_FIELDSÚ _ESTIMATED_OUTPUT_TOKENS_ADAPTERÚ*_ESTIMATED_OUTPUT_TOKENS_PER_MODEL_ADAPTERr.  c                 ó–   — | €y	 t         j                  | «      S # t        $ r%}t        j                  dt
        |«       Y d}~yd}~ww xY w)z=Coerce one declared estimate to a positive int, or ignore it.Nú%Ignoring malformed %s in metadata: %s)rN  Úvalidate_pythonr   r   r  rJ  ©r.  Úvalidation_errors     r,   Ú _validated_output_token_estimaterU  
  sL   € à
€{ØðÜ/×?Ñ?ÀÓDÐDøÜò Ü×$Ñ$Ø3Ü)Øô	
ô
 ûðúó   … š	A£AÁAc                 ó–   — | €y	 t         j                  | «      S # t        $ r%}t        j                  dt
        |«       Y d}~yd}~ww xY w)z7Coerce a declared per-model estimate map, or ignore it.NrQ  )rO  rR  r   r   r  rL  rS  s     r,   Ú+_validated_output_token_estimates_per_modelrX    sL   € à
€{ØðÜ9×IÑIÈ#ÓNÐNøÜò Ü×$Ñ$Ø3Ü3Øô	
ô
 ûðúrV  rd   c                 óà   — | rt         j                  | «      ry|�7t        | j                  t        «      «      }|�|j                  |«      nd}|�|S t        | j                  t        «      «      S )zÜResolve the per-model, then global, estimate out of one metadata blob.

    The two fields are validated independently so a malformed per-model map
    cannot discard a valid global estimate, or the other way round.
    N)rM  Ú
isdisjointrX  r>   rL  rU  rJ  )rd   r!  Ú	per_modelÚper_model_estimates       r,   Ú&_estimated_output_tokens_from_metadatar]  (  sq   € ñ Ô>×IÑIÈ(ÔSØàÐÜFØ�L‰LÔ@ÓAó
ˆ	ð BKÐAV I§M¡M°*Ô$=Ð\`ÐØÐ)Ø%Ð%ä+¨H¯L©LÔ9VÓ,WÓXÐXr.   c                 ób   — t        | j                  |«      }|�|S t        | j                  |«      S )aî  Resolve the operator-declared output-token estimate for TPM reservation.

    Priority order (returns first found):
    1. Key metadata ``default_estimated_output_tokens_per_model[model_name]``
    2. Key metadata ``default_estimated_output_tokens``
    3. Team metadata ``default_estimated_output_tokens_per_model[model_name]``
    4. Team metadata ``default_estimated_output_tokens``

    Returns ``None`` when nothing is configured, which leaves the static
    heuristic floor in place.
    )r]  rd   r>  )r8  r!  Úkey_estimates      r,   Úget_estimated_output_tokensr`  ?  s:   € ô AÐAR×A[ÑA[Ð]gÓh€LØÐØÐÜ1Ð2C×2QÑ2QÐS]Ó^Ð^r.   c                   ó”   — e Zd ZdZedeeef   dz  fd„«       Zede	dz  fd„«       Z
edeee	f   dz  fd„«       Zedee   fd„«       Zy)ÚOutputTokenEstimateRequestz The shape of any management request that can carry an output-token estimate.

    Read-only members: the gate inspects a request, it never writes one back.
    r%   Nc                  ó   — y rŸ   r   ©Úselfs    r,   rd   z#OutputTokenEstimateRequest.metadataZ  ó   € Ø7:r.   c                  ó   — y rŸ   r   rd  s    r,   rI  z:OutputTokenEstimateRequest.default_estimated_output_tokens]  s   € Ø=@r.   c                  ó   — y rŸ   r   rd  s    r,   rK  zDOutputTokenEstimateRequest.default_estimated_output_tokens_per_model`  s   € ØUXr.   c                  ó   — y rŸ   r   rd  s    r,   Úmodel_fields_setz+OutputTokenEstimateRequest.model_fields_setc  ó   € Ø36r.   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Úpropertyr   r@   Úobjectrd   r  rI  rK  r   rj  r   r.   r,   rb  rb  T  sy   „ ñð
 Ø:˜' # v +Ñ.°Ñ5Ò:ó Ø:àØ@°°t±Ò@ó Ø@àØX¸7À3ÈÀ8Ñ;LÈtÑ;SÒXó ØXàØ6 *¨S¡/Ò6ó Ù6r.   rb  ÚdataÚexisting_metadatac                 óø   — d| j                   v r| j                  xs i n|}| j                  �| j                  n|j                  t        «      | j
                  �| j
                  fS |j                  t        «      fS )aˆ  The output-token estimates this request would leave stored on the entity.

    Mirrors how the management endpoints merge metadata: a supplied ``metadata``
    replaces the stored blob wholesale, an omitted one preserves it, and the
    dedicated top-level fields overlay whatever survives. Both sources are read
    because the same declaration reaches the same stored field either way.
    rd   )rj  rd   rI  r>   rJ  rK  rL  )rr  rs  Úbases      r,   Ú!_requested_output_token_estimatesrv  g  s…   € ð ",¨t×/DÑ/DÑ!Dˆ�‰Ò	™"ÐJ[ð 	ð
 ×/Ñ/Ð;ð 	×,Ò,à�X‰XÔ3Ó4à×9Ñ9ÐEð 	×6Ñ6ð	ð ð �X‰XÔ=Ó>ðð r.   Úentity)r¶   Úteamc                 ó  — |j                   t        j                  j                  k(  ry|xs i }t	        | |«      |j                  t        «      |j                  t        «      fk(  ryt        dddt        › dt        › d|› d�i¬«      ‚)	at  Only a proxy admin may change what a key or team declares its models emit.

    That declaration is what the TPM limiter reserves for a request omitting
    ``max_tokens``, so lowering or clearing it under-reserves against every
    window the request is charged against, including the team and organization
    ones the writer may not own. A key's metadata is writable by its holder and
    a team's by its team admin, so neither is a trustworthy source for a value
    that weakens a limit set above them. Gated on the resulting value rather
    than on presence, so a form resending the stored declaration stays a no-op.
    Né“  rÑ   úOnly proxy admins can set z or ú on a zc. They decide how many output tokens the rate limiter reserves for a request that omits max_tokens.rÊ   )	Ú	user_roleÚLitellmUserRolesÚPROXY_ADMINrš   rv  r>   rJ  rL  r   )rr  rs  r8  rw  Ústoreds        r,   Ú-enforce_output_token_estimates_are_admin_onlyr�    sœ   € ð  ×"Ñ"Ô&6×&BÑ&B×&HÑ&HÒHØØ*;Ò*A¸r€FÜ(¨¨vÓ6Ø�
‰
Ô0Ó1Ø�
‰
Ô:Ó;ð;ò ð 	Ü
ØàÐ1Ô2OÐ1PÐPTÜ6Ð7°v¸f¸Xð FMðMð
ôð r.   c                   óR   — e Zd ZdZedeeef   dz  fd„«       Zede	e   fd„«       Z
y)ÚBatchEnqueuedTokenLimitRequestzPThe shape of any management request that can carry a batch enqueued-token limit.r%   Nc                  ó   — y rŸ   r   rd  s    r,   rd   z'BatchEnqueuedTokenLimitRequest.metadata¤  rf  r.   c                  ó   — y rŸ   r   rd  s    r,   rj  z/BatchEnqueuedTokenLimitRequest.model_fields_set§  rk  r.   )rl  rm  rn  ro  rp  r   r@   rq  rd   r   rj  r   r.   r,   rƒ  rƒ  ¡  s=   „ ÙZàØ:˜' # v +Ñ.°Ñ5Ò:ó Ø:àØ6 *¨S¡/Ò6ó Ù6r.   rƒ  c           	      ó:  — |j                   t        j                  j                  k(  ry|xs t        }d| j
                  v r| j                  xs t        n|}|j                  t        «      |j                  t        «      k(  ryt        dddt        › d|› d�i¬«      ‚)	aw  Only a proxy admin may change a key or team's batch enqueued-token limit.

    When set, ``batch_enqueued_token_limit`` replaces the standard RPM/TPM checks
    for batch submissions, so a holder-writable copy would let a caller lift their
    own batch quota. Gated on the resulting value rather than on presence, so a
    form resending the stored value stays a no-op.
    Nrd   rz  rÑ   r{  r|  zC. It replaces the standard rate limit checks for batch submissions.rÊ   )
r}  r~  r  rš   r   rj  rd   r>   r   r   )rr  rs  r8  rw  r€  Ú	requesteds         r,   Ú0enforce_batch_enqueued_token_limit_is_admin_onlyrˆ  «  s¥   € ð ×"Ñ"Ô&6×&BÑ&B×&HÑ&HÒHØØ*;Ò*L¼}€Fà,6¸$×:OÑ:OÑ,Oˆ�‰Ò	'�-ÐU[ð ð ‡}�}Ô<Ó=ÀÇÁÔLsÓAtÒtØÜ
ØàÐ1Ô2YÐ1ZÐZ`ÐagÐ`hð iPð Pð
ôð r.   Úmetadata_accessor_key)r>  Úorganization_metadataÚproject_metadataÚrate_limit_key)r:  rF  Úmodel_itpm_limitÚmodel_otpm_limitc                 óR   — t        | |«      rt        | |«      j                  |«      S y rŸ   )rB   r>   )r8  r‰  rŒ  s      r,   Ú"get_model_rate_limit_from_metadatar�  É  s-   € ô
 Ð Ð"7Ô8ÜÐ(Ð*?Ó@×DÑDÀ^ÓTÐTØr.   c                 óR   — | j                   r| j                   j                  d«      S y ©Nr:  ©r>  r>   ©r8  s    r,   Úget_team_model_rpm_limitr•  Ó  ó(   € ð ×&Ò&Ø ×.Ñ.×2Ñ2Ð3DÓEÐEØr.   c                 óR   — | j                   r| j                   j                  d«      S y ©NrF  r“  r”  s    r,   Úget_team_model_tpm_limitr™  Û  r–  r.   c                 ó°   — | j                   r| j                   j                  d«      }|�|S | j                  r| j                  j                  d«      }|�|S y)a  
    Get the per-MCP-server rpm limit for a given api key.

    Priority order (returns first found):
    1. Key metadata (mcp_rpm_limit)
    2. Team metadata (mcp_rpm_limit)

    The returned dict is keyed by MCP server name (alias if set, else the
    configured server name).
    Úmcp_rpm_limitN)rd   r>   r>  )r8  r?  rA  s      r,   Úget_key_mcp_rpm_limitrœ  ã  s\   € ð ×!Ò!Ø)×2Ñ2×6Ñ6°ÓGˆØÐØˆMà×&Ò&Ø-×;Ñ;×?Ñ?ÀÓPˆ
ØÐ!ØÐàr.   c                 óR   — | j                   r| j                   j                  d«      S y )Nr›  r“  r”  s    r,   Úget_team_mcp_rpm_limitrž  ý  s'   € ð ×&Ò&Ø ×.Ñ.×2Ñ2°?ÓCÐCØr.   c                 óR   — | j                   r| j                   j                  d«      S y)zÉ
    Get the per-request-tag rpm limit configured on a given api key.

    The returned dict is keyed by request tag, so each tag/group tracked on
    the key gets its own independent RPM counter.
    Útag_rpm_limitN)rd   r>   r”  s    r,   Úget_key_tag_rpm_limitr¡    s'   € ð ×!Ò!Ø ×)Ñ)×-Ñ-¨oÓ>Ð>Ør.   c                 óR   — | j                   r| j                   j                  d«      S y r’  ©r‹  r>   r”  s    r,   Úget_project_model_rpm_limitr¤    ó(   € ð ×)Ò)Ø ×1Ñ1×5Ñ5Ð6GÓHÐHØr.   c                 óR   — | j                   r| j                   j                  d«      S y r˜  r£  r”  s    r,   Úget_project_model_tpm_limitr§    r¥  r.   Úcustom_auth_configuredÚrun_common_checksc                 ó   — | r|ry 	 y)Na¤  custom_auth is configured but 'custom_auth_run_common_checks' is not set. Problem: budgets, model-access allowlists, and per-model rate limits configured on your DB team/project records will NOT be enforced for custom-auth requests (rate limits set directly on the returned UserAPIKeyAuth still apply). Fix: set 'general_settings.custom_auth_run_common_checks: true'. Docs: https://docs.litellm.ai/docs/proxy/custom_authr   ©r¨  r©  s     r,   Ú!custom_auth_common_checks_warningr¬  #  s   € ñ
 "Ñ%6Øð	?ðr.   )Úloggerr­  c                 óX   — t         ry t        | |¬«      }|€y |j                  |«       da y )Nr«  T)Ú*_custom_auth_common_checks_warning_emittedr¬  r  )r¨  r©  r­  r
  s       r,   Ú,warn_once_if_custom_auth_skips_common_checksr°  7  s6   € õ 2ØÜ6Ø5Ø+ô€Gð €ØØ
‡N�N�7ÔØ15Ñ.r.   c                 ó$   — dg}|D ]  }|| v sŒ y y)Nz	vertex-aiTFr   )rÇ   Ú%PROVIDER_SPECIFIC_PASS_THROUGH_ROUTESr¹   s      r,   Úis_pass_through_provider_router³  J  s-   € àð4Ð)ð
 8ò ˆØ�UŠ?Ùðð r.   c                  ó2  — t        j                  dd«      } t        j                  dd«      }t        j                  dd«      }t        j                  dd«      }t        j                  dd«      }| duxs$ |duxs |duxs t        |«      xs t        |«      S )u$  
    Check if the user has set up single sign-on (SSO).

    Covers OAuth providers (Microsoft, Google, generic) and SAML IdP metadata.
    Used by UI discovery (``sso_configured``) so the login button enables when
    any supported SSO path is configured â€” including SAML-only setups.
    ÚMICROSOFT_CLIENT_IDNÚGOOGLE_CLIENT_IDÚGENERIC_CLIENT_IDÚSAML_IDP_METADATA_URLÚSAML_IDP_METADATA_XML)ÚosÚgetenvÚbool)Úmicrosoft_client_idÚgoogle_client_idÚgeneric_client_idÚsaml_idp_metadata_urlÚsaml_idp_metadata_xmls        r,   Ú_has_user_setup_ssorÂ  W  s§   € ô "$§¡Ð+@À$Ó!GÐÜ Ÿi™iÐ(:¸DÓAÐÜ!Ÿy™yÐ)<¸dÓCÐÜ#%§9¡9Ð-DÀdÓ#KÐÜ#%§9¡9Ð-DÀdÓ#KÐð 	 4Ð'ò 	'Ø 4Ð'ò	'à DÐ(ò	'ô Ð%Ó&ò	'ô Ð%Ó&ðr.   c                 ó€  — | syt        | t        «      r| n| g}g }|D ]›  }t        |t        «      sŒ|j                  d«      }|j                  d«      }|�|sŒ;t	        |«      j                  «       t	        t        j                  «      j                  «       k(  sŒ}|j                  |j                  «       «       Œ� |r|S y)zDReturn the header_name mapped to CUSTOMER role, if any (dict-based).NÚlitellm_user_roleÚheader_name)	r?   r    rU   r>   r@   r®   r~  ÚCUSTOMERr(  )Úuser_id_mappingr=  Úcustomer_headers_mappingsrV   ÚrolerÅ  s         r,   Ú%get_customer_user_header_from_mappingrÊ  n  s³   € áØÜ&0°Ä$Ô&G‘?ÈoÐM^€EØ')ÐØò BˆÜ˜$¤Ô%ØØ�x‰xÐ+Ó,ˆØ—h‘h˜}Ó-ˆØˆ<™{ØÜˆt‹9�?‰?Ó¤Ô$4×$=Ñ$=Ó >× DÑ DÓ FÓFØ%×,Ñ,¨[×->Ñ->Ó-@ÕAðBñ !Ø(Ð(àr.   Úrequest_headersc                 óº   — | €yt         D ]O  }| j                  «       D ]:  \  }}|j                  «       |j                  «       k(  sŒ(t        |«      }|sŒ6|c c S  ŒQ y)av  
    Check standard customer ID headers for a customer/end-user ID.

    This enables tools like Claude Code to pass customer IDs via ANTHROPIC_CUSTOM_HEADERS.
    No configuration required - these headers are always checked.

    Args:
        request_headers: The request headers dict

    Returns:
        The customer ID if found in standard headers, None otherwise
    N)r   r=  r®   Ú_coerce_user_id_to_str)rË  Ústandard_headerrÅ  Úheader_valueÚuser_id_strs        r,   Ú&_get_customer_id_from_standard_headersrÑ  „  sk   € ð ÐØä7ò 'ˆØ)8×)>Ñ)>Ó)@ò 	'Ñ%ˆK˜Ø× Ñ Ó" o×&;Ñ&;Ó&=Ó=Ü4°\ÓB�ÚØ&Ô&ñ		'ð'ð r.   c                 ó*  — | €yt        | t        «      ryt        | t        t        f«      rt	        | «      S t        | t        «      rN| j                  «       }|syt        j                  r)|dd dv r"t        |«      }t        |t        t        f«      ry|S y)u5  Return a usable end-user identifier string, or None if the value isn't one.

    Always drops non-string structured values (dict/list/tuple/set) because
    stringifying them produces garbage spend-log rows like
    ``"{'device_id': ...}"``. Strings that *decode* to a structured payload
    are only rejected when ``litellm.validate_end_user_id_in_db`` is enabled
    â€” operators who currently pass JSON-encoded identifiers keep their
    existing behavior until they opt in. See
    auth_utils.py:get_end_user_id_from_request_body for the extraction chain.
    Nr\   )ú{rµ   )r?   r¼  r  r%  r@   rA   rC   Úvalidate_end_user_id_in_dbr   rU   r    )rš   rî   rÅ   s      r,   rÍ  rÍ  Ÿ  s‡   € ð €}ØÜ�%œÔàÜ�%œ#œu˜Ô&Ü�5‹zÐÜ�%œÔØŸ+™+›-ˆÙØô
 ×-Ò-°(¸2¸A°,À*Ñ2LÜ+¨HÓ5ˆFÜ˜&¤4¬ ,Ô/ØØˆàr.   c                 óÜ  — ddl m} t        |¬«      }|�|S |��d }|j                  dd «      }|rt	        |«      }|s8d}|j                  |«      }t        |t        «      r|j                  «       dk7  r|}t        |t        «      rY|j                  «       D ��	ci c]  \  }}	|j                  «       |	“Œ }
}}	|D ]"  }t        |
j                  |«      «      }|sŒ |c S  n[t        |t        «      rK|j                  «       D ]8  \  }}|j                  «       |j                  «       k(  sŒ(t        |«      }|sŒ6|c S  d| v rt        | d   «      }|r|S dt        d	t        fd
„} || j                  d«      «      }t        |j                  d«      «      }|r|S  || j                  d«      «      }t        |j                  d«      «      }|r|S t        | j                  d«      «      }|r|S y c c}	}w )Nr   )rŒ   )rË  Úuser_header_mappingsÚuser_header_namer'   Úuserrš   r%   c                 óˆ   — t        | t        «      r| S t        | t        «      rt        | «      }t        |t        «      r|S i S i S rŸ   )r?   rU   r@   r   )rš   rÅ   s     r,   Ú_as_dictz3get_end_user_id_from_request_body.<locals>._as_dictó  s@   € ô �eœTÔ"ØˆLÜ�eœSÔ!Ü+¨EÓ2ˆFÜ'¨´Ô5�6Ð=¸2Ð=Øˆ	r.   re   rd   Úuser_idÚsafety_identifier)rÎ   rŒ   rÑ  r>   rÊ  r?   r@   rA   r    r=  r®   rÍ  r   rU   )r2   rË  rŒ   Úcustomer_idÚcustom_header_name_to_checkrÇ  Úuser_id_header_config_keyrš   Úkr¢   Úheaders_lowerÚexpected_headerrÐ  rÅ  rÏ  rÚ  re   Úmetadata_dicts                     r,   Ú!get_end_user_id_from_request_bodyrä  Â  s  € õ <ô @ÐP_Ô`€KØÐØÐð
 Ñ"Ø9=Ð#ð "2×!5Ñ!5Ð6LÈdÓ!SˆÙÜ*OÐP_Ó*`Ð'ñ +Ø/AÐ%Ø+×/Ñ/Ð0IÓJˆEÜ˜%¤Ô%¨%¯+©+«-¸2Ò*=Ø.3Ð+ô Ð1´4Ô8Ø=L×=RÑ=RÓ=T×#U±T°Q¸ A§G¡G£I¨q¡LÐ#UˆMÑ#UØ#>ò '�Ü4°]×5FÑ5FÀÓ5WÓX�ÚØ&Ò&ñ'ô
 Ð3´SÔ9Ø-<×-BÑ-BÓ-Dò +Ñ)�˜\Ø×$Ñ$Ó&Ð*E×*KÑ*KÓ*MÓMÜ"8¸Ó"F�KÚ"Ø*Ò*ð	+ð �ÑÜ,¨\¸&Ñ-AÓBˆÙØÐð	œð 	¤ó 	ñ ' |×'7Ñ'7Ð8JÓ'KÓLÐÜ(Ð)9×)=Ñ)=¸fÓ)EÓF€KÙØÐñ $ L×$4Ñ$4°ZÓ$@ÓA€MÜ(¨×):Ñ):¸9Ó)EÓF€KÙØÐô )¨×)9Ñ)9Ð:MÓ)NÓO€KÙØÐàùóg $Vs   ÂG(zx-litellm-modelÚMODEL_ROUTING_HEADER_NAME)ú/filesú/batchesú/vector_storesú/skillsú/evalsú/fine_tuningz/videosÚ_MODEL_ROUTING_ROUTE_MARKERS)ræ  rç  ré  rê  Ú,_MODEL_ROUTING_HEADER_OR_QUERY_ROUTE_MARKERS)ræ  rç  rë  Ú/_MODEL_ROUTING_QUERY_TARGET_MODEL_ROUTE_MARKERS)ræ  rç  rè  Ú._MODEL_ROUTING_BODY_TARGET_MODEL_ROUTE_MARKERS)rê  Ú-_MODEL_ROUTING_COMPLETION_MODEL_ROUTE_MARKERS)z/realtime/client_secretsz/realtime/callsÚ*_MODEL_ROUTING_SESSION_MODEL_ROUTE_MARKERS)Úfile_idÚinput_file_idÚoutput_file_idÚerror_file_idÚbatch_idÚfine_tuning_job_idÚtraining_fileÚvalidation_fileÚvector_store_idÚvideo_idÚcharacter_idÚ_MODEL_ROUTING_ID_FIELDSÚ
candidatesc                 óF  — |€y t        |t        t        t        f«      r|n|g}|D ]t  }|€Œt        |t        «      r-|j                  d«      D �cg c]  }|j                  «       ‘Œ }}nt	        |«      j                  «       g}| j                  d„ |D «       «       Œv y c c}w )Nú,c              3   ó&   K  — | ]	  }|sŒ|–— Œ y ­wrŸ   r   )r¡   r4   s     r,   r£   z+_append_model_candidates.<locals>.<genexpr>T  s   è ø€ ÒB EºEœ%ÑBùs   ‚Š)r?   r    r¥   Úsetr@   r^   rA   Úextend)rþ  rš   r¦   rV   r4   Úmodel_namess         r,   Ú_append_model_candidatesr  H  s”   € Ø€}Øä'¨´´e¼SÐ/AÔB‘EÈÈ€FØò CˆØˆ<ØÜ�dœCÔ Ø6:·j±jÀ³oÖF¨U˜5Ÿ;™;�=ÐFˆKÑFä˜t›9Ÿ?™?Ó,Ð-ˆKØ×ÑÑB¨[ÔBÕBñCùò Gs   ÁBc                 óD   — g }| D ]  }||vsŒ|j                  |«       Œ |S rŸ   )r(  )rþ  Údedupedr4   s      r,   Ú_dedupe_model_candidatesr  W  s1   € Ø "€GØò "ˆØ˜ÒØ�N‰N˜5Õ!ð"ð €Nr.   Úmappingr¶   c                 ó¬   — | sy || v r| |   S |j                  «       }| j                  «       D ]%  \  }}t        |«      j                  «       |k(  sŒ#|c S  y rŸ   )r®   r=  r@   )r	  r¶   Ú	key_lowerÚmapping_keyrš   s        r,   Ú#_get_case_insensitive_mapping_valuer  _  s^   € ÙØØ
ˆg�~Ø�s‰|ÐØ—y‘y“{€IØ%Ÿm™m›oò Ñˆ�UÜˆ{Ó×!Ñ!Ó# yÓ0ØŠLðð r.   Úmarkersc                 óL   ‡— | j                  «       Št        ˆfd„|D «       «      S )Nc              3   ó&   •K  — | ]  }|‰v –— Œ
 y ­wrŸ   r   )r¡   ÚmarkerÚnormalized_routes     €r,   r£   z,_route_matches_any_marker.<locals>.<genexpr>m  s   øè ø€ Ò@¨fˆvÐ)Ô)Ñ@ùs   ƒ)r®   r¼   )rÇ   r  r  s     @r,   Ú_route_matches_any_markerr  k  s    ø€ Ø#Ÿk™k›mÐÜÓ@¸Ô@Ó@Ð@r.   c                 ó$   — t        | t        ¬«      S )N©rÇ   r  )r  rì  ©rÇ   s    r,   Ú!_route_uses_model_routing_sourcesr  p  s   € Ü$¨5Ô:VÔWÐWr.   Úresource_idÚresource_id_fieldc                 óæ  — t        | t        «      r| sg S g }	 ddlm}m}m}m} t        | || «      ¬«        || «      }|r0t        | ||«      ¬«       t        |t         ||«      |«      ¬«       	 ddlm}
  |
| «      }|rBt        |t        |j                  d«      |«      ¬«       t        ||j                  d«      ¬«       |d	v rv	 dd
lm}m} |dk(  r/ || «      j                  d«      }t        |t        ||«      ¬«       n. || «      j                  d«      }t        |t        ||«      ¬«       t%        |«      S t%        |«      S # t        $ r*}	t        j                  dt        |	«      «       Y d }	~	�Œd }	~	ww xY w# t        $ r)}	t        j                  dt        |	«      «       Y d }	~	Œèd }	~	ww xY w# t        $ r3}	t        j                  dt        |	«      «       Y d }	~	t%        |«      S d }	~	ww xY w)Nr   )Ú"_is_base64_encoded_unified_file_idÚdecode_model_from_file_idÚ"get_model_id_from_unified_batch_idÚget_models_from_unified_file_id)rþ  rš   z6Unable to extract model from managed file/batch ID: %s)Úparse_unified_idÚmodel_idÚtarget_model_namesz<Unable to extract model from unified managed resource ID: %s)rû  rü  )Ú!decode_character_id_with_providerÚdecode_video_id_with_providerrû  z;Unable to extract model from managed video/character ID: %s)r?   r@   Ú1litellm.proxy.openai_files_endpoints.common_utilsr  r  r  r  r  Ú_resolve_model_id_with_routerrà   r   rì   Ú-litellm.llms.base_llm.managed_resources.utilsr  r>   Úlitellm.types.videos.utilsr"  r#  r  )r  r  rX   rþ  r  r  r  r  Úunified_file_idrI   r  Ú	parsed_idr"  r#  r   s                  r,   Ú(_extract_models_from_managed_resource_idr*  t  së  € ô
 �k¤3Ô'©{Øˆ	à#%€Jðe÷	
ó 	
ô 	!¨JÑ>WÐXcÓ>dÕeÙ!CÀKÓ!PˆÙÜ$Ø%Ù5°oÓFõô %Ø%Ü3Ñ4VÐWfÓ4gÐisÓtõðkÝRá+¨KÓ8ˆ	ÙÜ$Ø%Ü3°I·M±MÀ*Ó4MÈzÓZõô %°
À)Ç-Á-ÐPdÓBeÕfð Ð8Ñ8ð	n÷ð
 ! JÒ.Ù8¸ÓE×IÑIÈ*ÓU�Ü(Ø)Ü7¸À*ÓMöñ
 =¸[ÓI×MÑMÈjÓY�Ü(Ø)Ü7¸À*ÓMõô $ JÓ/Ð/Ô# JÓ/Ð/øôM ò eÜ×"Ñ"Ð#[Ô]`ÐabÓ]c×dÒdûðeûô ò kÜ×"Ñ"Ð#aÔcfÐghÓci×jÑjûðkûô, ò 	nÜ ×&Ñ&Ð'dÔfiÐjkÓfl×mÐmä# JÓ/Ð/ûð	núsJ   ˜AE	 Á2AE? Ã	A*F4 Å		E<ÅE7Å7E<Å?	F1ÆF,Æ,F1Æ4	G0Æ=G+Ç+G0r   c                 ó¨   — | �|€| S 	 |j                  | «      xs | S # t        $ r+}t        j                  dt	        |«      «       | cY d }~S d }~ww xY w)Nz7Unable to resolve model_id from managed resource ID: %s)Ú resolve_model_name_from_model_idrà   r   rì   r@   )r   rX   rI   s      r,   r%  r%  º  sZ   € ØÐ˜:Ð-ØˆðØ×:Ñ:¸8ÓDÒPÈÐPøÜò Ü×"Ñ"Ð#\Ô^aÐbcÓ^dÔeØ�ûðús   ˆ �	A¦ AÁAÁAÚrequest_query_paramsc           	      óH  — g }t        |¬«      }t        |t        ¬«      }t        |t        ¬«      }t        |t        ¬«      }	t        |t
        ¬«      }
| j                  d«      }t        ||«       |	s|st        || j                  d«      «       t        |t        ¬«      r<| j                  d«      }t        |t        «      rt        ||j                  d«      «       |
r=t        | j                  d«      t        «      rt        || d   j                  d«      «       |r||r0t        |t        |d«      «       t        |t        |t        «      «       |rt        |t        |d«      «       t        D ])  }t        |t        | j                  |«      ||¬«      «       Œ+ t        |«      S )Nr  r  r4   r!  ÚsessionÚ
completion)r  rX   )r  r  rí  rî  rï  rð  r>   r  rñ  r?   rU   r  rå  rý  r*  r  )rÆ   rÇ   rË  r-  rX   rþ  Úuses_model_routing_sourcesÚ"uses_header_or_query_model_sourcesÚuses_query_target_model_sourcesÚuses_body_target_model_sourcesÚuses_completion_model_sourcesÚ
body_modelr/  r˜   s                 r,   Ú&_extract_model_candidates_from_requestr7  Ä  s˜  € ð $&€JÜ(IÐPUÔ(VÐÜ0IØÔIô1Ð&ô .GØÔLô.Ð#ô -FØÔKô-Ð"ô ,EØÔJô,Ð!ð %×(Ñ(¨Ó1€JÜ˜Z¨Ô4Ù%©ZÜ  ¨\×-=Ñ-=Ð>RÓ-SÔTÜ  uÔ6`ÕaØ%×)Ñ)¨)Ó4ˆÜ�gœtÔ$Ü$ Z°·±¸WÓ1EÔFÙ$¬°L×4DÑ4DÀ\Ó4RÔTXÔ)YÜ  ¨\¸,Ñ-G×-KÑ-KÈGÓ-TÔUá!Ù-Ü$ØÜ3Ð4HÈ'ÓRôô %ØÜ3°OÔE^Ó_ôñ +Ü$ØÜ3Ð4HÐJ^Ó_ôô
 .ò 	ˆEÜ$ØÜ8Ø ×$Ñ$ UÓ+Ø&+Ø)ôõð	ô $ JÓ/Ð/r.   c                 ó2   — | sy t        | «      dk(  r| d   S | S )Nr\   r   )rë   )rþ  s    r,   Ú_format_model_candidatesr9    s&   € ñ ØÜ
ˆ:ƒ˜!ÒØ˜!‰}ÐØÐr.   c                 óŒ   — | €yt        | dd«      }t        |t        «      sy|j                  d«      }t        |t        d«      du S )aø  Whether FastAPI resolved this request to a user-defined pass-through handler.

    Reads the marker set by ``create_pass_through_route`` off the dispatched endpoint
    (``request.scope["endpoint"]``). Because routing has already run by the time auth
    dependencies execute, this reflects the handler that actually serves the request:
    a custom path colliding with a built-in route resolves to the built-in handler,
    which carries no marker, so model-access checks are never wrongly skipped.
    NFrè   ÚendpointT)rB   r?   rU   r>   r    )r#   rè   r;  s      r,   Ú+request_dispatched_to_pass_through_endpointr<    sL   € ð €ØÜ˜7 G¨TÓ2€EÜ�eœTÔ"ØØ—i‘i 
Ó+€Hô �8ÔAÀ5ÓIÈTÐQÐQr.   c                 óf  — t        |«      ryt        | ||||¬«      }t        |«      }|€)t        j                  d|«      }|r|j                  d«      }|€H|j                  «       j                  d«      s)t        j                  d|«      }	|	r|	j                  d«      }|€H|j                  «       j                  d«      s)t        j                  d|«      }	|	r|	j                  d«      }|€H|j                  «       j                  d«      r)t        j                  d|«      }
|
r|
j                  d«      }|S )	a4  Resolve the model(s) a request targets, for model-access and budget checks.

    Returns ``None`` when the request was dispatched to a user-defined pass-through
    endpoint: its body is forwarded verbatim to the configured upstream, so a
    ``model`` field there names an upstream model, not a LiteLLM-managed one, and
    enforcing key/team model allowlists against it would reject valid requests. The
    check reads the FastAPI-resolved endpoint (``request.scope["endpoint"]``), not the
    request path, so a custom path that collides with a built-in route never
    suppresses model-access checks: on a collision the built-in handler is dispatched
    and does not carry the marker. Built-in provider passthrough routes
    (``/vertex_ai``, ``/gemini``, ...) are separate handlers and keep model enforcement.
    N)rÆ   rÇ   rË  r-  rX   z/openai/deployments/([^/]+)r\   z/vertexz /(?:v1beta|beta)/models/([^:]+):z^/models/([^:]+):z/models/([^:]+))	r<  r7  r9  rN   rO   Úgroupr®   r¯   Úsearch)rÆ   rÇ   rË  r-  rX   r#   rþ  r4   rO   Úgoogle_matchÚvertex_matchs              r,   Úget_model_from_requestrB  !  s  € ô( 3°7Ô;Øä>Ø!ØØ'Ø1Øô€Jô % ZÓ0€Eð €}ä—x‘xÐ >ÀÓFˆÙØ—K‘K “NˆEð €}˜UŸ[™[›]×5Ñ5°iÔ@Ü—y‘yÐ!DÀeÓLˆÙØ ×&Ñ& qÓ)ˆEà€}˜UŸ[™[›]×5Ñ5°iÔ@Ü—y‘yÐ!5°uÓ=ˆÙØ ×&Ñ& qÓ)ˆEð
 €}˜Ÿ™›×1Ñ1°)Ô<Ü Ÿi™iÐ(:¸EÓBˆÙØ ×&Ñ& qÓ)ˆEà€Lr.   r9   c                 ó8   — t        | «      t        k  ryd| dd  › �S )Nzsk-...éüÿÿÿ)rë   r   )r9   s    r,   Úabbreviate_api_keyrE  c  s%   € Ü
ˆ7ƒ|Ô/Ò/ØØ�G˜B˜C�L�>Ð"Ð"r.   )FrŸ   )NN)NNN)NNNN)“rº  rN   r  Úcollections.abcr   r   r   Ú	functoolsr   Úloggingr   Útypingr   r	   r
   Úfastapir   r   r   Úpydanticr   r   r   rC   r   r   Úlitellm._loggingr   Úlitellm.constantsr   r   r   r   rÄ   r   Ú$litellm.litellm_core_utils.url_utilsr   r   r   r   Úlitellm.proxy._typesÚ-litellm.proxy.common_utils.http_parsing_utilsr   Ú:litellm.types.passthrough_endpoints.pass_through_endpointsr    Úlitellm.types.routerr!   Úlitellm.types.utilsr"   r¼  r@   r-   r    r¥   r1   rU   rJ   rQ   rW   r`   rc   Ú__annotations__rf   rt   ri   rn   ru   ÚsortedÚmodel_fieldsÚkeysrŠ   r�   r“   rq  r™   r  r¨   r«   r²   rÂ   r»   r×   rã   rï   rò   r   rÏ   r  r  r/  r3  r7  ÚUserAPIKeyAuthrC  rH  rJ  rL  rM  rN  rO  rU  rX  r]  r`  rb  rv  ÚLiteralr�  rƒ  rˆ  r�  r•  r™  rœ  rž  r¡  r¤  r§  r¬  r¯  r°  r³  rÂ  rÊ  rÑ  rÍ  rä  rå  rì  rí  rî  rï  rð  rñ  rý  r  r  r  r  r  r*  r%  r7  r9  r<  rB  rE  r   r.   r,   ú<module>rZ     s  ðÜ 	Û 	Û 
ß 9Ñ 9Ý Ý ß 'Ñ 'ç 2Ñ 2ß >Ñ >ã ß )Ý 1÷ó õ G÷ó ô #Ý Võõ EÝ :ñ	 Wð 	À4È$Á;ð 	Ð[^ÐaeÑ[eó 	ð (-ñØ�c‘˜TÑ!ðàðð  ™ðð ˆ4��t‘ÐÑó	ð*-¨Tð -°dó -ð`°ð Àð Èó ðØðàðð *Mðð 
ó	ð0ØðØðØ03ðØAGÈ$Áðà	óðN /YÐ �U˜5  c ™?Ñ+Ó Xð 1QÐ �u˜U 3¨ 8™_Ñ-Ó Pñ 7@à!Ø!ðó7Ð ˜e I¨c¡NÑ3ó ñ =Fòó=Ð " E¨)°C©.Ñ$9ó ð$¨I°c©Nó ð4 ØØØØØð ØØØð ð #ð
 Øð ØØð
 Øð àØðs@7ñ| Ñ.Ó0Ó1ð}@7ñ~ Ð&×3Ñ3×8Ñ8Ó:Ó;ñ@7Ð ˜U 5¨¨c¨¡?Ñ3ó @ðF(
Ø
ð(
àð(
ð ˜‘ð(
ð ð	(
ð
 
ó(
ðV,Ð �%˜˜c 3˜h™Ñ(ó ð(¨g°c¸6°kÑ.Bð (ÀxÐPVÑGWó (ðN &ð N°ð N¸À#ÈÐPSÐU[ÐP[ÑH\ÑB\Ñ9]ó Nð(4°¸¸V¸Ñ0Dð 4ÈÐRUÐX_Ð`cÐekÐ`kÑXlÑRlÑImó 4ð

¨cð 
°dó 
ð> tð >¸tð >ÐQWÐZ^ÑQ^ð >Ðgjð >Ðosó >ðB Cð ¨D°°c°©N¸TÑ,Aó ð(8Øð8àð8ð ó8ðv,°Có ,ð^#%˜wð #%¨3ó #%ðL¨ð °C¸$±Jó ñ& �3Ôðo 3ð o¨3ò oó ðoðd:°ð :¸Tó :ðz&°ð &¸ó &ðR'˜Só 'ð+¨cð +¸#ð +À#ÈÁ*ó +ð8R°#ð R¸#À¹*ó RðR°#ð R¸#À¹*ó Rð "ñ(Ø%ð(à�d‘
ð(ð 
ˆ#ˆsˆ(�^�dÑó(ðZ "ñ(Ø%ð(à�d‘
ð(ð 
ˆ#ˆsˆ(�^�dÑó(ðV (IÐ ˜uÓ HØ1\Ð '¨Ó \Ù1:Ø"Ð$KÐLó2Ð '¨ó ñ +6°kÓ*BÐ   %Ó BÙ4?ÀÈÈ[ÐHXÑ@YÓ4ZÐ *¨EÓ Zð¨&ð °S¸4±Zó ð°Vð ÀÈÈSÈÑ@QÐTXÑ@Xó ðYØ�c˜3�hÑ $Ñ&ðYà�d‘
ðYð 	ˆ4�ZóYð2 "ñ_Ø%ð_à�d‘
ð_ð 	ˆ4�Zó_ô*7 ô 7ð&Ø
$ðà˜s F˜{Ñ+ðð ˆ6�6ˆ>Ñóð0Ø
$ðà˜s F˜{Ñ+¨dÑ2ðð &ðð �MÑ"ð	ð
 
óôD7 Xô 7ðØ
(ðà˜s F˜{Ñ+¨dÑ2ðð &ðð �MÑ"ð	ð
 
óð<Ø%ðà"Ð#_Ñ`ðð ÐhÑiðð 
ˆ#ˆsˆ(�^�dÑó	ðØ%ðà	ˆ#ˆsˆ(�^�dÑóðØ%ðà	ˆ#ˆsˆ(�^�dÑóðØ%ðà	ˆ#ˆsˆ(�^�dÑóð4Ø%ðà	ˆ#ˆsˆ(�^�dÑóðØ%ðà	ˆ#ˆsˆ(�^�dÑóðØ%ðà	ˆ#ˆsˆ(�^�dÑóðØ%ðà	ˆ#ˆsˆ(�^�dÑóðà ðð ðð 	ˆ4�Zó	ð" .3Ð *ð *ò	6à ð6ð ð6ð ð	6ð
 
ó6ð&
¨#ð 
°$ó 
ð˜Tó ð.¸dÀT¹kó ð,Ø˜D‘[ðàˆ4�Zóð6  #ð  ¨#°©*ó  ñFQ°Dð QÈ4ÐRVÉ;ð QÐbeÐhlÑbló Qðh $5Ð ˜5Ó 4ð'Ð ˜eó ð7Ð ,¨eó ð:Ð /°ó ð
9Ð .°ó ð
 8CÐ -¨uÓ Bð
5Ð *¨Eó ð#Ð ˜%ó ðC¨¨c©ð C¸3ð CÀ4ó Cð¨¨c©ð °t¸C±yó ð	°¸¸c¸Ñ1BÀTÑ1Ið 	ÐPSð 	ÐX[ó 	ðA Sð A°5¸¸c¸±?ð AÀtó Að
X¨Sð X°Tó Xð %)Ø $ñC0ØðC0à˜T‘zðC0ð ˜‘ðC0ð 
ˆ#�Yó	C0ðL¨C°$©Jð ÀFÈTÁMð ÐVYÐ\`ÑV`ó ð 15Ø59Ø $ñ;0Øð;0àð;0ð ˜S #˜XÑ&¨Ñ-ð;0ð " # s (Ñ+¨dÑ2ð	;0ð
 ˜‘ð;0ð 
ˆ#�Yó;0ð|Ø�S‘	ðàˆ4�‰9�_�tÑóðR¸À4¹ð RÈDó Rð0 15Ø59Ø $Ø"ñ?Øð?àð?ð ˜S #˜XÑ&¨Ñ-ð?ð " # s (Ñ+¨dÑ2ð	?ð
 ˜‘ð?ð �t‰^ð?ð 	ˆ4�‰9�_�tÑó?ðD# ð #¨ô #r.   