Ë
    tPêjJ¤ ã                   óh  — U d Z 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mZmZmZ ddlmZmZmZmZmZmZ ddlmZmZmZmZmZ dd	lm Z  dd
l!m"Z" ddl#m$Z$ ddl%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z, ddl-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z:m;Z; ddl<m=Z= ddl>m?Z?m@Z@ ddlAmBZB ddlCmDZD ddlEmFZF ddlGmHZHmIZImJZJ ddlGmKZL ddlMmNZN ddlOmPZPmQZQ ddlRmSZSmTZT ddlUmVZV ddlWmXZX ddlYmZZZ ddl[m\Z\ ddl]m^Z^ ddl_m`Z`maZambZbmcZcmdZdmeZemfZfmgZg ddlhmiZimjZjmkZkmlZlmmZm ddlnmoZompZp dd lqmrZrmsZsmtZtmuZumvZv dd!lwmxZx erdd"lymzZ{  e«       Z|ee}d#<   d$„ ZK G d%„ d&e«      Z~ G d'„ d(e«      Z G d)„ d*e«      Z€ G d+„ d,e«      Z� G d-„ d.e«      Z‚ G d/„ d0e«      Zƒ G d1„ d2e«      Z„ G d3„ d4e„e«      Z…d5eSd6e€fd7„Z†d5eSd6e„fd8„Z‡d5eSd6e…fd9„Zˆd5eSd6d:fd;„Z‰d<eŠd5eSd6esdz  fd=„Z‹d>etdz  d?etdz  d6eŠdz  fd@„ZŒd>etdz  d?etdz  d6dfdA„Z�dBZŽee}dC<   dDZ�ee}dE<   dFetdGe�d6dfdH„Z‘ e’dIdJh«      Z“ee}dK<   dLeudz  d6e’eŠ   fdM„Z”dNesdOevd6eeŠe•f   fdP„Z–dQeeŠe•f   d6dfdR„Z—dLeeŠe•f   d6dfdS„Z˜e)Z™ee}dT<   e'Zšee}dU<    e
i e›�j9                  e™dV«      ¥e›�j9                  ešdW«      ¥«      Z�eeeŠežeŸdW   z  f      e}dX<    e
i «      Z eeeŠežeŸdW   z  f      e}dY<   e Z¡eeeŠe•f      e}dZ<   e&Z¢ee}d[<   e*Z£ee}d\<   e+Z¤ee}d]<   d^e•d6e�fd_„Z¥d^e•d6e�fd`„Z¦d^e•d6e�fda„Z§dLeeŠe•f   dbeeŠe•f   d6dfdc„Z¨dLeeŠe•f   dFeeŠe•f   dbeeŠe•f   d6eeŠežeŸdW   z  eeŠežf   z  f   fdd„Z©deeeŠe•f   dLeeŠe•f   dFeeŠe•f   dfevd6eŸeeŠežeŸdW   z  eeŠežf   z  f   e’eŠ   f   f
dg„Zªdhesd6esfdi„Z«dNesdjevd6e5fdk„Z¬e|�j[                  dldmg ee=«      g¬n«       ee=«      fd<eŠdOevdoe;fdp„«       Z®dqe.doe;dre�dsedt   dueŠdz  d6e0dz  fdv„Z¯e|�ja                  dwdmg ee=«      g¬n«       ee=«       eddx¬y«      fdqe.dzedoe;dueŠdz  d6e0dz  f
d{„«       Z±e|�ja                  d|dmg ee=«      g¬n«       ee=«       eddx¬y«      fdqe.dzedoe;dueŠdz  d6e0dz  f
d}„«       Z²	 	 d¿dhesdoe;d5eSd~eŠdz  de�d6d€fd�„Z³dhesdoe;d5eSd6d€fd‚„Z´dNesdOevdoe;d5eSd6e5f
dƒ„ZµdOevdNesd6eŠfd„„Z¶d…eŠd†eŠdOevdoe;d6df
d‡„Z·	 dÀd…eŠd5eSdˆe€dz  d6ee   fd‰„Z¸dŠe¹eŠ   d5eSd‹e^dz  d6e¹eŠ   fdŒ„Zºd…eŠd5eSd6e»eŠ   fd�„Z¼	 dÀdhesd5eSdŽeDd�eTd‹e^dz  d6dfd�„Z½d…eŠd†eŠdNesdOevdoe;d5eSdz  d6dfd‘„Z¾ G d’„ d“«      Z¿e|�ja                  d”d•dmg ee=«      g¬–«       ee=«      fdLe4doe;fd—„«       ZÀd†eŠd5eSd6e¹eŸeŠeŠf      fd˜„ZÁe|�ja                  d™dšdmg ee=«      g¬–«       ee=«      fdhesdoe;fd›„«       ZÂe|�ja                  dœd�dmg ee=«      g¬–«       ee=«      fdhevdoe;fdž„«       ZÃe|�ja                  dŸd dmg ee=«      g¬–«       ee=«      fd¡e~doe;fd¢„«       ZÄe|�ja                  d£d¤dmg ee=«      g¬–«       ee=«      fd¡epdoe;fd¥„«       ZÅd¦eŠdz  d6eŸeŸeceŠf   d§f   dz  fd¨„ZÆ G d©„ dªe«      ZÇe|�ja                  d«d¬dmg ee=«      g¬–«      d¡eÇd6eofd­„«       ZÈe|�j“                  d«d®dmg ee=«      g¬–«      e`ddfd¯eÊd¦eŠdz  d°eadz  d6eofd±„«       ZËd²e¹e›   d6e¹e›   fd³„ZÌdLe•d6eeŠe•f   dz  fd´„ZÍdLe•d6e�fdµ„ZÎd6e’eŠ   fd¶„ZÏ	 	 dÁd·e’eŠ   d¸eeŸeŠe•f      d¹e�dºe’eŠ   dz  d»e’eŠ   dz  d6eŸeŸeŠd§f   eŸeŠd§f   f   fd¼„ZÐ	 	 dÁd·e’eŠ   d¸eeŸeŠe•f      dseŠdºe’eŠ   dz  d»e’eŠ   dz  d6dfd½„ZÑd6e8fd¾„ZÒy)Âzå
Allow proxy admin to add/update/delete models in the db

Currently most endpoints are in `proxy_server.py`, but those should  be moved here over time.

Endpoints here:

model/{model_id}/update - PATCH endpoint for model update.
é    N)Ú	AwaitableÚMappingÚSequence)ÚJSONDecodeError)ÚMappingProxyType)ÚTYPE_CHECKINGÚ	AnnotatedÚFinalÚLiteralÚProtocolÚcast)Ú	APIRouterÚDependsÚHeaderÚHTTPExceptionÚRequestÚstatus)Ú	BaseModelÚ
ConfigDictÚFieldÚValidationErrorÚfield_validator)Úverbose_proxy_logger)Úuuid)ÚLITELLM_PROXY_ADMIN_NAME)ÚCUSTOM_PRICING_FIELDSÚPTU_EMPTIED_PRICING_FIELDSÚPTU_MODEL_INFO_FIELDSÚPTU_ZEROED_PRICING_FIELDSÚPTU_ZEROED_TABLE_FIELDSÚSEARCH_CONTEXT_SIZESÚptu_config_error)ÚBlockModelRequestÚCommonProxyErrorsÚLiteLLM_ProxyModelTableÚLiteLLM_TeamTableÚLitellmTableNamesÚLitellmUserRolesÚModelInfoDeleteÚPrismaCompatibleUpdateDBModelÚProxyErrorTypesÚProxyExceptionÚReconcileOutcomeÚTeamModelAddRequestÚTeamModelDeleteRequestÚUserAPIKeyAuth)Úuser_api_key_auth)Úcoordination_redis_cacheÚpublish_config_change)Úencrypt_value_helper)ÚUserApiKeyCache)Ú_is_user_team_admin)Ú_refresh_cached_teamÚteam_model_addÚteam_model_delete)Úupdate_team)Úcreate_object_audit_log)ÚPTU_COST_ATTRIBUTION_ENV_VARÚis_ptu_cost_attribution_enabled)ÚPrismaClientÚProxyLogging)ÚModelRepository)ÚTableActions)ÚModelTableRepository)ÚTeamRepository)ÚRouter)Ú&DEFAULT_CLASSIFIER_CONTEXT_WINDOW_SIZEÚClassificationRubricÚComplexityRouterConfigÚComplexityTierÚTierDefinitionÚclassification_system_promptÚ!custom_tier_classification_promptÚnormalize_classification_prompt)ÚSTRATEGY_ROUTER_PARAM_FIELDSÚ"carries_complexity_router_settingsÚ+validate_complexity_router_config_placementÚ'validate_complexity_router_config_writeÚ$validate_strategy_router_model_write)Ú)AutoRouterClassifierDefaultPromptResponseÚUpdateUsefulLinksRequest)ÚSPECIAL_MODEL_INFO_PARAMSÚ
DeploymentÚGenericLiteLLMParamsÚ	ModelInfoÚupdateDeployment)Úget_utc_datetime)ÚmodelsÚrouterc               �   ó4   K  — t        | i |¤Žƒ d{  –—† S 7 Œ­w)z¤
    Backward-compatible shim for tests/legacy call sites that patch this symbol.
    Team model management now uses team_model_add/team_model_delete directly.
    N)Ú_legacy_update_team)ÚargsÚkwargss     ú‚/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/management_endpoints/model_management_endpoints.pyr:   r:   s   s   è ø€ ô
 % dÐ5¨fÑ5×5Ð5Ð5ús   ‚‘’c                   óF   — e Zd ZU dZ ed¬«      Zee   ed<    e	d¬«      Z
y)ÚUpdatePublicModelGroupsRequestz.Request model for updating public model groupsz(List of model group names to make public©ÚdescriptionÚmodel_groupsÚforbid)ÚextraN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   re   ÚlistÚstrÚ__annotations__r   Úmodel_config© ó    r`   rb   rb   {   s#   … Ù8á#Ð0ZÔ[€L�$�s‘)Ó[á HÔ-�Lrq   rb   c                   ód   — e Zd Zedefd„«       Zedefd„«       Zedefd„«       Zddœde	defd„Z
y	)
Ú_ProxyModelRowÚreturnc                  ó   — y ©Nrp   ©Úselfs    r`   Úmodel_idz_ProxyModelRow.model_id„   s   € Ø"rq   c                  ó   — y rv   rp   rw   s    r`   Ú
model_namez_ProxyModelRow.model_name‡   s   € Ø!$rq   c                  ó   — y rv   rp   rw   s    r`   Ú
model_infoz_ProxyModelRow.model_infoŠ   s   € Ø$'rq   F©Úexclude_noner   c                 ó   — y rv   rp   ©rx   r   s     r`   Úmodel_dump_jsonz_ProxyModelRow.model_dump_json�   ó   � rq   N)rh   ri   rj   Úpropertyrm   ry   r{   Úobjectr}   Úboolr‚   rp   rq   r`   rs   rs   ƒ   sK   „ ØØ"˜#Ò"ó Ø"àØ$˜CÒ$ó Ø$àØ'˜FÒ'ó Ø'à6;ÒH¨tÐHÀÔHrq   rs   c                   óÒ   — e Zd Zdeeef   deedz     fd„Zdeeef   dee	e
      fd„Zdeeef   deeef   dee
dz     fd„Zdeeef   dee
dz     fd„Zdeeef   dee   fd	„Zy)
Ú_ProxyModelTableÚwherert   Nc                 ó   — y rv   rp   ©rx   r‰   s     r`   Úfind_uniquez_ProxyModelTable.find_unique‘   rƒ   rq   c                 ó   — y rv   rp   r‹   s     r`   Ú	find_manyz_ProxyModelTable.find_many“   rƒ   rq   Údatac                 ó   — y rv   rp   )rx   r‰   r�   s      r`   Úupdatez_ProxyModelTable.update•   s   € à+.rq   c                 ó   — y rv   rp   r‹   s     r`   Údeletez_ProxyModelTable.delete™   rƒ   rq   c                 ó   — y rv   rp   r‹   s     r`   Údelete_manyz_ProxyModelTable.delete_many›   rƒ   rq   )rh   ri   rj   r   rm   r…   r   r   rŒ   r   rs   rŽ   r‘   r“   Úintr•   rp   rq   r`   rˆ   rˆ   �   sº   „ Ø] G¨C°¨KÑ$8Ð]¸YÀyÐSWÑGWÑ=XÓ]àc '¨#¨v¨+Ñ"6Ðc¸9ÀXÈnÑE]Ñ;^Ócð/Ø  V Ñ,ð/Ø4;¸CÀ¸KÑ4Hð/à	�> DÑ(Ñ	)ó/ð ^˜w s¨F {Ñ3Ð]¸	À.ÐSWÑBWÑ8XÓ]àP G¨C°¨KÑ$8ÐP¸YÀs¹^ÔPrq   rˆ   c                   ó   — e Zd ZU eed<   y)Ú_TxModelTablesÚlitellm_proxymodeltableN)rh   ri   rj   rˆ   rn   rp   rq   r`   r˜   r˜   ž   s   … Ø-Ô-rq   r˜   c                   óB   — e Zd Zedeeef   fd„«       Zddœdedefd„Z	y)Ú_ExistingModelRowrt   c                  ó   — y rv   rp   rw   s    r`   Úlitellm_paramsz _ExistingModelRow.litellm_params£   s   € Ø69rq   Fr~   r   c                 ó   — y rv   rp   r�   s     r`   r‚   z!_ExistingModelRow.model_dump_json¦   rƒ   rq   N)
rh   ri   rj   r„   r   rm   r…   r�   r†   r‚   rp   rq   r`   r›   r›   ¢   s.   „ ØØ9 ¨¨V¨Ñ 4Ò9ó Ø9à6;ÒH¨tÐHÀÔHrq   r›   c                   ó>   — e Zd Zedee   fd„«       Zdeeef   fd„Z	y)Ú_TeamRowrt   c                  ó   — y rv   rp   rw   s    r`   rZ   z_TeamRow.modelsª   s   € Ø'*rq   c                  ó   — y rv   rp   rw   s    r`   Ú
model_dumpz_TeamRow.model_dump­   rƒ   rq   N)
rh   ri   rj   r„   r   rm   rZ   r   r…   r£   rp   rq   r`   r    r    ©   s*   „ ØØ*˜ ™Ò*ó Ø*à5˜G C¨ KÑ0Ô5rq   r    c                   ó2   — e Zd Zdeeef   deedz     fd„Zy)Ú_TeamLookupTabler‰   rt   Nc                 ó   — y rv   rp   r‹   s     r`   rŒ   z_TeamLookupTable.find_unique±   rƒ   rq   )	rh   ri   rj   r   rm   r…   r   r    rŒ   rp   rq   r`   r¥   r¥   °   s"   „ Ø\ G¨C°¨KÑ$8Ð\¸YÀxÐRVÁÑ=WÔ\rq   r¥   c            	       óH   — e Zd Zdeeef   deeef   deeef   dee   fd„Z	y)Ú
_TeamTabler‰   r�   Úincludert   c                 ó   — y rv   rp   )rx   r‰   r�   r©   s       r`   r‘   z_TeamTable.updateµ   s   € à'*rq   N)
rh   ri   rj   r   rm   r…   r†   r   r&   r‘   rp   rq   r`   r¨   r¨   ´   sK   „ ð+Ø  V Ñ,ð+Ø4;¸CÀ¸KÑ4Hð+ØSZÐ[^Ð`dÐ[dÑSeð+à	Ð$Ñ	%ô+rq   r¨   Úprisma_clientrt   c                 ó,   — t        | «      j                  S rv   )r@   Útable©r«   s    r`   Ú_proxy_model_tabler¯   º   s   € Ü˜=Ó)×/Ñ/Ð/rq   c                 ó,   — t        | «      j                  S rv   )rC   r­   r®   s    r`   Ú_repo_team_tabler±   ¾   s   € Ü˜-Ó(×.Ñ.Ð.rq   c                 ó.   — | j                   j                  S rv   )ÚdbÚlitellm_teamtabler®   s    r`   Ú_db_team_tablerµ   Â   s   € Ø×Ñ×-Ñ-Ð-rq   z.TableActions[prisma_models.LiteLLM_ModelTable]c                 ó,   — t        | «      j                  S rv   )rB   r­   r®   s    r`   Ú_model_alias_tabler·   Æ   s   € Ü Ó.×4Ñ4Ð4rq   ry   c              ƒ   ó˜   K  — t        |«      j                  d| i¬«      ƒ d {  –—† }|sy t        di |j                  d¬«      ¤Ž}|S 7 Œ$­w)Nry   ©r‰   Tr~   rp   )r¯   rŒ   rU   r£   )ry   r«   Údb_modelÚdeployment_pydantic_objs       r`   Úget_db_modelr¼   Ê   sW   è ø€ Ü.¨}Ó=×IÑIÐQ[Ð]eÐPfÐIÓg×g€HáØä%/Ñ%Y°(×2EÑ2EÐSWÐ2EÓ2XÑ%YÐØ"Ð"ð hús   ‚!A
£A¤%A
Úincoming_paramsÚexisting_paramsc                 ó  ‡ ‡— ‰ €yt        ‰ j                  ¬«      }|�|S t        ˆˆ fd„t        D «       «      }t	        ‰ j
                  |«      rt        ‰ j                  «      }|�|S ‰ j
                  €yt        ‰ j
                  |¬«      S )aÈ  Reject writes that would corrupt a strategy router's pseudo-model.

    An auto-router deployment's ``litellm_params.model`` (``auto_router/...``) is
    the discriminator the router loads it by; a write that mangles it makes the
    router drop the deployment silently under ``ignore_invalid_deployments``.
    Only writes that supply ``litellm_params.model`` are judged on the naming
    contract, against the merged (stored + incoming) params, so partial patches
    and restores of an already-corrupted row stay legal. A config is judged only
    when the write carries one, for the same reason: a rename must not be held
    hostage by a stored config it does not touch. Returns the violation, or None.
    N)Úcomplexity_router_configc              3   óR   •K  — | ]  }‰‰fD ]  }|�t        ||d «      �|–— Œ Œ  y ­wrv   ©Úgetattr)Ú.0ÚfieldÚsourcer¾   r½   s      €€r`   ú	<genexpr>z3_strategy_router_write_violation.<locals>.<genexpr>ê   sG   øè ø€ ò &àØ&¨Ð8ò&ð ØÐ¤'¨&°%¸Ó">Ð"Jô 	ð&Øñ&ùs   ƒ$')ÚmodelÚpresent_fields)	rP   rÀ   Ú	frozensetrM   rN   rÈ   rO   Úmodel_extrarQ   )r½   r¾   Úconfig_violationrÉ   Úplacement_violations   ``   r`   Ú _strategy_router_write_violationrÎ   Ô   sš   ù€ ð ÐØÜEØ!0×!IÑ!IôÐð Ð#ØÐÜ%ô &ä1ô&ó €Nô *¨/×*?Ñ*?ÀÔPÜ%PÐQ`×QlÑQlÓ%mÐØÐ*Ø&Ð&Ø×ÑÐ$ØÜ/°o×6KÑ6KÐ\jÔkÐkrq   c                 óŒ   — t        | |¬«      }|€y t        |t        j                  j                  t
        j                  d¬«      ‚)N©r½   r¾   zlitellm_params.model©ÚmessageÚtypeÚcodeÚparam)rÎ   r,   r+   Úvalidation_errorÚvaluer   ÚHTTP_400_BAD_REQUEST)r½   r¾   Ú	violations      r`   Ú)_raise_on_strategy_router_write_violationrÚ   ú   sE   € ô 1ÀÐbqÔr€IØÐØÜ
ØÜ×-Ñ-×3Ñ3Ü×(Ñ(Ø$ô	ð rq   Úenforce_rpm_tpm_on_model_addÚ$ENFORCE_RPM_TPM_ON_MODEL_ADD_SETTING)ÚrpmÚtpmÚ_REQUIRED_RATE_LIMIT_FIELDSr�   Úenforcedc                 óì   ‡ ‡— |syt        ˆ ˆfd„t        D «       «      }|syt        dj                  |«      › dt        › d�t
        j                  j                  t        j                  d|d   › �¬«      ‚)	a§  Require both rpm and tpm (each a positive value) when the operator opts in via config.yaml.

    Off by default, so deployments keep adding models without limits. When
    ``enforce_rpm_tpm_on_model_add: true`` is set under general_settings, a model added
    without both rpm and tpm set to a positive value is rejected rather than stored
    unbounded (or effectively excluded from routing by a zero/negative limit).
    Nc              3   óH   •K  — | ]  }t        ‰|«      xŠ�‰dk  r|–— Œ y ­w©Nr   rÂ   )rÄ   rÅ   r�   r×   s     €€r`   rÇ   z=_raise_if_rate_limits_required_but_missing.<locals>.<genexpr>  s1   øè ø€ ò àÜ˜^¨UÓ3Ð3ˆEÐ<ÀÈÂ
ô 	ñùs   ƒ"z and z' must be set to a positive value when 'z ' is enabled in general_settingszlitellm_params.r   rÑ   )
Útuplerß   r,   ÚjoinrÜ   r+   rÖ   r×   r   rØ   )r�   rà   Úmissingr×   s   `  @r`   Ú*_raise_if_rate_limits_required_but_missingrç     s‚   ù€ ñ ØÜô ä0ôó €Gñ
 ØÜ
à�|‰|˜GÓ$Ð%ð &Ü4Ð5Ð5UðWô ×-Ñ-×3Ñ3Ü×(Ñ(Ø ¨¡
˜|Ð,ôð rq   Ú	ptu_countÚcost_per_ptu_per_hourÚ_PTU_PRICED_PAIRr}   c                 ó`   ‡ — ‰ �
t        «       s
t        «       S t        ˆ fd„t        D «       «      S )a  The PTU fields a patch sends as an explicit null, which update_db_model drops.

    Empty while the feature is off, so disabling pauses PTU rather than letting a client
    that round-trips a model_info blob erase a configuration set up during an earlier opt-in.
    c              3   óV   •K  — | ]   }|‰j                   v rt        ‰|«      €|–— Œ" y ­wrv   )Úmodel_fields_setrÃ   )rÄ   rÅ   r}   s     €r`   rÇ   z1_explicitly_cleared_ptu_fields.<locals>.<genexpr>4  s3   øè ø€ ò àØ�J×/Ñ/Ñ/´G¸JÈÓ4NÐ4Vô 	ñùs   ƒ&))r=   rÊ   r   ©r}   s   `r`   Ú_explicitly_cleared_ptu_fieldsrï   ,  s3   ø€ ð ÐÔ!@Ô!BÜ‹{ÐÜó ä*ôó ð rq   rº   Ú
patch_datac                 ó\  — | j                   r| j                   j                  d¬«      nt        }|j                   r|j                   j                  d¬«      nt        }t        |j                   «      }t	        i |¥|¥j                  «       D ��ci c]  \  }}||vsŒ||“Œ c}}«      S c c}}w )zÛThe model_info a patch would store, which is the stored blob updated by the patch.

    A PTU invariant holds over the deployment as it will exist, not over whichever subset
    of fields a caller happened to send.
    Tr~   )r}   r£   Ú_EMPTY_MODEL_INFOrï   r   Úitems)rº   rð   ÚstoredÚincomingÚclearedÚkÚvs          r`   Ú_merged_ptu_model_inforù   ;  s¡   € ð JR×I\ÒI\�H×'Ñ'×2Ñ2ÀÐ2ÔEÔbs€Fà?I×?TÒ?Tˆ
×Ñ×(Ñ(°dÐ(Ô;ÔZkð ô 4°J×4IÑ4IÓJ€GÜÐ.D°Ð.D¸8Ð.D×.JÑ.JÓ.L×a¡d a¨ÐPQÐY`ÒP`˜Q ™TÓaÓbÐbùÓas   ÂB(
ÂB(
Úincoming_model_infoc                 óž   ‡ — t        «       ryt        ˆ fd„t        D «       «      }|syt        dddj	                  |«      › dt
        › d�¬«      ‚)	aµ  Reject PTU model_info fields unless the operator opted into PTU cost attribution.

    Takes the incoming request's model_info rather than the merged deployment, so an
    unrelated patch of a model that still stores PTU config from an earlier opt-in is
    left alone. The fields are rejected rather than dropped so a caller never believes
    a flat cost was configured while the rollup that would price it is not running.

    Only a value is rejected. An explicit null reaches the clear loop, which is gated on
    the same flag, so a disabled proxy neither writes PTU config nor erases what an
    earlier opt-in stored. Disabling pauses the feature rather than discarding its setup.
    Nc              3   óF   •K  — | ]  }‰j                  |«      €Œ|–— Œ y ­wrv   ©Úget)rÄ   rÅ   rú   s     €r`   rÇ   z:_raise_if_ptu_cost_attribution_disabled.<locals>.<genexpr>W  s#   øè ø€ Òs eÐH[×H_ÑH_Ð`eÓHfÑHrœEÑsùó   ƒ!š!é�  z%PTU cost attribution is disabled, so ú, z cannot be set. Set z=true to enable it.©Ústatus_codeÚdetail)r=   rä   r   r   rå   r<   )rú   Úsupplieds   ` r`   Ú'_raise_if_ptu_cost_attribution_disabledr  I  s[   ø€ ô 'Ô(ØÜÓsÔ/DÔsÓs€HÙØÜ
Øà3°D·I±I¸hÓ4GÐ3Hð IÜ/Ð0Ð0CðEôð rq   c                 ó8   — t        | «      }|�t        d|¬«      ‚y)aA  Enforce the PTU cross-field invariant on the effective model_info.

    The rules live in litellm_core_utils.ptu_pricing so that config.yaml registration
    refuses the same deployments this endpoint does, for the same reason. Per-field bounds
    (positive count, non-negative rate) are enforced by ModelInfo itself.

    Registration additionally requires an operator-declared ``model_info.id``, which this
    endpoint does not: a stored deployment already holds a stable primary key, where a
    config-declared one is otherwise keyed by a hash of its own parameters.
    Nr   r  )r"   r   )r}   Úerrors     r`   Ú_validate_ptu_model_infor	  c  s&   € ô $ JÓ/€EØÐÜ¨°EÔ:Ð:ð rq   Ú_PTU_ZEROED_PRICING_FIELDSÚ_PTU_EMPTIED_PRICING_FIELDSç        rp   Ú_PTU_ZEROED_PRICINGÚ_NO_PRICING_OVERRIDErò   Ú_CUSTOM_PRICING_FIELDSÚ_PTU_ZEROED_TABLE_FIELDSÚ_SEARCH_CONTEXT_SIZESr×   c                 ób   — t        | t        t        f«      xr t        | t        «       xr | dk7  S rã   )Ú
isinstancer–   Úfloatr†   ©r×   s    r`   Ú_is_nonzero_rater  �  s,   € Ü�eœc¤5˜\Ó*ÒY´:¸eÄTÓ3JÐ/JÒYÈuÐXYÉzÐYrq   c                 óx   — t        | t        «      r t        d„ | j                  «       D «       «      S t	        | «      S )Nc              3   ó2   K  — | ]  }t        |«      –— Œ y ­wrv   )r  )rÄ   Úrates     r`   rÇ   z$_is_nonzero_price.<locals>.<genexpr>–  s   è ø€ ÒE¨dÔ# D×)ÑEùs   ‚)r  ÚdictÚanyÚvaluesr  r  s    r`   Ú_is_nonzero_pricer  ”  s.   € Ü�%œÔÜÑE°e·l±l³nÔEÓEÐEÜ˜EÓ"Ð"rq   c                 óæ   — t        | t        «      rt        | «      xr t        | «       S t        | t        t
        f«      r|  S t        | t        t        f«      xr t        | t        «       xr | dk(  S rã   )r  r  r†   r  rl   rä   r–   r  r  s    r`   Ú_is_zero_pricer  š  sd   € Ü�%œÔÜ�E‹{Ò;Ô#4°UÓ#;Ð;Ð;Ü�%œ$¤˜Ô'ØˆyÐÜ�eœc¤5˜\Ó*ÒY´:¸eÄTÓ3JÐ/JÒYÈuÐXYÉzÐYrq   r  c           	      ó,  ‡— t        «       sy| j                  d«      �| j                  d«      €yt        t        t        ˆfd„t        D «       «      t        ˆfd„t
        D «       «      z   «      «      }|syt        dddj                  |«      › d	�¬
«      ‚)a4  Refuse a rate the caller supplies for a deployment that bills reserved capacity.

    Separate from the zeroing so the team-model path can run it before it touches the team, whose
    ACL write autocommits: a refusal raised after it would leave the team changed and the
    deployment row never written.
    Nrè   ré   c              3   óX   •K  — | ]!  }t        ‰j                  |«      «      sŒ|–— Œ# y ­wrv   ©r  rþ   ©rÄ   rÅ   r  s     €r`   rÇ   z5_raise_if_ptu_deployment_is_priced.<locals>.<genexpr>¯  s&   øè ø€ Òf˜EÔ?PÐQY×Q]ÑQ]Ð^cÓQdÕ?e”%Ñfùs   ƒ*£*c              3   óF   •K  — | ]  }‰j                  |«      sŒ|–— Œ y ­wrv   rý   r#  s     €r`   rÇ   z5_raise_if_ptu_deployment_is_priced.<locals>.<genexpr>°  s   øè ø€ ÒZ˜eÀhÇlÁlÐSXÕFY”EÑZùrÿ   r   z0A PTU deployment bills by reserved capacity, so r  zu cannot be charged on top of it. Send 0 or no value, or remove ptu_count and cost_per_ptu_per_hour to bill per token.r  )r=   rþ   rä   Úsortedr  r  r   rå   )r}   r  Úpriceds    ` r`   Ú"_raise_if_ptu_deployment_is_pricedr'  ¢  s›   ø€ ô +Ô,ØØ‡~�~�kÓ"Ð*¨j¯n©nÐ=TÓ.UÐ.]ØÜÜÜÓfÔ%;ÔfÓfÜÓZÔ'BÔZÓZñ[ó	
ó€Fñ ØÜ
Øà>¸t¿y¹yÈÓ?PÐ>Qð Rnð nôð rq   c           
      ó‚  ‡ ‡— t        «       st        S ‰ j                  d«      �‰ j                  d«      €t        S t        ‰ |¬«       t	        ˆˆ fd„t
        D «       «      }t        i t        ¥t        j                  t        t        j                  t        d«      «      ¥t        j                  |t        z
  d«      ¥«      S )aÌ  The pricing a PTU deployment must carry, empty unless one is being stored.

    Reserved capacity is already billed by the flat cost the rollup writes, so charging the
    traffic it serves bills the same tokens twice. Left unset the rate falls back to the public
    cost map, which makes the double charge the default rather than an opt-in.

    Only a price the caller supplies is refused. A non-zero price already on the row is zeroed
    instead, so a deployment priced through a path this rule does not cover heals on its next
    save rather than rejecting every later edit of a field that has nothing to do with pricing.

    ``supplied`` is the caller's litellm_params alone, because that is the blob a price is
    authored on. model_info's copy is written by the server, both by the mirror in
    ``Deployment.__init__`` and by the cost-map defaults /model/info fills in, so a client that
    round-trips a model_info blob sends back prices it never chose.
    rè   ré   ©r}   r  c              3   óŠ   •K  — | ]:  }t        ‰j                  |«      «      st        ‰j                  |«      «      r|–— Œ< y ­wrv   r"  ©rÄ   rÅ   r�   r}   s     €€r`   rÇ   z&_ptu_zeroed_pricing.<locals>.<genexpr>Ø  s>   øè ø€ ò àÜ˜ZŸ^™^¨EÓ2Ô3Ô7HÈ×I[ÑI[Ð\aÓIbÔ7cô 	ñùó   ƒA Ar  )r=   r  rþ   r'  rÊ   r  r   r  r  Úfromkeysr  r  )r}   r�   r  rô   s   ``  r`   Ú_ptu_zeroed_pricingr.  ¾  s®   ù€ ô* +Ô,Ü#Ð#Ø‡~�~�kÓ"Ð*¨j¯n©nÐ=TÓ.UÐ.]Ü#Ð#Ü&°*ÀxÕPÜô ä+ôó €Fô
 ð	
Ü!ð	
ä�m‰mÔ4´d·m±mÔDYÐ[^Ó6_Ó`ð	
ô �m‰m˜FÔ%=Ñ=¸sÓCð	
óð rq   Ústored_model_infoÚpatchc                 ó–  ‡ ‡‡— |j                   r|j                   j                  d¬«      nt        }t        ‰‰|¬«      }|r|t	        «       fS t        ˆ fd„t        D «       «      }|rt        |j                  «      t        z  st        t	        «       fS t        t	        ˆˆfd„t        j                  t        t        «      D «       «      fS )aU  The pricing a patch must write into both blobs, and the pricing it must drop from them.

    A patch that takes the deployment off PTU takes the zeroed pricing with it, since the zeros
    exist only to stop the double charge. Left behind they would serve the deployment for free.
    Reading the stored row rather than the patch alone keeps that release off a deployment that
    never carried PTU config, whose zero price is a rate its operator chose. A zero the patch
    itself carries is released with the rest, because the dashboard echoes the whole stored
    blob on every save, so a supplied zero cannot be told apart from the one this rule wrote.

    The release spans every field the zeroing could have written, not just the mirrored ones, or
    a rate zeroed on the way in (per-second, per-character tiers) would bill nothing forever.
    Tr~   ©r}   r�   r  c              3   óD   •K  — | ]  }‰j                  |«      d u–— Œ y ­wrv   rý   )rÄ   rÅ   r/  s     €r`   rÇ   z%_ptu_pricing_delta.<locals>.<genexpr>ý  s"   øè ø€ Ò`ÀeÐ*×.Ñ.¨uÓ5¸TÔAÑ`ùs   ƒ c              3   óŠ   •K  — | ]:  }t        ‰j                  |«      «      st        ‰j                  |«      «      r|–— Œ< y ­wrv   )r  rþ   r+  s     €€r`   rÇ   z%_ptu_pricing_delta.<locals>.<genexpr>   s=   øè ø€ ò +àÜ˜*Ÿ.™.¨Ó/Ô0´NÀ>×CUÑCUÐV[ÓC\Ô4]ô 	ñ+ùr,  )r�   r£   rò   r.  rÊ   r  rê   rï   r}   r  r  Úunionr
  r  )r/  r}   r�   r0  r  ÚzeroedÚwas_ptus   ```    r`   Ú_ptu_pricing_deltar8  æ  s¯   ú€ ð& MR×L`ÒL`�e×*Ñ*×5Ñ5À4Ð5ÔHÔfw€HÜ'°:ÈnÐgoÔp€FÙØ”y“{Ð"Ð"ÜÓ`ÔO_Ô`Ó`€GÙÔ8¸×9IÑ9IÓJÔM]Ò]Ü#¤Y£[Ð0Ð0Ü¤ô +ä+×1Ñ1Ô2LÔNiÓjô+ó "ð ð rq   Úmodel_paramsc                 óÄ  — | j                   j                  d¬«      }| j                  j                  d¬«      }t        |||¬«      }|s| S t	        |j                  «       D ��ci c]  \  }}|t        |t        «      rg n|“Œ c}}«      }| j                  t	        | j                  j                  |¬«      | j                   j                  |¬«      dœ«      ¬«      S c c}}w )zO``model_params`` with PTU pricing applied, or itself when it configures no PTU.Tr~   r2  )r‘   )r�   r}   )	r}   r£   r�   r.  r   ró   r  rä   Ú
model_copy)r9  r}   r�   ÚoverrideÚkeyr×   rô   s          r`   Ú_ptu_priced_deploymentr>    sß   € à$×/Ñ/×:Ñ:ÈÐ:ÓM€JØ(×7Ñ7×BÑBÐPTÐBÓU€NÜ)°ZÐP^ÐiwÔx€HÙØÐô %ØJRÏ.É.ÓJZ×[¹J¸CÀˆ”J˜u¤eÔ,‰b°%Ñ	7Ó[ó€Fð ×"Ñ"Üà".×"=Ñ"=×"HÑ"HÐPVÐ"HÓ"WØ*×5Ñ5×@Ñ@ÈÐ@ÓOñó
ð #ó ð ùó 	\s   Á#C
Úupdated_patchc                 ó¦  — |j                   �%t        |j                   j                  d¬«      «       |j                  xs | j                  }| j                  j                  d¬«      }| j                   j                  d¬«      }|j                  rW|j                  j                  d¬«      j                  «       D ��ci c]  \  }}|t        |«      “Œ }}}|j                  |«       |j                   r+|j                  |j                   j                  d¬«      «       |j                  r_|j                  j                  D ]F  }|t        v sŒt        |j                  |«      �Œ#|j                  |d «       |j                  |d «       ŒH |j                   r‹|j                   j                  D ]F  }|t        v sŒt        |j                   |«      �Œ#|j                  |d «       |j                  |d «       ŒH t        |j                   «      D ]  }|j                  |d «       Œ t        |«       t        | j                   r| j                   j                  d¬«      nt        |||¬«      \  }	}
|j                  |	«       |j                  |	«       |
D ]&  }|j                  |d «       |j                  |d «       Œ( |j                  «       D ]3  \  }}t!        |t"        j"                  «      sŒ!|j%                  «       ||<   Œ5 t'        |t)        j*                  |«      t)        j*                  |«      ¬«      }|j,                  �|j,                  |d<   |S c c}}w )NTr~   )r/  r}   r�   r0  )r{   r�   r}   Úblocked)r}   r  r£   r{   r�   ró   r4   r‘   rí   rT   rÃ   Úpoprï   r	  r8  rò   r  ÚdatetimeÚ	isoformatr*   ÚjsonÚdumpsrA  )rº   r?  Úmerged_model_nameÚmerged_litellm_paramsÚmerged_model_infor÷   rø   Úencrypted_paramsrÅ   Úptu_pricingÚptu_releasedr=  r×   Úprisma_compatible_model_dicts                 r`   Úupdate_db_modelrN    s'  € Ø×ÑÐ+Ü/°×0HÑ0H×0SÑ0SÐaeÐ0SÓ0fÔgØ,×7Ñ7ÒN¸8×;NÑ;NÐØ#+×#:Ñ#:×#EÑ#EÐSWÐ#EÓ#XÐØ'×2Ñ2×=Ñ=È4Ð=ÓPÐð ×#Ò#ð 4A×3OÑ3O×3ZÑ3ZÐhlÐ3ZÓ3m×3sÑ3sÓ3u÷#
Ù+/¨1¨aˆAÔ# AÓ&Ñ&ð#
Ðñ #
ð 	×$Ñ$Ð%5Ô6ð ×ÒØ× Ñ  ×!9Ñ!9×!DÑ!DÐRVÐ!DÓ!WÔXð ×#Ò#Ø"×1Ñ1×BÑBò 	3ˆEØÔ1Ò1´g¸m×>ZÑ>ZÐ\aÓ6bÑ6jØ%×)Ñ)¨%°Ô6Ø!×%Ñ% e¨TÕ2ð	3ð ×ÒØ"×-Ñ-×>Ñ>ò 	7ˆEØÔ1Ò1´g¸m×>VÑ>VÐX]Ó6^Ñ6fØ!×%Ñ% e¨TÔ2Ø%×)Ñ)¨%°Õ6ð	7ô 4°M×4LÑ4LÓMò 	/ˆEØ×!Ñ! %¨Õ.ð	/ô Ð.Ô/Ü 2à×Òð #×-Ñ-×8Ñ8ÀdÐ8ÔKäØ$Ø,Øô!Ñ€K�ð ×Ñ˜[Ô)Ø× Ñ  Ô-Øò /ˆØ×Ñ˜e TÔ*Ø×!Ñ! %¨Õ.ð/ð (×-Ñ-Ó/ò 7‰
ˆˆUÜ�eœX×.Ñ.Õ/Ø%*§_¡_Ó%6Ð˜cÒ"ð7ô +HØ$Ü—z‘zÐ"7Ó8Ü—:‘:Ð/Ó0ô+Ð ð ×ÑÐ(Ø2?×2GÑ2GÐ$ YÑ/à'Ð'ùó}#
s   Â=Mz/model/{model_id}/updatezmodel management)ÚtagsÚdependenciesÚuser_api_key_dictc              ƒ   óÖ  K  — ddl m}m}m}m}m} 	 |€'t        ddt        j                  j                  i¬«      ‚|s5t        dt        j                  j                  t        j                  d¬«      ‚t        | |¬	«      ƒ d{  –—† }|€x|rG|j!                  | ¬
«      �5t        dt        j                  j                  t        j                  d¬«      ‚t        d| › d�t        j"                  t        j$                  d¬«      ‚t&        j)                  ||||¬«      ƒ d{  –—†  |j*                  �R|j,                  t.        j0                  k7  r5t        dt        j2                  j                  t        j4                  d¬«      ‚t7        |j8                  |j8                  ¬«       t;        ||||¬«      ƒ d{  –—† }	|j<                  xs ||	d<   t?        t@        tC        «       «      |	d<   tE        |«      jG                  d| i|	¬«      ƒ d{  –—† }
|
€/t        d| › d�t        j"                  t        j$                  d¬«      ‚tI        «       }tK        «       ƒ d{  –—† }tM        jN                  tQ        | d|tR        jT                  |jW                  d¬«      |
jW                  d¬«      |j<                  tX        ¬«      «       t[        || t]        |
dd«      fgd|j^                  |j`                  ¬«       |
S 7 �Œm7 �ŒÕ7 �ŒB7 Œð7 Œ¥# tb        $ ra}te        jf                  d|«       ti        |t        t        f«      r|‚t        d|› �t        jj                  t        jl                  d¬«      ‚d}~ww xY w­w) a*  
    PATCH Endpoint for partial model updates.

    Only updates the fields specified in the request while preserving other existing values.
    Follows proper PATCH semantics by only modifying provided fields.

    Args:
        model_id: The ID of the model to update
        patch_data: The fields to update and their new values
        user_api_key_dict: User authentication information

    Returns:
        Updated model information

    Raises:
        ProxyException: For various error conditions including authentication and database errors
    r   )Úlitellm_proxy_admin_nameÚ
llm_routerÚpremium_userr«   Ústore_model_in_dbNéô  r  r  ú1Model updates only supported for DB-stored modelsrÑ   ©ry   r«   ©ry   úGCannot edit config-based model. Store model in DB via /model/new first.úModel ú not found on proxy.©r9  rQ  r«   rU  ú4Only proxy admins can change a model's blocked flag.rA  rÐ   )rº   rð   rQ  r«   Ú
updated_byÚ
updated_atry   ©r‰   r�   ÚupdatedTr~   ©Ú	object_idÚactionrQ  Ú
table_nameÚbefore_valueÚafter_valueÚlitellm_changed_byrS  r}   r‘   ©ÚbeforeÚwritten_modelsrf  Ústill_desiredÚ
live_afterzError in patch_model: %szError updating model: )7Úlitellm.proxy.proxy_serverrS  rT  rU  r«   rV  r   r$   Údb_not_connected_errorr×   r,   r+   rÖ   r   rØ   r¼   Úget_deploymentÚnot_found_errorÚHTTP_404_NOT_FOUNDÚModelManagementAuthChecksÚcan_user_make_model_callrA  Ú	user_roler(   ÚPROXY_ADMINÚ
auth_errorÚHTTP_403_FORBIDDENrÚ   r�   Ú_update_team_model_in_dbÚuser_idr   rm   rY   r¯   r‘   Úlive_model_ids_snapshotÚclear_cacheÚasyncioÚcreate_taskr;   r'   ÚPROXY_MODEL_TABLE_NAMEr‚   r   Ú raise_if_reload_degraded_servingrÃ   rn  ro  Ú	Exceptionr   Ú	exceptionr  Úinternal_server_errorÚHTTP_500_INTERNAL_SERVER_ERROR)ry   rð   rQ  rS  rT  rU  r«   rV  rº   Úupdate_dataÚupdated_modelÚlive_before_reloadÚreload_outcomeÚes                 r`   Úpatch_modelrŒ  h  sj  è ø€ ÷6õ ð{
ØÐ ÜØØÔ!2×!IÑ!I×!OÑ!OÐPôð ñ !Ü ØKÜ$×5Ñ5×;Ñ;Ü×0Ñ0Øô	ð ô !-°hÈmÔ \×\ˆàÐá˜j×7Ñ7ÀÐ7ÓJÐVÜ$ØeÜ(×9Ñ9×?Ñ?Ü×4Ñ4Øô	ð ô !Ø   
Ð*>Ð?Ü$×4Ñ4Ü×.Ñ.Øô	ð ô (×@Ñ@Ø!Ø/Ø'Ø%ð	 Aó 
÷ 	
ð 	
ð ×ÑÐ)Ð.?×.IÑ.IÔM]×MiÑMiÒ.iÜ ØNÜ$×/Ñ/×5Ñ5Ü×.Ñ.Øô	ð ô 	2Ø&×5Ñ5Ø$×3Ñ3õ	
ô $<ØØ!Ø/Ø'ô	$
÷ 
ˆð %6×$=Ñ$=Ò$YÐAYˆ�LÑ!Ü$(¬Ô.>Ó.@Ó$Aˆ�LÑ!ô &8¸Ó%F×%MÑ%MØ˜xÐ(Øð &Nó &
÷  
ˆð
 Ð Ü Ø   
Ð*>Ð?Ü$×4Ñ4Ü×.Ñ.Øô	ð ô %<Ó$=ÐÜ&1£m× 3ˆô 	×ÑÜ#Ø"Ø Ø"3Ü,×CÑCØ%×5Ñ5À4Ð5ÓHØ)×9Ñ9ÀtÐ9ÓLØ#4×#<Ñ#<Ü)Aô	ô	
ô 	)Ø%Ø%¤w¨}¸lÈDÓ'QÐRÐSØØ(×6Ñ6Ø%×0Ñ0õ	
ð Ðð{ ]ùð$	
ùð0
ùð 
øð !4ùô4 ò 
Ü×&Ñ&Ð'AÀ1ÔEä�aœ-¬Ð8Ô9ØˆGäØ,¨Q¨CÐ0Ü ×6Ñ6Ü×6Ñ6Øô	
ð 	
ûð
üs‰   ‚M)’A0K< ÂK/ÂBK< ÄK2ÄBK< Æ3K5Æ4AK< ÈK8ÈAK< ÉK:ÉBK< Ë.M)Ë/K< Ë2K< Ë5K< Ë8K< Ë:K< Ë<	M&ÌAM!Í!M&Í&M)r�   rA  rf  )rA  Ú	unblockedrj  c              ƒ   ó(  K  — ddl m}m}m}m} 	 |€'t        ddt        j                  j                  i¬«      ‚|dur5t        dt        j                  j                  t        j                  d ¬«      ‚|j                  t        j                   k7  r5t        d	t        j"                  j                  t        j$                  d
¬«      ‚t'        | j(                  |¬«      ƒ d {  –—† }	|	€Œ|rQ|j+                  | j(                  ¬«      �5t        dt        j                  j                  t        j                  d ¬«      ‚t        d| j(                  › d�t        j,                  t        j.                  d ¬«      ‚t1        |«      j2                  j5                  d| j(                  i||j6                  xs |t9        t:        t=        «       «      dœ¬«      ƒ d {  –—† }
t?        «       }tA        «       ƒ d {  –—† }tC        jD                  tG        | j(                  ||tH        jJ                  |	jM                  d¬«      tO        |
tP        «      r|
jM                  d¬«      nd ||¬«      «       tS        || j(                  tU        |
dd «      fg||jV                  |jX                  ¬«       |
S 7 �Œ¿7 ŒÑ7 Œ·# tZ        $ rb}t]        j^                  d||«       tO        |t
        t        f«      r|‚t        d|› �t        j`                  t        jb                  d ¬«      ‚d }~ww xY w­w)Nr   )rS  rT  r«   rV  rW  r  r  TrX  rÑ   r_  rA  rY  rZ  r[  r\  r]  ry   )rA  r`  ra  rb  r~   rd  r}   rk  zError in model %s: %sz%Error updating model blocked status: )2rp  rS  rT  r«   rV  r   r$   rq  r×   r,   r+   rÖ   r   rØ   rw  r(   rx  ry  rz  r¼   ry   rr  rs  rt  r@   r­   r‘   r|  r   rm   rY   r}  r~  r  r€  r;   r'   r�  r‚   r  r   r‚  rÃ   rn  ro  rƒ  r   r„  r…  r†  )r�   rQ  rA  rf  rj  rS  rT  r«   rV  rº   rˆ  r‰  rŠ  r‹  s                 r`   Ú_set_model_blocked_statusr�  	  sÞ  è ø€ ÷ó ð[
ØÐ ÜØØÔ!2×!IÑ!I×!OÑ!OÐPôð ð
  DÑ(Ü ØKÜ$×5Ñ5×;Ñ;Ü×0Ñ0Øô	ð ð ×&Ñ&Ô*:×*FÑ*FÒFÜ ØNÜ$×/Ñ/×5Ñ5Ü×.Ñ.Øô	ð ô !-Ø—]‘]Ø'ô!
÷ 
ˆð
 ÐÙ˜j×7Ñ7ÀÇÁÐ7ÓOÐ[Ü$ØeÜ(×9Ñ9×?Ñ?Ü×4Ñ4Øô	ð ô !Ø  §¡ Ð/CÐDÜ$×4Ñ4Ü×.Ñ.Øô	ð ô &5°]Ó%C×%IÑ%I×%PÑ%PØ˜tŸ}™}Ð-à"Ø/×7Ñ7ÒSÐ;SÜ"¤3Ô(8Ó(:Ó;ñð &Qó &
÷  
ˆô %<Ó$=ÐÜ&1£m× 3ˆä×ÑÜ#ØŸ-™-ØØ"3Ü,×CÑCØ%×5Ñ5À4Ð5ÓHäHRÐS`ÔbkÔHl�M×1Ñ1¸tÐ1ÔDÐrvà#5Ø)Aôô	
ô 	)Ø%Ø!Ÿ]™]¬G°MÀ<ÐQUÓ,VÐWÐXØØ(×6Ñ6Ø%×0Ñ0õ	
ð Ððo
ùð( 
øð !4ùô6 ò 
Ü×&Ñ&Ð'>ÀÈÔJä�aœ-¬Ð8Ô9ØˆGäØ;¸A¸3Ð?Ü ×6Ñ6Ü×6Ñ6Øô	
ð 	
ûð
üsb   ‚L�CJ$ ÃJÃC/J$ ÇJ ÇJ$ Ç*J"Ç+B1J$ ÊLÊJ$ Ê J$ Ê"J$ Ê$	LÊ-AL
Ì
LÌLz/model/blockz�The litellm-changed-by header enables tracking of actions performed by authorized users on behalf of other users, providing an audit trail for accountabilityrc   Úhttp_requestc              ƒ   ó>   K  — t        | |dd|¬«      ƒ d{  –—† S 7 Œ­w)zŒ
    Block a DB-stored model deployment from serving requests.

    Parameters:
    - model_id: str - The model deployment id to block.
    TrA  ©r�   rQ  rA  rf  rj  N©r�  ©r�   r�  rQ  rj  s       r`   Úblock_modelr•  u  s.   è ø€ ô( +ØØ+ØØØ-ô÷ ð ð úó   ‚–—z/model/unblockc              ƒ   ó>   K  — t        | |dd|¬«      ƒ d{  –—† S 7 Œ­w)z™
    Unblock a DB-stored model deployment so it can serve requests again.

    Parameters:
    - model_id: str - The model deployment id to unblock.
    Fr�  r’  Nr“  r”  s       r`   Úunblock_modelr˜  ’  s.   è ø€ ô( +ØØ+ØØØ-ô÷ ð ð úr–  Únew_encryption_keyÚshould_create_model_in_dbzFprisma_models.LiteLLM_ProxyModelTable | LiteLLM_ProxyModelTable | Nonec              ƒ   óÊ  K  — | j                   j                  d¬«      }| j                   j                  }|j                  «       D ]!  \  }}t	        ||¬«      }	|	| j                   |<   Œ# | j
                  j                  | j                  | j                   j                  d¬«      | j
                  j                  d¬«      |j                  xs t        |j                  xs t        dœ}
| j
                  j                  �| j
                  j                  |
d<   t        d|
«      }|r/t        |«      j                  j                  |¬«      ƒ d {  –—† }|S t        di |
¤Ž}|S 7 Œ­w)	NTr~   )r×   r™  )ry   r{   r�   r}   Ú
created_byr`  ry   zMapping[str, object])r�   rp   )r�   r  rÈ   ró   r4   r}   Úidr{   r‚   r|  r   r   r@   r­   Úcreater%   )r9  rQ  r«   r™  rš  Ú_litellm_params_dictÚ_original_litellm_model_namer÷   rø   Úencrypted_valueÚ_dataÚ_create_dataÚmodel_responses                r`   Ú_add_model_to_dbr¥  µ  s[  è ø€ ð #/×"=Ñ"=×"BÑ"BÐPTÐ"BÓ"UÐØ*6×*EÑ*E×*KÑ*KÐ Ø$×*Ñ*Ó,ò 9‰ˆˆ1Ü.°QÐK]Ô^ˆØ)8ˆ×#Ñ# AÒ&ð9ð !×+Ñ+×.Ñ.Ø"×-Ñ-Ø&×5Ñ5×EÑEÐSWÐEÓXØ"×-Ñ-×=Ñ=È4Ð=ÓPØ'×/Ñ/ÒKÔ3KØ'×/Ñ/ÒKÔ3Kñ€Eð ×Ñ×!Ñ!Ð-Ø(×3Ñ3×6Ñ6ˆˆjÑÜÐ5°uÓ=€LÙ Ü.¨}Ó=×CÑC×JÑJÐP\ÐJÓ]×]ˆð Ðô 1Ñ9°5Ñ9ˆØÐð ^ús   ‚EE#ÅE!ÅE#c              ƒ   óT  K  — | j                   j                  }|€y| j                  }|r|| j                   _        d|› dt	        j
                  «       › �}|| _        t        | ||¬«      ƒ d{  –—† }|r.t        t        ||g¬«      t        ddi¬«      |¬	«      ƒ d{  –—†  |S 7 Œ67 Œ­w)
zú
    If 'team_id' is provided,

    - generate a unique 'model_name' for the model (e.g. 'model_name_{team_id}_{uuid})
    - store the model in the db with the unique 'model_name'
    - add the public model name to the team's allowed models list
    NÚmodel_name_Ú_©r9  rQ  r«   ©Úteam_idrZ   rÓ   Úhttp©Úscope©r�   r�  rQ  )
r}   r«  r{   Úteam_public_model_namer   Úuuid4r¥  r8   r.   r   )r9  rQ  r«   Ú_team_idÚoriginal_model_nameÚunique_model_namer¤  s          r`   Ú_add_team_model_to_dbrµ  Ô  sÍ   è ø€ ð #×-Ñ-×5Ñ5€HØÐØð ".×!8Ñ!8Ðñ
 Ø9Lˆ×ÑÔ6ð "-¨X¨J°a¼¿
¹
»°~ÐFÐØ/€LÔô #3Ø!Ø+Ø#ô#÷ €Nñ ÜÜ$Ø Ø+Ð,ôô !¨°Ð'7Ô8Ø/ô
÷ 	
ð 	
ð Ðð!øð	
ús$   ‚A+B(Á-B$Á./B(ÂB&ÂB(Â&B(c              ƒ   ó,  K  — ddl m} t        j                  ||||¬«      ƒ d{  –—†  |j                  �;t        |j                  j                  d¬«      «       t        t        | |¬«      «       t        t        | |¬«      |j                  r|j                  j                  d¬«      nt        ¬«       |j                  r|j                  j                  nd}|€t        | |¬	«      S t        || ¬
«      }|j                  €ddlm}  |«       |_        ||j                  _        | j                  r| j                  j                  nd}||k7  }	|	rt%        ||||¬«      ƒ d{  –—†  nt'        ||| |||¬«      ƒ d{  –—†  t        | |¬	«      S 7 �Œh7 Œ.7 Œ­w)zþ
    Handle team model updates with proper alias management.

    If patch_data contains a team_id:
    - Creates unique internal model_name and team alias
    - Adds model to team object
    - Preserves team_public_model_name for external reference
    r   )rU  r^  NTr~   )rº   rð   r)  )rº   r?  )rð   rº   )rW   )r«  Úpublic_model_namerð   rQ  )r«  r·  rº   rð   rQ  r«   )rp  rU  ru  Úallow_team_model_actionr}   r  r£   r	  rù   r'  r�   rò   r«  rN  Ú_get_public_model_nameÚlitellm.types.routerrW   r°  Ú _setup_new_team_model_assignmentÚ&_update_existing_team_model_assignment)
rº   rð   rQ  r«   rU  Úpatch_team_idr·  rW   Ú
db_team_idÚis_new_team_assignments
             r`   r{  r{    s§  è ø€ õ 8ä
#×
;Ñ
;ØØ+Ø#Ø!ð	 <ó ÷ ð ð ×ÑÐ(Ü/°
×0EÑ0E×0PÑ0PÐ^bÐ0PÓ0cÔdÜ Ô!7ÀÐV`Ô!aÔbÜ&Ü)°8È
ÔSàGQ×G`ÒG`ˆJ×%Ñ%×0Ñ0¸dÐ0ÔCÔfwõð =G×<QÒ<Q˜:×0Ñ0×8Ò8ÐW[€Mð ÐÜ¨À
ÔKÐKô  6ØØô Ðð ×ÑÐ$Ý2á )£ˆ
ÔØ3D€J×ÑÔ0ð 8@×7JÒ7J˜×+Ñ+×3Ò3ÐPT€JØ$.°-Ñ$?ÐáÜ.Ø!Ø/Ø!Ø/ô	
÷ 	
ñ 	
ô 5Ø!Ø/ØØ!Ø/Ø'ô
÷ 	
ð 	
ô  H¸JÔGÐGðCùðb	
øð	
ús4   ‚#F¥F¦D;FÅ!FÅ"FÅ;FÅ<FÆFÆFc                 ó˜  ‡— | j                   r| j                   j                  ndxs$ |j                   r|j                   j                  ndŠdt        dz  dt        fˆfd„}| j                  }|r ||«      s||j                  k7  r|S |j                   r,|j                   j
                  r|j                   j
                  S |j                  S )a  Determine the public model name from patch or existing model.

    The top-level ``model_name`` is the rename channel. For team-scoped rows
    the DB ``model_name`` column holds an internal routing key
    (``model_name_{team_id}_{uuid}``), and ``/model/info`` historically leaked
    it into the dashboard edit form, so a non-rename save (e.g. a TPM tweak)
    would PATCH the internal name and the update path would treat it as a
    rename -- overwriting ``team_public_model_name`` and rewriting the team ACL
    (see issue #28382).

    Guard against that by ignoring an incoming ``model_name`` that matches the
    internal shape, or is a no-op against the current DB column. Anything else
    is a genuine rename and wins. We deliberately do NOT read
    ``patch_data.model_info.team_public_model_name``: the dashboard passes the
    existing ``model_info`` blob through untouched on a rename, so honoring it
    would return the OLD public name and silently drop the rename.

    Precedence (highest first):
    1. patch_data.model_name -- a genuine rename: not internal-shape and not a
       no-op against db_model.model_name.
    2. db_model.model_info.team_public_model_name -- existing public name.
    3. db_model.model_name -- last-resort fallback for legacy rows.
    NÚnamert   c                 ó8   •— ‰�| sy| j                  d‰› d�«      S )NFr§  r¨  )Ú
startswith)rÁ  r«  s    €r`   Ú_is_internal_shapez2_get_public_model_name.<locals>._is_internal_shapez  s%   ø€ Øˆ?¡$ØØ�‰ ¨W¨I°QÐ7Ó8Ð8rq   )r}   r«  rm   r†   r{   r°  )rð   rº   rÄ  rõ   r«  s       @r`   r¹  r¹  [  s¹   ø€ ð6 8B×7LÒ7L�j×+Ñ+×3Ò3ÐRVò Ø'/×':Ò':ˆ×Ñ×#Ò#Àð ð9¤ t¡ð 9´õ 9ð
 !×+Ñ+€HÙÑ*¨8Ô4¸ÀX×EXÑEXÒ9XØˆà×Ò˜x×2Ñ2×IÒIØ×"Ñ"×9Ñ9Ð9à×ÑÐrq   r«  r·  c              ƒ   ó®   K  — d| › dt        j                  «       › �}||_        t        t	        | |g¬«      t        ddi¬«      |¬«      ƒ d{  –—†  y7 Œ­w)	z=Set up a new team model with unique name and team membership.r§  r¨  rª  rÓ   r¬  r­  r¯  N)r   r±  r{   r8   r.   r   )r«  r·  rð   rQ  r´  s        r`   r»  r»  ‰  s^   è ø€ ð "-¨W¨I°Q´t·z±z³|°nÐEÐØ-€JÔä
Ü ØØ%Ð&ô
ô  F¨FÐ#3Ô4Ø+ô÷ ò ús   ‚AAÁAÁAr­   c              ƒ   ó  K  — d| › d�}|xs t        |«      }|j                  dd|ii¬«      ƒ d{  –—† }|sg S g }|D ]@  }t        |j                  «      }|€Œ|j	                  d«      | k(  sŒ0|j                  |«       ŒB |S 7 ŒQ­w)aG  
    Fetch all deployments for a given team_id from the database.

    Centralizes team deployment queries to ensure consistent filtering and error handling.
    This is the established helper pattern for team deployment DB access in this module.

    Note: prisma-client-py 0.11.0 does not support JSON path filtering, so we filter
    by the model_name prefix (team models use "model_name_{team_id}_*") and confirm
    team_id in model_info with Python-side filtering.

    Pass ``table`` (a transaction's proxy-model table) to run the read inside an
    existing transaction.
    r§  r¨  r{   rÃ  r¹   Nr«  )r¯   rŽ   Úmodel_info_as_mappingr}   rþ   Úappend)r«  r«   r­   ÚprefixÚresponseÚresultÚrowr}   s           r`   Ú_get_team_deploymentsrÍ  �  s©   è ø€ ð  " ' ¨!Ð,€FØÒ6Ô'¨Ó6€EØ!ŸO™Oà˜<¨Ð0ð
ð ,ó ÷ €Hñ
 Øˆ	ð €FØò ˆÜ*¨3¯>©>Ó:ˆ
ØÑ! j§n¡n°YÓ&?À7Ó&JØ�M‰M˜#Õðð €Mðús   ‚/B±B²&BÁBÁ.BÚteam_idsrT  c              ƒ   óª  K  — g }|j                   j                  «       4 ƒd{  –—† }|}| D ]w  }t        |||j                  ¬«      ƒ d{  –—† }|D �cg c]  }|j                  ‘Œ }	}|	sŒ?|j                  j                  dd|	ii¬«      ƒ d{  –—†  |j                  |	«       Œy ddd«      ƒd{  –—†  |rt        t        «       d¬«      ƒ d{  –—†  |�<dd	l	m
}
 |
4 ƒd{  –—†  |D ]  }|j                  |¬
«       Œ ddd«      ƒd{  –—†  |S |S 7 Œñ7 ŒÌc c}w 7 Œ�7 Œn# 1 ƒd{  –—†7  sw Y   Œ~xY w7 Œf7 ŒU7 Œ.# 1 ƒd{  –—†7  sw Y   |S xY w­w)a  
    Delete every BYOK model owned by the given teams, from the DB and the router.

    The DB rows are removed inside a single transaction, so deletion is atomic
    across all team_ids. Each team's rows are deleted by the exact model_ids read
    in the same transaction, which keeps the deleted set identical to the set
    handed to the router. The router is synced only after the transaction commits,
    so a rollback can never leave a deployment live in the router without its row.

    Returns the model_ids that were deleted.
    N)r­   ry   Úinr¹   r™   )Úredis_cacheÚobject_typer   )ÚMODEL_RECONCILE_LOCK©r�  )r³   ÚtxrÍ  r™   ry   r•   Úextendr3   r2   rp  rÓ  Údelete_deployment)rÎ  r«   rT  Údeleted_model_idsÚtx_ctxrÕ  r«  ÚrowsrÌ  Ú	model_idsrÓ  ry   s               r`   Údelete_team_modelsrÜ  À  so  è ø€ ð  +-ÐØ×Ñ×"Ñ"Ó$÷ 4ð 4¨Ø$*ˆØò 	4ˆGÜ.¨w¸ÈR×MgÑMgÔh×hˆDØ15Ö6¨#˜Ÿ›Ð6ˆIÐ6ÚØ×0Ñ0×<Ñ<ÀJÐQUÐW`ÐPaÐCbÐ<Óc×cÐcØ!×(Ñ(¨Õ3ð	4÷4÷ 4ñ Ü#Ô0HÓ0JÐXqÔr×rÐrð
 ÐÝCà'÷ 	:ñ 	:Ø-ò :�Ø×,Ñ,°Ð,Õ9ð:÷	:÷ 	:ð ÐÐÐð-4øð iúÚ6àcøð4ø÷ 4÷ 4ñ 4úð 	søð	:øð 	:ø÷ 	:÷ 	:ð 	:ð ÐüsÎ   ‚!E£D¤E§#D"Á
DÁD"ÁDÁ&D"Á+#D"ÂDÂD"Â&EÂ1D Â2EÃD7ÃEÃ#D9Ã$EÃ'D=ÄEÄD;Ä	EÄD"ÄD"Ä EÄ"D4Ä(D+Ä)D4Ä0EÄ9EÄ;EÄ=EÅEÅEÅEc              ƒ   óÖ   K  — t        | |«      ƒ d{  –—† }t        «       }|D ]?  }t        |j                  «      }|€Œ|j	                  d«      }|sŒ/|j                  |«       ŒA |S 7 ŒT­w)a  
    Public model names currently backed by a deployment in the team.

    Called on delete (after the deployment row is removed) so a public name that is
    load-balanced across several deployments stays in team.models while a replica
    still serves it.
    Nr°  )rÍ  ÚsetrÇ  r}   rþ   Úadd)r«  r«   ÚdeploymentsÚpublic_namesrÌ  r}   Úpublic_names          r`   Ú_get_team_public_model_namesrã  ê  so   è ø€ ô  5°W¸mÓL×L€KÜ$'£E€LØò .ˆÜ*¨3¯>©>Ó:ˆ
ØÑ!Ø$Ÿ.™.Ð)AÓBˆKÚØ× Ñ  Õ-ð.ð Ðð Mús   ‚A)’A'“*A)¾A)ÁA)Úuser_api_key_cacheÚproxy_logging_objc           	   ƒ   ó  ‡K  — | j                   j                  }|€y‰duxr | j                  ‰j                  v }|rg nt	        | j                  |¬«      ƒ d{  –—† }|D ��	ch c]  \  }}	||k(  sŒ|	’Œ }
}}	| j                   j
                  �|
| j                   j
                  hz  n|
}|syt        ||¬«      ƒ d{  –—† }‰�t        ˆfd„|D «       «      n	t        «       }||z
  |z
  }|syt        |«      j                  d|i¬«      ƒ d{  –—† }|€yt        |«      j                  d|id|j                  D �cg c]	  }||vsŒ|‘Œ c}idd	i¬
«      ƒ d{  –—† }t        |||¬«      ƒ d{  –—†  y7 �Œc c}	}w 7 Œ¿7 Œqc c}w 7 Œ-7 Œ­w)a�  
    Strip a deleted team model's public name(s) from team.models and refresh the cache.

    Must be called after the deployment row is deleted: a public name is removed only
    when no remaining team deployment still backs it, so a load-balanced replica isn't
    revoked while siblings serve it, and concurrent deletes can't leave a ghost.

    Legacy team models (created before team_public_model_name existed) store a
    ``{public_name: "model_name_{team_id}_{uuid}"}`` entry in the team's model_aliases,
    so the alias scan runs for every team model; skipping it for internal-shaped names
    left stale aliases that rewrote requests to deployments that no longer exist.
    Aliases are scrubbed only when the deleted deployment's name no longer resolves in
    the router, so deleting one replica of a load-balanced group never breaks aliases
    that still route to the surviving replicas (in any team).

    A public name that still resolves to a live router deployment (e.g. a gateway-level
    model group shared with the team) is kept in team.models, so deleting a per-team
    duplicate does not revoke the team's access to the shared deployment.
    N)r·  r«   )r«  r«   c              3   ó@   •K  — | ]  }|‰j                   v sŒ|–— Œ y ­wrv   )Ú model_name_to_deployment_indices)rÄ   rÁ  rT  s     €r`   rÇ   z/_remove_unbacked_team_models.<locals>.<genexpr>4  s   øè ø€ Òj˜4°d¸j×>iÑ>iÒ6i”$Ñjùs   ƒ—r«  r¹   rZ   Úobject_permissionT)r‰   r�   r©   )Úteam_rowrä  rå  )r}   r«  r{   rè  Údelete_team_model_aliasr°  rã  rÊ   rµ   rŒ   r‘   rZ   r7   )r9  r«   rä  rå  rT  r«  Údeleted_name_still_servedÚremoved_model_aliasesÚalias_team_idÚaliasÚremoved_alias_namesÚcandidate_namesÚteam_backed_namesÚrouter_served_namesÚnames_to_removeÚexisting_team_rowrÈ   Úupdated_team_rows       `             r`   Ú_remove_unbacked_team_modelsr÷     sä  øè ø€ ð4 "×,Ñ,×4Ñ4€GØ€Øð 	˜$ÐÒi <×#:Ñ#:¸j×>iÑ>iÐ#ið ñ
 %ñ 	ä*Ø*×5Ñ5Ø'ô
÷ 
ð ð EZ×!vÑ,@¨M¸5Ð]jÐnuÓ]u¢%Ð!vÐÑ!vð ×"Ñ"×9Ñ9ÐEð 	˜|×6Ñ6×MÑMÐNÒNà ð ñ
 Øä%AÈ'ÐanÔ%o×oÐð Ð!ô 	Ój ?ÔjÔjä‹[ð ð
 -Ð/@Ñ@ÐCVÑV€OÙØä%3°MÓ%B×%NÑ%NÐV_ÐahÐUiÐ%NÓ%j×jÐØÐ Øä7EÀmÓ7T×7[Ñ7[Ø˜'Ð"ØÐ,=×,DÑ,DÖe 5ÈÐUdÒHdšÒeÐfØ$ dÐ+ð 8\ó 8÷ 2Ðô
 Ø!Ø-Ø+ô÷ ñ ðC
ûó
 "wð  pøð  kúò fð2øð
ús€   ƒAF
ÁE4Á	F
Á#E7Á1E7Á5AF
Â=E=Â>AF
ÄE?Ä-F
Ä;	FÅFÅ	F
ÅFÅF
Å.FÅ/F
Å7F
Å?F
ÆF
ÆF
c              ƒ   óÀ  K  — dt         dt        dz  fd„}|j                  r|j                  j                  nd}|rÛ||k7  rÖd|_        |€t        j                  d«       yt        | |«      ƒ d{  –—† }|D �	cg c]2  }	|	j                  |j                  k7  r ||	j                  «      |k(  r|	‘Œ4 }
}	t        t        | |g¬«      t        ddi¬	«      |¬
«      ƒ d{  –—†  |
sht        t        | |g¬«      t        ddi¬	«      |¬
«      ƒ d{  –—†  d|_        y|s0|r.t        t        | |g¬«      t        ddi¬	«      |¬
«      ƒ d{  –—†  d|_        y7 Œác c}	w 7 Œ}7 ŒO7 Œ­w)aŽ  Update an existing team model if the public name changed.

    Note on DB scan: Prisma's JSON filtering does not support compound AND conditions
    across multiple JSON paths, so we fetch all deployments for the team and filter
    team_public_model_name in Python. For teams with many deployments this scan grows
    linearly; if team deployment counts become large this should be revisited.
    r}   rt   Nc                 óh   — t        | «      }|€y |j                  d«      }t        |t        «      r|S d S )Nr°  )rÇ  rþ   r  rm   )r}   Úparsedr×   s      r`   Ú_get_team_public_model_namezK_update_existing_team_model_assignment.<locals>._get_team_public_model_name\  s9   € ô .¨jÓ9ˆØˆ>ØØ—z‘zÐ":Ó;ˆÜ" 5¬#Ô.ˆuÐ8°DÐ8rq   z]prisma_client not initialized; skipping public name update entirely to avoid orphaned entriesrª  rÓ   r¬  r­  r¯  )r…   rm   r}   r°  r{   r   ÚwarningrÍ  r8   r.   r   r9   r/   )r«  r·  rº   rð   rQ  r«   rû  Úold_public_nameÚteam_deploymentsÚdÚother_deployments_with_old_names              r`   r¼  r¼  L  sª  è ø€ ð 9Üð9ä	ˆt‰ó9ð LT×K^ÒK^˜X×0Ñ0×GÒGÐdh€OáÐ,°Ò?ð !%ˆ
ÔØÐ Ü ×(Ñ(Øoôð ô )>¸gÀ}Ó(U×"UÐð &ö2
àØ�|‰|˜x×2Ñ2Ò2Ñ7RÐST×S_ÑS_Ó7`ÐdsÒ7sò ð2
Ð'ð 2
ô Ü$ØØ)Ð*ôô !¨°Ð'7Ô8Ø/ô
÷ 	
ð 	
ñ /Ü#Ü+Ø#Ø+Ð,ôô %¨F°FÐ+;Ô<Ø"3ô÷ ð ð0 !€JÕñ! Ñ!2ô Ü$ØØ)Ð*ôô !¨°Ð'7Ô8Ø/ô
÷ 	
ð 	
ð !€JÕðU #Vúò2
ð	
øðøð	
úsT   ‚A-EÁ/EÁ0EÁ87EÂ/+EÃEÃ/EÄ
EÄ9EÅEÅEÅEÅEÅEc                   ó®   — e Zd ZdZe	 	 ddedededz  dede	d   f
d	„«       Z
ed
eez  dededede	d   f
d„«       Ze	 dd
ededededede	d   fd„«       Zy)ru  z;
    Common auth checks for model management endpoints
    Nr«  rQ  Úteam_objrU  rt   Tc           	      ó  — |du r't        ddt        j                  j                  i¬«      ‚|j                  r|j                  t
        j                  k(  ry|�t        ||¬«      s t        ddd| › d|j                  › d	�i¬«      ‚y)
NFé“  r  r  T)rQ  r  zTeam ID=z& does not match the API key's team ID=z^, OR you are not the admin for this team. Check `/user/info` to verify your team admin status.)	r   r$   Únot_premium_userr×   rw  r(   rx  r6   r«  ©r«  rQ  r  rU  s       r`   Úcan_user_make_team_model_callz7ModelManagementAuthChecks.can_user_make_team_model_call¤  s¥   € ð ˜5Ñ ÜØØÔ!2×!CÑ!C×!IÑ!IÐJôð ð ×&Ò&Ð+<×+FÑ+FÔJZ×JfÑJfÒ+fØØÐÔ%8ÐK\ÐgoÕ%pÜØà˜x¨ yÐ0VÐWh×WpÑWpÐVqð  rPð  Qðôð ð rq   r9  r«   c              ƒ   ó<  K  — | j                   �| j                   j                  €y| j                   j                  �+|dur't        ddt        j                  j
                  i¬«      ‚t        |«      j                  d| j                   j                  i¬«      ƒ d {  –—† }|€'t        ddd| j                   j                  › d	�i¬«      ‚t        j                  |j                  «       «      }t        j                  | j                   j                  |||¬
«       y7 Œ~­w)NTr  r  r  r«  r¹   r   úTeam id=ú does not exist in dbr  )r}   r«  r   r$   r  r×   r±   rŒ   r&   Úmodel_validater£   ru  r  )r9  rQ  r«   rU  Ú_existing_team_rowrõ  s         r`   r¸  z1ModelManagementAuthChecks.allow_team_model_action»  s'  è ø€ ð ×"Ñ"Ð*¨l×.EÑ.E×.MÑ.MÐ.UØØ×"Ñ"×*Ñ*Ð6¸<ÈtÑ;SÜØØÔ!2×!CÑ!C×!IÑ!IÐJôð ô
 +;¸=Ó*I×*UÑ*UØ˜l×5Ñ5×=Ñ=Ð>ð +Vó +
÷ %
Ðð Ð%ÜØØ 8¨L×,CÑ,C×,KÑ,KÐ+LÐLaÐ!bÐcôð ô $5×#CÑ#CÐDV×DaÑDaÓDcÓ#dÐä!×?Ñ?Ø ×+Ñ+×3Ñ3Ø/Ø&Ø%ð	 	@ô 	
ð ð#%
ús   ‚BDÂDÂA?DÚallow_missing_teamc              ƒ   óŠ  K  — | j                   �÷| j                   j                  �át        |«      j                  d| j                   j                  i¬«      ƒ d {  –—† }|€V|r-|j                  t
        j                  k(  ryt        dddi¬«      ‚t        ddd	| j                   j                  › d
�i¬«      ‚t        j                  |j                  «       «      }t        j                  | j                   j                  |||¬«      S |j                  t
        j                  k7  rt        ddd|j                  › d�i¬«      ‚y7 Œç­w)Nr«  r¹   Tr  r  zBOnly a proxy admin can delete a model whose team has been deleted.r  r   r	  r
  r  zAUser does not have permission to make this model call. Your role=z. You can only make model calls if you are a PROXY_ADMIN or if you are a team admin, by specifying a team_id in the model_info.)r}   r«  r±   rŒ   rw  r(   rx  r   r&   r  r£   ru  r  )r9  rQ  r«   rU  r  Úteam_obj_rowr  s          r`   rv  z2ModelManagementAuthChecks.can_user_make_model_callÝ  sj  è ø€ ð ×"Ñ"Ð.°<×3JÑ3J×3RÑ3RÐ3^Ü(8¸Ó(G×(SÑ(SØ  ,×"9Ñ"9×"AÑ"AÐBð )Tó )÷ #ˆLð Ð#ñ &Ø(×2Ñ2Ô6F×6RÑ6RÒRØ#Ü'Ø$'Ø 'Ð)mÐnôð ô $Ø #Ø# x°×0GÑ0G×0OÑ0OÐ/PÐPeÐ%fÐgôð ô 0×>Ñ>¸|×?VÑ?VÓ?XÓYˆHä,×JÑJØ$×/Ñ/×7Ñ7Ø"3Ø!Ø)ð	 Kó ð ð ×(Ñ(Ô,<×,HÑ,HÒHÜØàÐ`Ðar×a|Ña|Ð`}ð  ~}ð  ~ðôð ð ðG#ús   ‚AEÁEÁC(E)NF)F)rh   ri   rj   rk   Ústaticmethodrm   r0   r&   r†   r   r  rU   rX   r>   r¸  rv  rp   rq   r`   ru  ru  Ÿ  sü   „ ñð ð .2Ø"ñ	Øðà)ðð $ dÑ*ðð ð	ð
 
�‰òó ðð, ðØ Ð#3Ñ3ðà)ðð $ðð ð	ð
 
�‰òó ððB ð $)ñ.Ø ð.à)ð.ð $ð.ð ð	.ð
 !ð.ð 
�‰ò.ó ñ.rq   ru  z/model/deletez;Allows deleting models in the model list in the config.yaml)rd   rO  rP  c              ƒ   óà  K  — ddl m} 	 	 ddl m}m}m}m}m}m}m} |€t        dddi¬«      ‚t        |«      j                  d| j                  i¬	«      ƒ d {  –—† }	|	€t        d
dd| j                  › d�i¬«      ‚t        d!i |	j                  «       ¤Ž}
t        j                  |
|||d¬«      ƒ d {  –—†  |du �r$	 t        |«      j!                  d| j                  i¬	«      ƒ d {  –—† }|€t        d
dd| j                  › d�i¬«      ‚|�7|4 ƒd {  –—†  |j#                  | j                  ¬«       d d d «      ƒd {  –—†  |
j$                  j&                  �t)        |
||||¬«      ƒ d {  –—†  t+        j,                  t/        | j                  d|t0        j2                  |j5                  d¬«      d |j6                  t8        ¬«      «       dd|j:                  › d�iS t        dddi¬«      ‚7 �Œ—7 �Œ@7 �Œ7 Œä7 Œº# 1 ƒd {  –—†7  sw Y   ŒÊxY w7 Œ£# t<        $ rÏ}t?        j@                  d|«       tC        |t        «      rPtE        tG        |dd|› d�«      tH        jJ                  tG        |dd«      tG        |dtL        jN                  «      ¬«      ‚tC        |tD        «      r|‚tE        d tQ        |«      z   tH        jJ                  tG        |dd«      tL        jN                  ¬«      ‚d }~ww xY w­w)"Nr   ©rT  )rÓ  rT  rU  r«   rå  rV  rä  rW  r  úVNo DB Connected. Here's how to do it - https://docs.litellm.ai/docs/proxy/virtual_keysr  ry   r¹   r   zModel with id=z not found in dbT)r9  rQ  r«   rU  r  rÔ  )r9  r«   rä  rå  rT  Údeletedr~   rd  rÒ   zModel: z deleted successfullyúCSet `'STORE_MODEL_IN_DB='True'` in your env to enable this feature.z)Failed to delete model. Due to error - %sr  úAuthentication Error(ú)rÕ   ÚNoner  ©rÒ   rÓ   rÕ   rÔ   úAuthentication Error, rp   ))rp  rT  rÓ  rU  r«   rå  rV  rä  r   r¯   rŒ   r�  rU   r£   ru  rv  r“   r×  r}   r«  r÷  r  r€  r;   r'   r�  r‚   r|  r   ry   rƒ  r   r„  r  r,   rÃ   r+   ry  r   rØ   rm   )r}   rQ  rT  rÓ  rU  r«   rå  rV  rä  Úmodel_in_dbr9  rË  r‹  s                r`   Údelete_modelr    s1  è ø€ õ 6ðr
ð	÷	
÷ 	
ñ 	
ð Ð ÜØàÐuðôð ô $6°mÓ#D×#PÑ#PÐXbÐdn×dqÑdqÐWrÐ#PÓ#s×sˆØÐÜØØ >°*·-±-°Ð@PÐ!QÐRôð ô
 )ÑD¨;×+AÑ+AÓ+CÑDˆÜ'×@Ñ@Ø%Ø/Ø'Ø%Ø#ð Aó 
÷ 	
ð 	
ð  Ò$ðô
 #5°]Ó"C×"JÑ"JÐR\Ð^h×^kÑ^kÐQlÐ"JÓ"m×mˆFàˆ~Ü#Ø #Ø# ~°j·m±m°_ÐDTÐ%UÐVôð ð Ð%Ø/÷ Cñ CØ×0Ñ0°J·M±MÐ0ÔB÷C÷ Cð ×&Ñ&×.Ñ.Ð:Ü2Ø!-Ø"/Ø'9Ø&7Ø)ô÷ ð ô ×ÑÜ'Ø(Ÿm™mØ$Ø&7Ü0×GÑGØ!'×!7Ñ!7ÀTÐ!7Ó!JØ $Ø'8×'@Ñ'@Ü-Eô	ôð  ¨¯©Ð(9Ð9NÐOÐPÐPäØØÐ!fÐgôð ðG tùð	
ùð nùð Cøð Cø÷ C÷ Cñ Cúð
ùô8 ò 
Ü×&Ñ&Ð'RÐTUÔVÜ�aœÔ'Ü Ü  8Ð/DÀQÀCÀqÐ-IÓJÜ$×/Ñ/Ü˜a ¨&Ó1Ü˜Q ¬v×/JÑ/JÓKô	ð ô ˜œ>Ô*ØˆGÜØ,¬s°1«vÑ5Ü ×+Ñ+Ü˜!˜W fÓ-Ü×,Ñ,ô	
ð 	
ûð
üs¶   ‚K.ŠAH ÁG/ÁAH Â2G2Â34H Ã'G5Ã(+H ÄG8ÄH ÄG<Ä4H Ä?G:Å -H Å-HÅ.A1H ÇK.Ç H Ç2H Ç5H Ç8H Ç:H Ç<HÈHÈHÈ
H È	K+ÈC
K&Ë&K+Ë+K.c           
   ƒ   óŠ  K  — t        |«      j                  ddi¬«      ƒ d{  –—† }g }g }|D ]ñ  }t        d|j                  «      }|j                  }| |j                  «       v sŒ8t        |j                  «       «      t        |j                  «       «      j                  | «         }|j                  �'|j                  |j                  j                  |f«       ||= |j                  t        |«      j                  d|idt        j                  |«      i¬«      «       Œó t        j                   |Ž ƒ d{  –—†  |S 7 �Œ7 Œ	­w)	zÆ
    Delete a team model alias

    Iterate through all team model aliases and delete the one that matches the model_id

    Returns:
    - List of team id + model alias pairs that were removed
    ÚteamT)r©   Nzdict[str, str]r�  Úmodel_aliasesrb  )r·   rŽ   r   r  r�  r  rl   ÚkeysÚindexr  rÈ  r«  r‘   rE  rF  r  Úgather)	r·  r«   Úteam_model_aliasesÚtasksrí  Úteam_model_aliasr  r�  r=  s	            r`   rë  rë  ‘  sB  è ø€ ô '9¸Ó&G×&QÑ&QÐ[aÐcgÐZhÐ&QÓ&i× iÐØ€EØ:<ÐØ.ò ÐÜØÐ.×<Ñ<ó
ˆð × Ñ ˆà × 4Ñ 4Ó 6Ò6Ü�}×)Ñ)Ó+Ó,¬T°-×2FÑ2FÓ2HÓ-I×-OÑ-OÐPaÓ-bÑcˆCØ×$Ñ$Ð0Ø%×,Ñ,Ð.>×.CÑ.C×.KÑ.KÈSÐ-QÔRØ˜cÐ"Ø�L‰LÜ" =Ó1×8Ñ8Ø ˜*Ø)¬4¯:©:°mÓ+DÐEð 9ó õðô" �.‰.˜%Ð
 × Ð à Ð ð- !jùð( !ús)   ‚!E£D>¤AEÁ&CEÄ7EÄ8EÅEz
/model/newz=Allows adding new models to the model list in the config.yamlc              ƒ   ó  K  — ddl m}m}m}m}m}m} 	 |€t        dddi¬«      ‚t        j                  | |||¬«      ƒ d{  –—†  t        | j                  d¬	«       t        | j                  t        |j                  t        d
«      «      ¬«       d}| j                   j#                  d¬«      }	t%        |	«       t'        |	«       t)        | «      }
|du rØ	 t+        «       }t-        dd¬«      }	 | j.                  }| j                   j0                  €t3        |
||¬«      ƒ d{  –—† }nt5        |
||¬«      ƒ d{  –—† }|j7                  ||¬«      ƒ d{  –—† }|j                  dg «      xs g }d|v r:|j8                  j;                  |
j.                  ||
j                   ¬«      ƒ d{  –—†  nt        dddi¬«      ‚|€t        dddi¬«      ‚tC        jD                  tG        |jH                  d|tJ        jL                  dtO        |tP        «      r|jS                  d¬«      nd|jT                  tV        ¬«      «       tY        ||jH                  t[        |dd«      fgd|j\                  |j^                  ¬«       |S 7 �Œ77 �Œ[7 �ŒG7 �Œ/7 ŒÝ# t<        $ r }t?        j@                  d|«       Y d}~Œïd}~ww xY w# t<        $ rÏ}t?        j@                  d|«       tO        |t        «      rPta        t[        |dd|› d�«      tb        jd                  t[        |d d!«      t[        |d"tf        jh                  «      ¬#«      ‚tO        |t`        «      r|‚ta        d$tk        |«      z   tb        jd                  t[        |d d!«      tf        jh                  ¬#«      ‚d}~ww xY w­w)%a’  
    Add a new model to the proxy.

    Parameters:
    - model_name: str - The name users will use to call this model (required)
    - litellm_params: dict - LiteLLM-specific parameters (required)
        - model: str - The actual model identifier, e.g., "azure/my-deployment-name" (required - this is the only required field in litellm_params)
        - api_key: str - API key for the provider (optional)
        - api_base: str - API base URL (optional)
        - Other optional params: api_version, timeout, max_retries, etc.
    - model_info: dict - Additional model metadata returned in /v1/model/info (optional)

    Example curl:

    ```bash
    curl -L -X POST 'http://0.0.0.0:4000/model/new'     -H 'Authorization: Bearer LITELLM_VIRTUAL_KEY'     -H 'Content-Type: application/json'     -d '{
      "model_name": "my-azure-model",
      "litellm_params": {
        "model": "azure/my-deployment-name",
        "api_key": "my-azure-api-key",
        "api_base": "https://my-endpoint.openai.azure.com"
      },
      "model_info": {
        "my_custom_key": "my_custom_value"
      }
    }'
    ```

    Returns:
    - The created model entry with model_id
    r   )Úgeneral_settingsrU  r«   Úproxy_configrå  rV  NrW  r  r  r  r^  rÐ   F)r�   rà   Tr~   ©rn  ro  r©  ©r«   rå  ÚalertingÚslack)r{   Úlitellm_model_nameÚpassed_model_infozException in add_new_model: %sr  zCFailed to add model to db. Check your server logs for more details.Úcreatedrd  r}   rž  rk  zBlitellm.proxy.proxy_server.add_new_model(): Exception occured - %sr  r  r  rÕ   r  r  r  r  )6rp  r'  rU  r«   r(  rå  rV  r   ru  rv  rÚ   r�   rç   r†   rþ   rÜ   r}   r£   r  r	  r>  r}  r-   r{   r«  r¥  rµ  Úadd_deploymentÚslack_alerting_instanceÚmodel_added_alertrƒ  r   r„  r  r€  r;   ry   r'   r�  r  r   r‚   r|  r   r‚  rÃ   rn  ro  r,   r+   ry  r   rØ   rm   )r9  rQ  r'  rU  r«   r(  rå  rV  r¤  rú   Úpriced_model_paramsr‰  rŠ  r   Ú	_alertingr‹  s                   r`   Úadd_new_modelr5  ·  sÇ  è ø€ ÷X÷ ð}
ØÐ ÜØàÐuðôð ô (×@Ñ@Ø%Ø/Ø'Ø%ð	 Aó 
÷ 	
ð 	
ô 	2Ø(×7Ñ7Ø õ	
ô
 	3Ø'×6Ñ6ÜÐ*×.Ñ.Ô/SÐUZÓ[Ó\õ	
ð
 bfˆà%1×%<Ñ%<×%GÑ%GÐUYÐ%GÓ%ZÐÜ/Ð0CÔDÜ Ð!4Ô5Ü%;¸LÓ%IÐà Ñ$ðô
 )@Ó(AÐÜ/?ÈdÐ_cÔ/dˆNðTØ6B×6MÑ6MÐ,Ø×*Ñ*×2Ñ2Ð:Ü+;Ø%8Ø*;Ø&3ô,÷ &‘Nô ,AØ%8Ø*;Ø&3ô,÷ &�Nð
 (4×'BÑ'BØ"/ÐCTð (Có (÷ "�ð $4×#7Ñ#7¸
ÀBÓ#GÒ#MÈ2�	Ø˜iÑ'à+×CÑC×UÑUØ#6×#AÑ#AØ+GØ*=×*HÑ*Hð Vó ÷ ð øô  ØØÐ!fÐgôð ð
 Ð!ÜØØÐ!fÐgôð ô 	×ÑÜ#Ø(×1Ñ1Ø Ø"3Ü,×CÑCØ!äISÐTbÔdmÔIn�N×2Ñ2ÀÐ2ÔEÐtxà#4×#<Ñ#<Ü)Aôô	
ô 	)Ø%Ø+×4Ñ4´g¸nÈlÐ\`Ó6aÐbÐcØØ(×6Ñ6Ø%×0Ñ0õ	
ð ÐðC	
ùðF&ùð&ùð
"ùðùô
 ò TÜ$×.Ñ.Ð/OÐQR×SÑSûðTûôR ò 
Ü×&Ñ&Ð'kÐmnÔoÜ�aœÔ'Ü Ü  8Ð/DÀQÀCÀqÐ-IÓJÜ$×/Ñ/Ü˜a ¨&Ó1Ü˜Q ¬v×/JÑ/JÓKô	ð ô ˜œ>Ô*ØˆGÜØ,¬s°1«vÑ5Ü ×+Ñ+Ü˜!˜W fÓ-Ü×,Ñ,ô	
ð 	
ûð
üs°   ‚N”-J2 ÁI8ÁB*J2 Ã-3J Ä I;Ä!J Ä7I>Ä8J ÅJÅAJ Æ&JÆ'J Æ+CJ2 É7NÉ8J2 É;J É>J ÊJ ÊJ Ê	J/ÊJ*Ê%J2 Ê*J/Ê/J2 Ê2	N
Ê;C
NÎN
Î
Nz/model/updatezEdit existing model paramsc              ƒ   óä  K  — ddl m}m}m}m}m} 	 |€t        dddi¬«      ‚d}t        | dd«      }|€t        d	«      ‚|j                  }|€t        d
«      ‚t        |«      j                  j                  d|i¬«      ƒ d{  –—† }	|	€.|�!|j                  |¬«      �t        dddi¬«      ‚t        d«      ‚t        d'i |	j                  «       ¤Ž}
t         j#                  |
|||¬«      ƒ d{  –—†  t%        | j&                  |
j&                  ¬«       |du �rót)        d|	«      }t+        |j&                  «      }| j&                  €t        d«      ‚| j&                  j+                  d¬«      }|j-                  «       D ]   \  }}t/        |¬«      }|| j&                  |<   Œ" i }| j&                  j+                  «       }|j-                  «       D ]  \  }}|�|||<   Œ||v r||   �	||   ||<   Œ Œ! t1        j2                  |«      |j4                  xs |dœ}t7        |«      j9                  d|i|¬«      ƒ d{  –—† }t;        «       }t=        «       ƒ d{  –—† }t?        j@                  tC        |d|tD        jF                  tI        |tJ        «      r|jM                  d¬«      ndtI        |tJ        «      r|jM                  d¬«      nd|j4                  |¬«      «       tO        ||t        |dd«      fgd|jP                  |jR                  ¬«       |S y7 �Œ‰7 �Œ"7 ŒÝ7 ŒÃ# t        $ rÏ}tU        jV                  d|«       tI        |t        «      rPtY        t        |dd |› d!�«      tZ        j\                  t        |d"d#«      t        |d$t^        j`                  «      ¬%«      ‚tI        |tX        «      r|‚tY        d&tc        |«      z   tZ        j\                  t        |d"d#«      t^        j`                  ¬%«      ‚d}~ww xY w­w)(z‚
    Old endpoint for model update. Makes a PUT request.

    Use `/model/{model_id}/update` to PATCH the stored model in db.
    r   )r   rT  rU  r«   rV  NrW  r  r  r  r}   zmodel_info not providedzmodel_info.id not providedry   r¹   rZ  r   zOCan't edit model. Model in config. Store model in db via `/model/new`. to edit.zmodel not foundr^  rÐ   Tr›   zlitellm_params not providedr~   r  )r�   r`  rb  rc  rd  r‘   rk  zAlitellm.proxy.proxy_server.update_model(): Exception occured - %sr  r  r  rÕ   r  r  r  r  rp   )2rp  r   rT  rU  r«   rV  r   rÃ   rƒ  r�  r@   r­   rŒ   rr  rU   r£   ru  rv  rÚ   r�   r   r  ró   r4   rE  rF  r|  r¯   r‘   r}  r~  r  r€  r;   r'   r�  r  r   r‚   r‚  rn  ro  r   r„  r,   r+   ry  r   rØ   rm   )r9  rQ  r   rT  rU  r«   rV  Ú	_model_idÚ_model_infoÚ_existing_litellm_paramsÚ
deploymentÚexisting_model_rowÚ_existing_litellm_params_dictÚ_new_litellm_params_dictr÷   rø   r¡  Úmerged_dictionaryÚ_mpr=  r×   r¢  r¤  r‰  rŠ  r‹  s                             r`   Úupdate_modelr@  m  sO  è ø€ ÷õ ðB
ØÐ ÜØàÐuðôð ð !%ˆ	Ü/6°|À\ÐSWÓ/XˆØÐÜÐ5Ó6Ð6à—N‘Nˆ	ØÐÜÐ8Ó9Ð9ä)8¸Ó)G×)MÑ)M×)YÑ)YÐakÐmvÐ`wÐ)YÓ)x×#xÐ à#Ð+ØÐ%¨*×*CÑ*CÈYÐ*CÓ*WÐ*cÜ#Ø #Ø#Ð%vÐwôð ô
  Ð 1Ó2Ð2Ü&ÑOÐ)A×)LÑ)LÓ)NÑOˆ
ä'×@Ñ@Ø#Ø/Ø'Ø%ð	 Aó 
÷ 	
ð 	
ô 	2Ø(×7Ñ7Ø&×5Ñ5õ	
ð  Ò$Ü(,Ø#Ð%=ó)Ðô 48Ð8J×8YÑ8YÓ3ZÐ)à×*Ñ*Ð2ÜÐ =Ó>Ð>à.:×.IÑ.I×.NÑ.NÐ\`Ð.NÓ.aÐ$ð 1×6Ñ6Ó8ò A‘��1Ü"6¸QÔ"?�Ø1@�×+Ñ+¨AÒ.ðAð
 (*ÐØ%×4Ñ4×9Ñ9Ó;ˆCà!Ÿi™i›kò ‘
��UØÐ$Ø-2Ð% cÒ*ØÐ9Ñ9Ð>[Ð\_Ñ>`Ð>lØ-JÈ3Ñ-OÐ% cÒ*àðô #'§*¡*Ð->Ó"?Ø/×7Ñ7ÒSÐ;Sñ,ˆEô +=¸]Ó*K×*RÑ*RØ! 9Ð-Øð +Só +÷ %ˆNô )@Ó(AÐÜ*5«-×$7ˆNä×ÑÜ'Ø'Ø$Ø&7Ü0×GÑGô &Ð&8¼)ÔDð +×:Ñ:ÈÐ:ÔMà!ô & n´iÔ@ð '×6Ñ6ÀDÐ6ÔIà!à'8×'@Ñ'@Ø-Eô!ôô* -Ø)Ø!*¬G°NÀLÐRVÓ,WÐ XÐYØØ,×:Ñ:Ø)×4Ñ4õð "Ð!ðM %ð3 $yùð	
ùðZ%øð %8ùô@ ò 
Ü×&Ñ&Ð'jÐlmÔnÜ�aœÔ'Ü Ü  8Ð/DÀQÀCÀqÐ-IÓJÜ$×/Ñ/Ü˜a ¨&Ó1Ü˜Q ¬v×/JÑ/JÓKô	ð ô ˜œ>Ô*ØˆGÜØ,¬s°1«vÑ5Ü ×+Ñ+Ü˜!˜W fÓ-Ü×,Ñ,ô	
ð 	
ûð
üsu   ‚O0’A0L ÂLÂA)L Ã,LÃ-EL È3LÈ4L ÉLÉB9L Ì	O0ÌL ÌL ÌL ÌL Ì	O-ÌC
O(Ï(O-Ï-O0z/model_group/make_publicz$Update which model groups are publicÚrequestc              ƒ   óØ  K  — 	 ddl }ddlm}m} |j                  t
        j                  k7  rt        ddd|j                  › �i¬«      ‚|durt        d	dd
i¬«      ‚|j                  «       ƒ d{  –—† }d|vri |d<   | j                  |d   d<   |j                  |¬«      ƒ d{  –—†  | j                  |_        t        j                  d| j                  |j                  «       d| j                  |j                  dœS 7 Œ�7 Œ\# t        $ r[}t        j                   d|«       t#        |t        «      r|‚t%        d|› �t&        j(                  t*        j,                  d¬«      ‚d}~ww xY w­w)a  
    Update which model groups are public.

    This endpoint allows admins to specify which model groups should be publicly accessible.
    Public model groups are visible via the /public/model_hub endpoint.

    Args:
        request: Request containing list of model group names to make public
        user_api_key_dict: User authentication information

    Returns:
        Success message with updated public model groups

    Raises:
        ProxyException: For various error conditions including authentication errors
    r   N)r(  rV  r  r  ú<Only proxy admins can update public model groups. Your role=r  TrW  r  Úlitellm_settingsÚpublic_model_groups©Ú
new_configz.Updated public model groups to: %s by user: %sz(Successfully updated public model groups)rÒ   rE  r`  ú&Error updating public model groups: %sú$Error updating public model groups: rÑ   )Úlitellmrp  r(  rV  rw  r(   rx  r   Ú
get_configre   Úsave_configrE  r   Údebugr|  rƒ  r„  r  r,   r+   r…  r   r†  )rA  rQ  rJ  r(  rV  Úconfigr‹  s          r`   Úupdate_public_model_groupsrO  	  sŠ  è ø€ ð4;
ãßNð ×&Ñ&Ô*:×*FÑ*FÒFÜØàÐ[Ð\m×\wÑ\wÐ[xÐyðôð ð  DÑ(ÜØØÐ!fÐgôð ð +×5Ñ5Ó7×7ˆð  VÑ+Ø)+ˆFÐ%Ñ&à<C×<PÑ<PˆÐ!Ñ"Ð#8Ñ9ð ×&Ñ&°&Ð&Ó9×9Ð9ð '.×&:Ñ&:ˆÔ#ä×"Ñ"Ø<¸g×>RÑ>RÐTe×TmÑTmô	
ð
 BØ#*×#7Ñ#7Ø+×3Ñ3ñ
ð 	
ð' 8øð 	:ùô  ò 
Ü×&Ñ&Ð'OÐQRÔSä�aœÔ'ØˆGäØ:¸1¸#Ð>Ü ×6Ñ6Ü×6Ñ6Øô	
ð 	
ûð
üsO   ‚E*„A+D Á/C?Á04D Â$DÂ%AD Ã>E*Ã?D ÄD Ä	E'ÄAE"Å"E'Å'E*z/model_hub/update_useful_linkszUpdate useful linksc              ƒ   ó®  K  — 	 ddl }ddlm} |j                  t        j
                  k7  rt        ddd|j                  › �i¬«      ‚|j                  «       ƒ d{  –—† }d|vri |d<   | j                  |d   d	<   |j                  |¬
«      ƒ d{  –—†  | j                  |_
        t        j                  d| j                  |j                  «       d| j                  |j                  dœS 7 Œ�7 Œ\# t        $ r[}t        j                  d|«       t!        |t        «      r|‚t#        d|› �t$        j&                  t(        j*                  d¬«      ‚d}~ww xY w­w)z
    Update useful links.
    r   N)r(  r  r  rC  r  rD  Úpublic_model_groups_linksrF  z'Updated useful links to: %s by user: %sz!Successfully updated useful links)rÒ   Úuseful_linksr`  rH  rI  rÑ   )rJ  rp  r(  rw  r(   rx  r   rK  rR  rL  rQ  r   rM  r|  rƒ  r„  r  r,   r+   r…  r   r†  )rA  rQ  rJ  r(  rN  r‹  s         r`   Úupdate_useful_linksrS  a  sf  è ø€ ð4
ãÝ;ð ×&Ñ&Ô*:×*FÑ*FÒFÜØàÐ[Ð\m×\wÑ\wÐ[xÐyðôð ð +×5Ñ5Ó7×7ˆð  VÑ+Ø)+ˆFÐ%Ñ&àBI×BVÑBVˆÐ!Ñ"Ð#>Ñ?ð ×&Ñ&°&Ð&Ó9×9Ð9ð -4×,@Ñ,@ˆÔ)ä×"Ñ"Ø5°w×7KÑ7KÐM^×MfÑMfô	
ð
 ;Ø#×0Ñ0Ø+×3Ñ3ñ
ð 	
ð' 8øð 	:ùô  ò 
Ü×&Ñ&Ð'OÐQRÔSä�aœÔ'ØˆGäØ:¸1¸#Ð>Ü ×6Ñ6Ü×6Ñ6Øô	
ð 	
ûð
üsO   ‚E„AC. ÁC*Á4C. ÂC,ÂAC. Ã)EÃ*C. Ã,C. Ã.	EÃ7AEÅEÅEÚtier_labels.c                 óð   — | sy	 t        t        j                  | «      ¬«      j                  «       S # t        t
        f$ r4}t        d|› �t        j                  t        j                  d¬«      |‚d}~ww xY w)a(  Resolve the tier_labels query param into the labeled tiers the rubric is built from.

    Validated through ComplexityRouterConfig so the editor prefills what the router would send: the
    same field validators that reject a blank, duplicated, or canonical-name-stealing label on the
    write path reject it here, rather than this returning a rubric no router could be configured to
    use. A malformed value is the caller's error, so it surfaces as a 400.

    None when unset, letting classification_system_prompt apply its own default names.
    N)rT  z@tier_labels must be a JSON object of tier name to display name: rT  rÑ   )rG   rE  ÚloadsÚlabeled_tiersr   r   r,   r+   Úbad_request_errorr   rØ   )rT  r‹  s     r`   Ú_labeled_tiers_from_queryrY  ¥  st   € ñ ØðÜ%´$·*±*¸[Ó2IÔJ×XÑXÓZÐZøÜœ_Ð-ò ÜØVÐWXÐVYÐZÜ ×2Ñ2Ü×,Ñ,Øô	
ð
 ð	ûðús   …,2 ²A5Á/A0Á0A5c                   ó|   — e Zd ZU dZeedf   ed<   eZe	e
 ed¬«      f   ed<   dZedz  ed<     ed«      e«      Zy)	Ú(AutoRouterClassifierPromptPreviewRequestz†A POST rather than query params: classification_prompt is the operator's own text, which must
    not reach access logs through a URL..Útier_definitionsr   )ÚgeÚcontext_window_sizeNÚclassification_prompt)rh   ri   rj   rk   rä   rI   rn   rE   r^  r	   r–   r   r_  rm   r   rL   Ú_normalize_promptrp   rq   r`   r[  r[  ¼  sU   … ñ,ð ˜N¨CÐ/Ñ0Ó0Ø7]Ð˜ 3©°¬Ð#3Ñ4Ó]Ø(,Ð˜3 ™:Ó,à@™Ð(?Ó@ÐA`ÓaÑrq   r[  z&/auto_router/classifier/default_promptzRGet the system prompt an auto-router's LLM classifier sends for an edited tier setc              ƒ   ót   K  — t        t        | j                  | j                  | j                  «      ¬«      S ­w)a!  
    Get the classifier system prompt an edited tier set sends, so the dashboard can show it.

    Built by the same function the live classifier uses, so the preview cannot drift from what the
    router sends. Payload validity beyond a renderable definition stays the dry-run's job.
    ©Úsystem_prompt)rR   rK   r\  r_  r^  )rA  s    r`   Ú%preview_auto_router_classifier_promptrd  Ç  s6   è ø€ ô 5Ü7Ø×$Ñ$ g×&CÑ&CÀW×E`ÑE`ó
ôð ùs   ‚68zFGet the built-in system prompt used by an auto-router's LLM classifierr^  Úclassification_rubricc              ƒ   óØ   K  — | dk  r+t        dt        j                  t        j                  d¬«      ‚t        |«      }t        |€t        | |¬«      ¬«      S t        | ||¬«      ¬«      S ­w)aZ  
    Get the classifier system prompt a router would send, so the dashboard can show it.

    The prompt's closing line depends on whether prior conversation turns are quoted to the
    classifier, its tier bullets are named by the router's tier_labels, and its calibration examples
    come from the router's classification rubric, so the caller passes all three to get the text that router
    would actually send rather than a rubric it does not use.

    An edited tier set replaces the whole rubric; POST to this path for that prompt, which carries
    the operator's own instructions and so must not ride in a query string.

    Parameters:
    - context_window_size: int - The router's classifier_context_window_size. Defaults to the
      built-in default.
    - tier_labels: str | None - The router's tier_labels as a JSON object of canonical tier name to
      display name, e.g. `{"SIMPLE": "Cheap"}`. Omit or pass an empty object for the default names.
    - classification_rubric: ClassificationRubric | None - The router's
      classifier_llm_config.classification_rubric. Omit for the default.
    r   z(context_window_size must be non-negativer^  rÑ   )re  )rW  re  rb  )r,   r+   rX  r   rØ   rY  rR   rJ   )r^  rT  re  rW  s       r`   Ú)get_auto_router_classifier_default_promptrg  Ý  s€   è ø€ ð< ˜QÒÜØ>Ü ×2Ñ2Ü×,Ñ,Ø'ô	
ð 	
ô 5°[ÓA€MÜ4ð Ð$ô )Ð)<ÐTiÔjôð ô .Ø#°=ÐXmôô	ð ùs   ‚A(A*rZ   c                 óÄ   — t        «       }g }| D ]N  }|j                  di «      j                  dd«      }|€Œ(||vsŒ-|j                  |«       |j                  |«       ŒP |S )a"  
    Deduplicate models based on their model_info.id field.
    Returns a list of unique models keeping only the first occurrence of each model ID.

    Args:
        models: List of model dictionaries containing model_info

    Returns:
        List of deduplicated model dictionaries
    r}   r�  N)rÞ  rþ   rÈ  rß  )rZ   Úseen_idsÚunique_modelsrÈ   ry   s        r`   Ú"_deduplicate_litellm_router_modelsrk  	  sk   € ô “e€HØ€MØò #ˆØ—9‘9˜\¨2Ó.×2Ñ2°4¸Ó>ˆØÑ H°HÒ$<Ø× Ñ  Ô'Ø�L‰L˜Õ"ð	#ð
 Ðrq   c                 óÆ   — t        | t        «      r| S t        | t        «      sy	 t        j                  | «      }t        |t        «      r|S dS # t
        t        f$ r Y yw xY w)aE  A DB row's model_info column arrives as a dict or as its JSON string depending on
    the query path, and every consumer needs the mapping. Single owner of that parse:
    returns None when no usable mapping exists (None, an unparseable string, or JSON
    that is not an object), and callers choose what None means for them.N)r  r   rm   rE  rV  Ú	TypeErrorÚ
ValueError)r}   rú  s     r`   rÇ  rÇ  $	  sc   € ô
 �*œgÔ&ØÐÜ�j¤#Ô&ØðÜŸ
™
 :Ó.ˆô   ¬Ô0ˆ6Ð:°dÐ:øô ”zÐ"ò Ùðús   ¥A ÁA ÁA c                 óR   — ddl m} 	  |t        | «      ¬«      S # t        $ r Y yw xY w)Nr   )Ú$model_info_is_active_for_environmentrî   T)Úlitellm.routerrp  rÇ  rn  )r}   rp  s     r`   Ú_expects_liveness_on_this_podrr  4	  s.   € ÝCðÙ3Ô?TÐU_Ó?`ÔaÐaøÜò Ùðús   ˆ š	&¥&c                  óX   — ddl m}  | €
t        «       S t        | j                  «       «      S )zÛThe ids this pod's router is currently serving, read fresh from the module global
    because a reload can rebind it. The empirical ground truth every verdict below is
    computed from; an absent router serves nothing.r   r  )rp  rT  rÊ   Úget_model_idsr  s    r`   r}  r}  =	  s)   € õ 6àÐÜ‹{ÐÜ�Z×-Ñ-Ó/Ó0Ð0rq   rl  rm  Úwritten_must_servern  ro  c                 óì   ‡ ‡	— |€
t        «       n|Š	t        d„ |D «       «      }|rt        ˆ	fd„|D «       «      }nt        ˆ ˆ	fd„|D «       «      }‰ ‰	z
  |z
  }t        t        |€|n||z  «      «      }||fS )a˜  Judge a write-triggered reload by diffing the router's serving state instead of
    trusting any layer of the reload stack to report its own failure.

    The full cell matrix, per id:
    - written, must-serve (the write's purpose is this model's serving state): live now
      is fine; not live is reported unless the row is deliberately inactive for this
      pod's LITELLM_ENVIRONMENT; a row whose model_info cannot be read counts as
      expecting to serve, so its drop is still reported
    - written, metadata-only (must_not_degrade): live before and gone now is reported;
      a row that was already not serving stays silent, because its deadness predates
      this write and blaming it would block unrelated metadata fixes
    - not written but live before and gone now: collateral degradation of this pod
      caused by the reload this request triggered (a wholesale re-add failure, or a
      newly introduced conflict), reported only when the db still wants that id

    ``still_desired`` is the db + config id set the reload just reconciled against. An
    id absent from it was deleted on purpose, most often by another pod this one had not
    yet polled, so the reload dropping it is the reconcile working rather than damage.
    Without it (no reconcile ran) every drop is reported, which is the safe direction.

    ``live_after`` is the router's serving state captured by the reload itself, while it
    still held MODEL_RECONCILE_LOCK. Pass it whenever the caller has it: re-reading the
    router here instead means sampling it after the lock was released, where the NEXT
    reconcile's leading wipe (clear_cache un-serves every db model before reloading
    them) shows up as this reload having dropped them. Falling back to a fresh read is
    only correct when no reconcile ran and there is nothing to be concurrent with.

    Returns (written ids violating their obligation, collateral ids no longer served).
    c              3   ó&   K  — | ]	  \  }}|–— Œ y ­wrv   rp   )rÄ   ry   r¨  s      r`   rÇ   z)reload_serving_verdict.<locals>.<genexpr>m	  s   è ø€ Ò"N±°¸!¤8Ñ"Nùs   ‚c              3   óF   •K  — | ]  \  }}|‰vrt        |«      r|–— Œ y ­wrv   )rr  )rÄ   ry   r}   Únows      €r`   rÇ   z)reload_serving_verdict.<locals>.<genexpr>o	  s-   øè ø€ ò 
á$�˜*Ø˜sÑ"Ô'DÀZÔ'Pô ñ
ùs   ƒ!c              3   ó<   •K  — | ]  \  }}|‰v sŒ|‰vsŒ|–— Œ y ­wrv   rp   )rÄ   ry   r¨  rl  ry  s      €€r`   rÇ   z)reload_serving_verdict.<locals>.<genexpr>u	  s&   øè ø€ Òq¡[ X¨qÀhÐRXÒFXÐ]eÐmpÒ]pœÑqùs   ƒ�•)r}  rÊ   rä   r%  )
rl  rm  ru  rn  ro  Úwritten_idsræ   ÚdroppedÚ
collateralry  s
   `        @r`   Úreload_serving_verdictr~  H	  s‡   ù€ ðH /9Ð.@Ô(Ô*Àj€CÜ"Ñ"N¸~Ô"NÓN€KÙÜó 
à(6ô
ó 
‰ô Ôq°NÔqÓqˆØ˜c‘\ KÑ/€GÜœf°Ð0E¡WÈ7ÐUbÑKbÓcÓd€JØ�ZÐ Ð rq   c                 óæ   — t        | |d||¬«      \  }}|s|sy|rdt        |«      › d�nd}|rdt        |«      › d�nd	}t        d
|› d|› |› d�t        j                  t
        j                  d¬«      ‚)aF  The caller-visible error this pod's model-write endpoints owe their caller when
    the model they wrote is not being served after the reload they triggered. The DB
    write is durable either way and every other pod reloads on its own interval; this
    speaks only for the handling pod.

    Callers hold a ReconcileOutcome from the reload; pass BOTH of its fields. Supplying
    still_desired without live_after mixes a snapshot taken under the reconcile lock
    with one taken after it was released, which is what makes a concurrent model write
    look like collateral damage.T)rl  rm  ru  rn  ro  Nzthe model id(s) zY are not live in this pod's router after the reload and are not being served by this pod.z:the reload it triggered degraded this pod's serving state.z Previously served model id(s) z- are also no longer being served by this pod.Ú r\  z  was saved to the database, but zŠ Other pods reload on their own interval. Check server logs for 'Error upserting deployment' or 'Error creating deployment' for the cause.rÑ   )r~  rl   r,   r+   r…  r   r†  )	rl  rm  rf  rn  ro  ræ   r}  Úmissing_clauseÚcollateral_clauses	            r`   r‚  r‚  {	  s»   € ô  1ØØ%ØØ#ØôÑ€GˆZñ ™:Øñ ð œ4 ›=˜/ð *$ñ 	$ð Jð	 ñ ð *¬$¨zÓ*:Ð);Ð;hÑiàð ô
 à�V�HÐ<¸^Ð<LÐM^ÐL_ð `9ð 9ô ×2Ñ2Ü×2Ñ2Øô	ð 	rq   c               ƒ   ó   K  — ddl m} m}m}m}m}m} |�|€ |j                  d«       t        dd¬«      S | 4 ƒd{  –—†  	  |j                  d«       |j                  j                  «       }g }g }t        «       }	|D ]Þ  }
|
j                  di «      }|j                  dd	«      r§|j                  |j                  d
«      «       |
j                  d«      }|€Œ[t        |
j                  di «      j                  dd«      «      j                  d«      sŒ–|	j!                  |«       |j                  d
«      }|€Œ»|j#                  |¬«       ŒÎ|j                  |
«       Œà |	D ]r  }|j$                  j'                  |d«       |j(                  j'                  |d«       |j*                  j'                  |d«       |j,                  j'                  |d«       Œt |j/                  ||¬«      ƒ d{  –—† } |j                  dt1        |«      t1        |«      «       |cddd«      ƒd{  –—†  S 7 �Œî7 Œ@7 Œ
# t2        $ r<} |j4                  d|«       t        dd¬«      cY d}~cddd«      ƒd{  –—†7   S d}~ww xY w# 1 ƒd{  –—†7  sw Y   yxY w­w)añ  
    Clear router caches and reload models.

    Returns what the reload saw (see ReconcileOutcome) so callers can pass it to
    raise_if_reload_degraded_serving.

    Runs under MODEL_RECONCILE_LOCK for its whole extent, not just the reload at the
    end, so the auto-router reset and the reload that rebuilds those routers are atomic
    to any other reconcile. The inner call is _add_deployment_locked because
    add_deployment would re-acquire the same non-reentrant lock and deadlock.
    r   )rÓ  rT  r«   r(  rå  r   Nz9llm_router or prisma_client is None, skipping cache clearr)  z1Clearing only DB models, preserving config modelsr}   rº   Fr�  r{   r�   rÈ   r€  zauto_router/rÔ  r*  z3Reconciled %s DB models, preserved %s config modelsz:Failed to clear cache and reload models. Due to error - %s)rp  rÓ  rT  r«   r(  rå  r   rM  r-   Ú
model_listÚcopyrÞ  rþ   rÈ  rm   rÃ  rß  r×  Úauto_routersrB  Úcomplexity_routersÚadaptive_routersÚquality_routersÚ_add_deployment_lockedÚlenrƒ  r„  )rÓ  rT  r«   r(  rå  r   Úcurrent_modelsÚconfig_modelsÚdb_model_idsÚdb_router_namesrÈ   r}   r{   Úrouter_model_idÚoutcomer‹  s                   r`   r~  r~  «	  s¡  è ø€ ÷÷ ð Ð˜]Ð2Ø"Ð×"Ñ"Ð#^Ô_Ü¨d¸tÔDÐDà#÷ RIñ RIðQ	Ià&Ð ×&Ñ&Ð'ZÔ[ð %/×$9Ñ$9×$>Ñ$>Ó$@ˆNØ#%ˆMØ"$ˆLä%(£UˆOà'ò 0�Ø"ŸY™Y |°RÓ8�
Ø—>‘> *¨eÔ4Ø ×'Ñ'¨
¯©°tÓ(<Ô=ð "'§¡¨<Ó!8�JØ!Ñ-´#°e·i±iÐ@PÐRTÓ6U×6YÑ6YÐZaÐceÓ6fÓ2g×2rÑ2rØ&õ3ð (×+Ñ+¨JÔ7Ø*4¯.©.¸Ó*>˜Ø*Ñ6Ø&×8Ñ8¸OÐ8ÕLð "×(Ñ(¨Õ/ð!0ðf .ò A�
Ø×'Ñ'×+Ñ+¨J¸Ô=Ø×-Ñ-×1Ñ1°*¸dÔCØ×+Ñ+×/Ñ/°
¸DÔAØ×*Ñ*×.Ñ.¨z¸4Õ@ð	Að $0×#FÑ#FØ+Ð?Pð $Gó $÷ ˆGð 'Ð ×&Ñ&ØEÄsÈ<ÓGXÔZ]Ð^kÓZlôð ÷_RI÷ RIò RIùðPøðQRIùô` ò 	IØ*Ð ×*Ñ*Ð+gÐijÔkÜ#°$À4ÔHÔH÷eRI÷ RIò RIûð`	Iú÷aRI÷ RIñ RIüs§   ‚:J¼H*½JÁ I9ÁBH1Ã:H1Ä$H1Ä9B3H1Ç,H-Ç-+H1ÈJÈ$H/È%JÈ-H1È/JÈ1	I6È:I1ÉI6ÉI9ÉJÉ*I-É+JÉ1I6É6I9É9JÉ?JÊ JÊJ)NTrv   )NN)Órk   r  rC  rE  Úcollections.abcr   r   r   r   Útypesr   Útypingr   r	   r
   r   r   r   Úfastapir   r   r   r   r   r   Úpydanticr   r   r   r   r   Úlitellm._loggingr   Úlitellm._uuidr   Úlitellm.constantsr   Ú&litellm.litellm_core_utils.ptu_pricingr   r   r   r   r    r!   r"   Úlitellm.proxy._typesr#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   Ú$litellm.proxy.auth.user_api_key_authr1   Ú-litellm.proxy.common_utils.config_sync_pubsubr2   r3   Ú0litellm.proxy.common_utils.encrypt_decrypt_utilsr4   Ú-litellm.proxy.common_utils.user_api_key_cacher5   Ú/litellm.proxy.management_endpoints.common_utilsr6   Ú1litellm.proxy.management_endpoints.team_endpointsr7   r8   r9   r:   r]   Ú+litellm.proxy.management_helpers.audit_logsr;   Ú-litellm.proxy.spend_tracking.ptu_feature_flagr<   r=   Úlitellm.proxy.utilsr>   r?   Ú%litellm.repositories.model_repositoryr@   Ú%litellm.repositories.prisma_protocolsrA   Ú'litellm.repositories.table_repositoriesrB   Ú$litellm.repositories.team_repositoryrC   rq  rD   Ú)litellm.router_strategy.complexity_routerrE   rF   rG   rH   rI   rJ   rK   rL   Ú-litellm.router_utils.auto_router_model_namingrM   rN   rO   rP   rQ   ÚClitellm.types.proxy.management_endpoints.model_management_endpointsrR   rS   rº  rT   rU   rV   rW   rX   Úlitellm.utilsrY   ÚprismarZ   Úprisma_modelsr[   rn   rb   rs   rˆ   r˜   r›   r    r¥   r¨   r¯   r±   rµ   r·   rm   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.  r8  r>  rN  r0  rŒ  r�  Úpostr•  r˜  r¥  rµ  r{  r¹  r»  rÍ  rl   rÜ  rÞ  rã  r÷  r¼  ru  r  rë  r5  r@  rO  rS  rY  r[  rd  rþ   r–   rg  rk  rÇ  rr  r}  r~  r‚  r~  rp   rq   r`   ú<module>r°     s­  ðòó Û Û ß 8Ñ 8Ý  Ý "ß K× Kç N× Nß SÕ Så 1Ý Ý 6÷÷ ñ ÷÷ ÷ ÷ õ  C÷õ RÝ IÝ O÷ñ õ
õ P÷÷ ;Ý AÝ >Ý HÝ ?Ý !÷	÷ 	ó 	÷õ ÷÷õ õ +áÝ.á“€ˆÓ ò6ô. Yô .ô
I�Xô 
IôQ�xô Qô.�Xô .ôI˜ô Iô6ˆxô 6ô]�xô ]ô+Ð! 8ô +ð0 lð 0Ð7Gó 0ð/ Lð /Ð5Eó /ð. ,ð .°:ó .ð5 lð 5Ð7gó 5ð# ð #°\ð #ÀjÐSWÑFWó #ð#lØ)¨DÑ0ð#là)¨DÑ0ð#lð 	ˆ4�Zó#lðLØ)¨DÑ0ðà)¨DÑ0ðð 
óð /MÐ $ eÓ LØ%3Ð ˜UÓ 3ðÐBVð Ðbfð Ðkoó ñ8 $ [Ð2IÐ$JÓKÐ �%Ó Kð¨y¸4Ñ/?ð ÀIÈcÁNó ðc¨
ð cÐ@Pð cÐU\Ð]`ÐbhÐ]hÑUió cðÀÈÈfÈÑAUð ÐZ^ó ð4;¨°°f°Ñ)=ð ;À$ó ;ð. %>Ð ˜EÓ =Ø%?Ð ˜UÓ ?Ù>NðØ
�-Š-Ð2°CÓ
8ðà
�-Š-Ð3°RÓ
8ðó?Ð �U˜7 3¨°°b±	Ñ(9Ð#9Ñ:Ñ;ó ñ @PÐPRÓ?SÐ �e˜G C¨°°r±Ñ):Ð$:Ñ;Ñ<Ó SØ1EÐ �5˜  f Ñ-Ñ.Ó Eð !6Ð ˜Ó 5ð
 #:Ð ˜%Ó 9Ø3Ð �uÓ 3ðZ˜Fð Z tó Zð#˜Vð #¨ó #ðZ˜&ð Z Tó Zð°g¸cÀ6¸kÑ6Jð ÐV]Ð^aÐciÐ^iÑVjð Ðosó ð8%à˜˜V˜Ñ$ð%ð ˜C ˜KÑ(ð%ð �c˜6�kÑ"ð	%ð
 ˆS�%˜% ™)Ñ# g¨c°5¨jÑ&9Ñ9Ð9Ñ:ó%ðPà˜s F˜{Ñ+ðð ˜˜V˜Ñ$ðð ˜C ˜KÑ(ð	ð
 ðð ˆ7�3˜  b¡	Ñ)¨G°C¸°JÑ,?Ñ?Ð?Ñ@À)ÈCÁ.ÐPÑQóðB¨ð ¸
ó ð,H(˜jð H(Ð9Ið H(ÐNkó H(ðV ‡‚ØØ
Ð	ÙÐ+Ó,Ð-ð ó ñ )0Ð0AÓ(BñY
ØðY
à ðY
ð &òY
óð
Y
ðxi
Ø
ði
à%ði
ð ði
ð Ð*Ñ+ð	i
ð
 ˜d™
ði
ð ˜tÑ#ói
ðX ‡‚ØØ
Ð	ÙÐ+Ó,Ð-ð ó ñ )0Ð0AÓ(BÙ%+Øð tô&ñ	Ø
ðàðð &ðð ˜d™
ð	ð ˜tÑ#òóð
ð0 ‡‚ØØ
Ð	ÙÐ+Ó,Ð-ð ó ñ )0Ð0AÓ(BÙ%+Øð tô&ñ	Ø
ðàðð &ðð ˜d™
ð	ð ˜tÑ#òóð
ðD &*Ø&*ñØðà%ðð  ðð ˜d™
ð	ð
  $ðð Nóð>/Øð/à%ð/ð  ð/ð Nó	/ðdRHØðRHà ðRHð &ðRHð  ð	RHð
 #óRHðj+Ø ð+àð+ð 	ó+ð\Øðàðð !ðð &ð	ð
 
óð* QUñ Øð Ø!-ð Ø6FÈÑ6Mð àˆnÑó ðF'Ø�3‰ið'àð'ð ˜‘ð'ð 
ˆ#�Yó	'ðTØðàðð 	ˆ�Xóð6 !%ñIØðIàðIð (ðIð $ð	Ið
 ˜‘ðIð 
óIðXP!ØðP!àðP!ð ðP!ð !ð	P!ð
 &ðP!ð   $Ñ&ðP!ð 
óP!÷fmñ mðb ‡‚ØØMØ
Ð	ÙÐ+Ó,Ð-ð	 ó ñ )0Ð0AÓ(Bñx
Øðx
à%òx
óðx
ðv"!Øð"!àð"!ð 
ˆ%��S�‰/Ñó"!ðL ‡‚ØØOØ
Ð	ÙÐ+Ó,Ð-ð	 ó ñ )0Ð0AÓ(Bñl
Øðl
à%òl
óðl
ð` ‡‚ØØ,Ø
Ð	ÙÐ+Ó,Ð-ð	 ó ñ )0Ð0AÓ(BñS
Ø"ðS
à%òS
óðS
ðl ‡‚ØØ6Ø
Ð	ÙÐ+Ó,Ð-ð	 ó ñ )0Ð0AÓ(BñO
Ø+ðO
à%òO
óðO
ðd ‡‚Ø$Ø%Ø
Ð	ÙÐ+Ó,Ð-ð	 ó ñ )0Ð0AÓ(Bñ;
Ø%ð;
à%ò;
óð;
ð|¨3°©:ð ¸%ÀÀnÐVYÐFYÑ@ZÐ\_Ð@_Ñ:`ÐcgÑ:gó ô.b¨yô bð ‡‚Ø,ØdØ
Ð	ÙÐ+Ó,Ð-ð	 ó ðØ5ðà.òóðð  ‡‚Ø,ØXØ
Ð	ÙÐ+Ó,Ð-ð	 ó ð  FØ"Ø9=ñ)Øð)à�t‘ð)ð 0°$Ñ6ð)ð /ò	)óð)ðX¨t°D©zð ¸dÀ4¹jó ð*; fð ;°¸¸f¸Ñ1EÈÑ1Ló ;ð ¨fð ¸ó ð1 ¨3¡ó 1ð ,0Ø(,ñ0!Ø�c‰Nð0!à˜U 3¨ ;Ñ/Ñ0ð0!ð ð0!ð ˜S‘> DÑ(ð	0!ð
 ˜#‘ Ñ%ð0!ð ˆ5��c�‰?˜E # s (™OÐ+Ñ,ó0!ðn ,0Ø(,ñ-Ø�c‰Nð-à˜U 3¨ ;Ñ/Ñ0ð-ð ð-ð ˜S‘> DÑ(ð	-ð
 ˜#‘ Ñ%ð-ð 
ó-ð`kIÐ+ô kIrq   