Ë
    tPêjr}  ã                   ó  — U d dl mZmZmZmZ d dlmZ d dlmZ d dlm	Z	m
Z
mZ d dlmZmZ d dlmZ d dl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m Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z* d dl+m,Z, d dl-m.Z. d dl/m0Z0m1Z1 d dl2m3Z3 d dl4m5Z5 d dl6m7Z7  G d„ de«      Z8 G d„ de«      Z9 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>d e0d!e>dz  d"e<fd#„Z?d e0d!e>dz  d"e;fd$„Z@d e0d!e>dz  d"e=fd%„ZAd e0d!e>dz  d&eBd"ee9   fd'„ZCd(e<d)eBd*eeBeDf   d"e9dz  fd+„ZEd e0d!e>dz  d,eFeBeDf   d"e9dz  fd-„ZGdZd)eBd&eBdz  d"eFeBeDf   fd.„ZHd/eBdz  d0e)d e0d1eBd"eBdz  f
d2„ZId3ZJe
eKeBd4f      eLd5<   	 	 d[d e0d6eBd0e)d1eBd7eBdz  d!e>dz  d"eBdz  fd8„ZM	 dZd e0d0e)d1eBd9eNdz  d:eOeB   dz  d;eBdz  d6eBdz  d!e>dz  d"eBdz  fd<„ZP	 dZd e0d)eBd=eBd!e>dz  d"df
d>„ZQ	 	 	 	 d\d?e"d9eNdz  d e0d=eBd0e)d1eBd6eBdz  d:eOeB   dz  d;eBdz  d!e>dz  d"eKe edz  f   fd@„ZRdA„ ZSdB„ ZTdC„ ZUdD„ ZVdEeFd0e)dFeBfdG„ZWdHeDd"eeBeDf   dz  fdI„ZXdHeDd"eeD   dz  fdJ„ZYdKeDd"eFeBeDf   fdL„ZZdMeDd"eDfdN„Z[dOeeBeDf   d"dfdP„Z\	 	 d[dQedReFdSe#dz  dTedUedVe	dWe]dz  d"dfdX„Z^dY„ Z_y)]é    )ÚCallableÚMappingÚMutableMappingÚSequence)Údatetime©Úwraps)ÚAnyÚFinalÚProtocol)ÚHTTPExceptionÚRequest)Ú	BaseModelN)Úverbose_logger)Úuuid)Úis_otel_v2_enabled)ÚBudgetNewRequestÚDeleteCustomerRequestÚDeleteTeamRequestÚDeleteUserRequestÚ
KeyRequestÚLiteLLM_BudgetTableÚLiteLLM_TeamMembershipÚLiteLLM_UserTableÚ ManagementEndpointLoggingPayloadÚMemberÚSpanÚSSOUserDefinedValuesÚUpdateCustomerRequestÚUpdateKeyRequestÚUpdateTeamRequestÚUpdateUserRequestÚUserAPIKeyAuthÚVirtualKeyEvent)Ú_read_request_body)Úget_budget_reset_time)ÚPrismaClientÚjsonify_object)ÚBudgetRepository)ÚTeamMembershipRepository)ÚUserRepositoryc                   ó&   — e Zd ZdZdeeef   fd„Zy)Ú_PrismaRecordz9Row surface the management helpers read back from Prisma.Úreturnc                  ó   — y ©N© ©Úselfs    úk/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/management_helpers/utils.pyÚ
model_dumpz_PrismaRecord.model_dump.   ó   � ó    N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   ÚstrÚobjectr5   r1   r7   r4   r-   r-   +   s   „ ÙCà5˜G C¨ KÑ0Ô5r7   r-   c                   ó2   — e Zd ZU dZeed<   deeef   fd„Zy)Ú_PrismaUserRecordz>User row surface the management helpers read back from Prisma.Úuser_idr.   c                  ó   — y r0   r1   r2   s    r4   r5   z_PrismaUserRecord.model_dump6   r6   r7   N©	r8   r9   r:   r;   r<   Ú__annotations__r   r=   r5   r1   r7   r4   r?   r?   1   s   … ÙHàƒLà5˜G C¨ KÑ0Ô5r7   r?   c                   ó2   — e Zd ZU dZeed<   deeef   fd„Zy)Ú_PrismaBudgetRecordz@Budget row surface the management helpers read back from Prisma.Ú	budget_idr.   c                  ó   — y r0   r1   r2   s    r4   r5   z_PrismaBudgetRecord.model_dump>   r6   r7   NrB   r1   r7   r4   rE   rE   9   s   … ÙJàƒNà5˜G C¨ KÑ0Ô5r7   rE   c                   óJ   — e Zd ZdZdeeef   defd„Zdeeef   dedz  fd„Z	y)Ú_PrismaBudgetTablez2Budget table actions the management helpers issue.Údatar.   c             ƒ   ó   K  — y ­wr0   r1   )r3   rJ   s     r4   Úcreatez_PrismaBudgetTable.createD   ó   è ø� ùó   ‚ÚwhereNc             ƒ   ó   K  — y ­wr0   r1   ©r3   rO   s     r4   Úfind_uniquez_PrismaBudgetTable.find_uniqueF   rM   rN   )
r8   r9   r:   r;   r   r<   r=   rE   rL   rR   r1   r7   r4   rI   rI   A   s9   „ Ù<àU G¨C°¨KÑ$8ÐUÐ=PÓUàb¨'°#°v°+Ñ*>ÐbÐCVÐY]ÑC]Ôbr7   rI   c                   ó�   — e Zd ZdZdeeef   deeef   defd„Zdeeef   deeeeef   f   de	dz  fd„Z
deeef   dee	   fd„Zy)	Ú_PrismaUserTablez0User table actions the management helpers issue.rO   rJ   r.   c             ƒ   ó   K  — y ­wr0   r1   ©r3   rO   rJ   s      r4   Úupdate_manyz_PrismaUserTable.update_manyL   rM   rN   Nc             ƒ   ó   K  — y ­wr0   r1   rV   s      r4   Úupsertz_PrismaUserTable.upsertN   s	   è ø€ à#&ùrN   c             ƒ   ó   K  — y ­wr0   r1   rQ   s     r4   Ú	find_manyz_PrismaUserTable.find_manyR   rM   rN   )r8   r9   r:   r;   r   r<   r=   ÚintrW   r?   rY   r   r[   r1   r7   r4   rT   rT   I   sˆ   „ Ù:àg¨'°#°v°+Ñ*>ÐgÀgÈcÐSYÈkÑFZÐgÐ_bÓgð'Ø  V Ñ,ð'Ø4;¸CÀÈÈfÈÑAUÐ<UÑ4Vð'à	˜TÑ	!ó'ð b¨°°V°Ñ(<ÐaÀÐJ[ÑA\Ôar7   rT   c                   ó8   — e Zd ZdZdeeef   deeef   defd„Z	y)Ú_PrismaTeamMembershipTablez;Team membership table actions the management helpers issue.rJ   Úincluder.   c             ƒ   ó   K  — y ­wr0   r1   )r3   rJ   r_   s      r4   rL   z!_PrismaTeamMembershipTable.createX   rM   rN   N)
r8   r9   r:   r;   r   r<   r=   Úboolr-   rL   r1   r7   r4   r^   r^   U   s+   „ ÙEàl G¨C°¨KÑ$8ÐlÀ7È3ÐPTÈ9ÑCUÐlÐZgÔlr7   r^   c                   óR   — e Zd ZdZedefd„«       Zedefd„«       Zede	fd„«       Z
y)ÚMemberWriteTxaZ  Transaction surface `add_new_member` writes through when the caller owns one.

    A caller already holding a transaction, and with it a pooled connection plus that
    transaction's locks, passes it here so these writes reuse that connection rather than
    checking out another one that lock waiters may already have drained from the pool.
    r.   c                  ó   — y r0   r1   r2   s    r4   Úlitellm_usertablezMemberWriteTx.litellm_usertablec   s   € Ø58r7   c                  ó   — y r0   r1   r2   s    r4   Úlitellm_budgettablez!MemberWriteTx.litellm_budgettablef   s   € Ø9<r7   c                  ó   — y r0   r1   r2   s    r4   Úlitellm_teammembershipz$MemberWriteTx.litellm_teammembershipi   s   € ØDGr7   N)r8   r9   r:   r;   ÚpropertyrT   re   rI   rg   r^   ri   r1   r7   r4   rc   rc   [   sE   „ ñð Ø8Ð#3Ò8ó Ø8àØ<Ð%7Ò<ó Ø<àØGÐ(BÒGó ÙGr7   rc   Úprisma_clientÚtxr.   c                 óH   — |�|j                   S t        | «      j                  S r0   )re   r+   Útable©rk   rl   s     r4   Ú_user_tablerp   m   s"   € Ø#% >ˆ2×ÑÐZ´~ÀmÓ7T×7ZÑ7ZÐZr7   c                 óH   — |�|j                   S t        | «      j                  S r0   )rg   r)   rn   ro   s     r4   Ú_budget_tablerr   q   s#   € Ø%' ^ˆ2×!Ñ!Ð^Ô9IÈ-Ó9X×9^Ñ9^Ð^r7   c                 óH   — |�|j                   S t        | «      j                  S r0   )ri   r*   rn   ro   s     r4   Ú_team_membership_tablert   u   s$   € Ø(*¨ˆ2×$Ñ$ÐiÔ<TÐUbÓ<c×<iÑ<iÐir7   Ú
user_emailc              ƒ   óª   K  — |�&|j                   j                  d|i¬«      ƒ d {  –—† S | j                  d|idd¬«      ƒ d {  –—† }|�|S dS 7 Œ(7 Œ­w)Nru   ©rO   ÚuserÚfind_all)Úkey_valÚ
table_nameÚ
query_typer1   )re   r[   Úget_data)rk   rl   ru   Úrowss       r4   Ú_find_users_by_emailr   y   sv   è ø€ ð 
€~Ø×)Ñ)×3Ñ3¸<ÈÐ:TÐ3ÓU×UÐUØ<I×<RÑ<RØ˜zÐ*ØØð =Só =÷ 7€Dð
 Ð#ˆ4Ð+¨Ð+ð Vøð7ús!   ‚$A¦A§AÁAÁAÁAÚ
user_tabler@   Úcreate_datac              ƒ   óR   K  — | j                  d|i|d|idœ¬«      ƒ d{  –—† S 7 Œ­w)a‡  Insert the user row if it is absent, leaving an existing row as it is.

    Upserting keeps concurrent provisioning of the same new user from racing on create.
    The update branch re-states user_id rather than being empty because Prisma only
    compiles an upsert down to INSERT ... ON CONFLICT when the update is non-empty, and
    otherwise falls back to a racy SELECT-then-INSERT.
    r@   )rL   Úupdate©rO   rJ   N)rY   )r€   r@   r�   s      r4   Ú_upsert_user_rowr…   †   s?   è ø€ ð ×"Ñ"Ø˜'Ð"Ø#°	¸7Ð/CÑDð #ó ÷ ð ð ús   ‚' %¡'Ú	user_datac              ƒ   ó´   K  — |�4t        |j                  t        |d   «      t        |«      «      ƒ d {  –—† S | j	                  |d¬«      ƒ d {  –—† S 7 Œ7 Œ­w)Nr@   rx   )rJ   r{   )r…   re   r<   r(   Úinsert_data)rk   rl   r†   s      r4   Ú_create_user_rowr‰   –   sY   è ø€ ð 
€~Ü% b×&:Ñ&:¼CÀ	È)Ñ@TÓ<UÔWeÐfoÓWpÓq×qÐqØ×*Ñ*°	ÀfÐ*ÓM×MÐMð røØMús!   ‚2A´AµAÁAÁAÁAc                 óJ  — t         j                  xs i }|j                  d«      xs g |j                  dt         j                  «      |j                  dt         j                  «      |xs |j                  dd «      | ddœ}i }|j                  «       D ]  \  }}|€Œ	|||<   Œ |S )NÚmodelsÚ
max_budgetÚbudget_durationru   Úinternal_user)r‹   rŒ   r�   ru   r@   Ú	user_role)ÚlitellmÚdefault_internal_user_paramsÚgetÚmax_internal_user_budgetÚinternal_user_budget_durationÚitems)r@   ru   Ú	user_infoÚreturned_dictÚnon_null_dictÚkÚvs          r4   Úget_new_internal_user_defaultsr›   ž   s¨   € Ü×;Ñ;ÒA¸r€Ið —-‘- Ó)Ò/¨RØ—m‘m L´'×2RÑ2RÓSØ$Ÿ=™=Ð):¼G×<aÑ<aÓbØ ÒE I§M¡M°,ÀÓ$EØØ$ñ2€Mð €MØ×#Ñ#Ó%ò !‰ˆˆ1Ø‰=Ø ˆM˜!Òð!ð Ðr7   Úexisting_budget_idÚuser_api_key_dictÚlitellm_proxy_admin_namec              ƒ   ó†  K  — ddl m} t        j                  j	                  «       }t        | d«      r| j                  d¬«      n| }|j                  «       D ��	ci c]  \  }}	||v sŒ||	“Œ }
}}	t        | dd«      }|€�|�|S |
rˆt        di |
¤Ž}|j                  |j                  d¬«      «      }t        |«      j                  j                  i |¥|j                  xs ||j                  xs |dœ¥¬	«      ƒ d{  –—† }|j                  S y|
r |t        dd|i|
¤Ž|¬
«      ƒ d{  –—†  |�||k7  r|S |S c c}	}w 7 ŒA7 Œ­w)a/  
    Common helper to handle budget creation/updates for entities (organizations, tags, etc).

    This function:
    1. Creates a new budget if budget_id is None but budget fields are provided
    2. Updates an existing budget if budget fields are provided and budget_id exists
    3. Returns the budget_id to use (existing or newly created)

    Args:
        data: The request object (e.g., TagNewRequest, NewOrganizationRequest, etc.) containing budget fields
        existing_budget_id: The existing budget_id if updating an entity, None if creating new
        user_api_key_dict: User authentication info
        prisma_client: Database client
        litellm_proxy_admin_name: Admin name for audit trail

    Returns:
        Optional[str]: The budget_id to use, or None if no budget was created/updated
    r   )Úupdate_budgetr5   T)Úexclude_nonerF   N©Ú
created_byÚ
updated_by©rJ   )Ú
budget_objr�   r1   )Ú>litellm.proxy.management_endpoints.budget_management_endpointsr    r   Úmodel_fieldsÚkeysÚhasattrr5   r•   Úgetattrr(   r)   rn   rL   r@   rF   r   )rJ   rœ   r�   rk   rž   r    Úbudget_paramsÚ
_json_datar™   rš   Ú_budget_dataÚdata_budget_idÚ
budget_rowÚnew_budget_dataÚ_budgets                  r4   Úhandle_budget_for_entityr³   ±   s‚  è ø€ õ2ô
 /×;Ñ;×@Ñ@ÓB€Mô ?FÀdÈLÔ>Y˜Ÿ™°T˜Ô:Ð_c€JØ,6×,<Ñ,<Ó,>×U¡D A qÀ!À}ÒBT˜1˜a™4ÐU€LÑUô )0°°kÀ4Ó(H€Nð Ð!ØÐ%à!Ð!Ùä 3Ñ C°lÑ CˆJØ%2×%AÑ%AÀ*×BWÑBWÐeiÐBWÓBjÓ%kˆOä8HÈÓ8W×8]Ñ8]×8dÑ8dðØ%ðà"3×";Ñ";Ò"WÐ?WØ"3×";Ñ";Ò"WÐ?Wòð 9eó 9÷ 3ˆGð ×$Ñ$Ð$ð ñ
 ÙÜ+ÑYÐ6HÐYÈLÑYØ"3ô÷ ð ð Ð%¨.Ð<NÒ*NØ!Ð!ð "Ð!ùóU Vð3øð"ús7   ‚AEÁD7Á'D7Á,BEÃ;D=Ã<+EÄ'D?Ä(EÄ?E)rŒ   Úsoft_budgetÚmax_parallel_requestsÚ	tpm_limitÚ	rpm_limitÚmodel_max_budgetr�   Úallowed_models.Ú_CLONABLE_BUDGET_FIELDSÚdefault_team_budget_idÚbudget_duration_overridec              ƒ   óî  K  — t        | |«      }|j                  d|i¬«      ƒ d{  –—† }|€y|j                  «       }|j                  xs ||j                  xs |dœ}	t        D ]:  }
|j                  |
«      }|€Œt        |t        «      rt        |«      dk(  rŒ6||	|
<   Œ< |�||	d<   |	j                  d«      rt        |	d   «      |	d<   |j                  |	¬«      ƒ d{  –—† }|j                  S 7 ŒÊ7 Œ­w)	ay  
    Create a new budget row that copies the values from the team's default
    member budget. Returns the new budget_id, or None if the default budget
    no longer exists in the DB.

    Used when adding a new team member without an explicit per-member budget,
    so the member starts with the team default's values but gets their own
    private budget row (which can be edited independently).

    ``budget_duration_override`` replaces the default's reset window for this
    member while keeping the default's other limits, so an admin can set a
    member's reset cadence without discarding the team default's max_budget.
    rF   rw   Nr¢   r   r�   Úbudget_reset_atr¥   )rr   rR   r5   r@   rº   r’   Ú
isinstanceÚlistÚlenr&   rL   rF   )rk   r»   r�   rž   r¼   rl   Úbudget_tableÚdefault_budgetÚdefault_budget_dictÚcloned_dataÚfieldÚvalueÚ
new_budgets                r4   Ú%_clone_team_default_budget_for_memberrÉ     s   è ø€ ô* /<¸MÈ2Ó.N€LØ".×":Ñ":À+ÐOeÐAfÐ":Ó"g×g€NØÐØà!/×!:Ñ!:Ó!<Ðà'×/Ñ/ÒKÐ3KØ'×/Ñ/ÒKÐ3Kñ €Kô )ò #ˆØ#×'Ñ'¨Ó.ˆØˆ=Øô �eœTÔ"¤s¨5£z°Q¢ØØ"ˆ�EÒð#ð  Ð+Ø)AˆÐ%Ñ&ð ‡�Ð(Ô)Ü)>¸{ÐK\Ñ?]Ó)^ˆÐ%Ñ&à3?×3FÑ3FÈKÐ3FÓ3X×-X€JØ×ÑÐð9 høð6 .Yús"   ‚$C5¦C1§B9C5Ã C3Ã!C5Ã3C5Úmax_budget_in_teamr¹   r�   c              ƒ   óX  K  — |duxs |du}|s|�t        | |||||¬«      ƒ d{  –—† S |s|€y|j                  xs ||j                  xs |dœ}	|�||	d<   |�||	d<   |�||	d<   t        |¬«      |	d<   t        | |«      }
|
j	                  |	¬	«      ƒ d{  –—† }|j
                  S 7 Œ€7 Œ­w)
a�  
    Resolve the budget a new team member should be linked to.

    Explicit per-member limits create a fresh budget. Otherwise the team's
    default member budget is cloned (with ``budget_duration`` overriding its
    reset window while keeping its other limits). A lone ``budget_duration``
    with no team default creates a window-only budget. With nothing set the
    member gets no budget.
    N)rk   r»   r�   rž   r¼   rl   r¢   rŒ   r¹   r�   )r�   r¾   r¥   )rÉ   r@   r&   rr   rL   rF   )rk   r�   rž   rÊ   r¹   r�   r»   rl   Úhas_explicit_limitÚbudget_datarÂ   Úresponses               r4   Ú_resolve_member_budget_idrÏ   E  sý   è ø€ ð& !3¸$Ð >Ò \À.ÐX\ÐB\ÐáÐ"8Ð"DÜ:Ø'Ø#9Ø/Ø%=Ø%4Øô
÷ 
ð 	
ñ  /Ð"9Øð (×/Ñ/ÒKÐ3KØ'×/Ñ/ÒKÐ3Kñ-€Kð Ð%Ø$6ˆ�LÑ!ØÐ!Ø(6ˆÐ$Ñ%ØÐ"Ø)8ˆÐ%Ñ&Ü)>ÈÔ)_ˆÐ%Ñ&Ü.;¸MÈ2Ó.N€LØ(×/Ñ/°[Ð/ÓA×A€HØ×ÑÐð3
øð0 Bús"   ‚#B*¥B&¦A/B*ÂB(ÂB*Â(B*Úteam_idc              ƒ   óv   K  — t        | |«      }|j                  |dd|iidœdd|gii¬«      ƒ d{  –—†  y7 Œ­w)aØ  Append team_id to a user's teams array, only if it is not already present.

    The row-level filter makes the append a no-op once the team is present, so
    repeated or concurrent adds of the same team cannot accumulate duplicate
    team ids in user.teams (a duplicate also breaks auth logic that keys off the
    number of teams a user belongs to). Teams added concurrently for a different
    team id are unaffected, since each update filters on its own team id.
    ÚteamsÚhas)r@   ÚNOTÚpushr„   N)rp   rW   )rk   r@   rÐ   rl   r€   s        r4   Ú_append_team_id_if_absentrÖ   w  sR   è ø€ ô +6°mÀRÓ*H€JØ
×
 Ñ
 Ø!¨7°U¸GÐ4DÐ*EÑFØ˜  	Ð*Ð+ð !ó ÷ ò ús   ‚/9±7²9Ú
new_memberc
           
   ƒ   óÔ  K  — d}
d}| j                   �Œt        | j                   ¬«      }t        t        ||	«      | j                   d|gi|¥«      ƒ d{  –—† }t	        || j                   ||	«      ƒ d{  –—†  |��Ft        j                  |j                  «       «      }
�n!| j                  ��t        t        t        j                  «       «      | j                  ¬«      }t        ||	| j                  «      ƒ d{  –—† }t        |«      dk(  rA|g|d<   t        ||	|«      ƒ d{  –—† }|�˜t        j                  |j                  «       «      }
ntt        |«      dk(  rI|d   }t	        ||j                   ||	«      ƒ d{  –—†  t        j                  |j                  «       «      }
nt        |«      dkD  rt        ddd	i¬
«      ‚t!        ||||||||	¬«      ƒ d{  –—† }|rh|
�f|
j                   �Zt#        ||	«      }|j%                  ||
j                   |dœddi¬«      ƒ d{  –—† }t'        j                  |j                  «       «      }|
€t)        d«      ‚|
|fS 7 �Œ7 �Œç7 �Œd7 �Œ>7 Œç7 Œ�7 ŒH­w)as  
    Add a new member to a team

    - add team id to user table
    - add team member w/ budget to team member table

    Returns created/existing user + team membership w/ budget id

    Callers already inside a transaction pass it as ``tx`` so every write here runs on that
    connection instead of borrowing more from the pool while the caller's locks are held.
    N)r@   rÒ   )r@   ru   r   é   i�  ÚerrorzIMultiple users with this email found in db. Please use 'user_id' instead.)Ústatus_codeÚdetail)rk   r�   rž   rÊ   r¹   r�   r»   rl   )rÐ   r@   rF   Úlitellm_budget_tableT)rJ   r_   z8Unable to update user table with membership information!)r@   r›   r…   rp   rÖ   r   Úmodel_validater5   ru   r<   r   Úuuid4r   rÁ   r‰   r   rÏ   rt   rL   r   Ú	Exception)r×   rÊ   rk   rÐ   r�   rž   r»   r¹   r�   rl   Úreturned_userÚreturned_team_membershipÚnew_user_defaultsÚ_returned_userÚexisting_user_rowr–   Ú
_budget_idÚmembership_tableÚ_returned_team_memberships                      r4   Úadd_new_memberré   ‰  s   è ø€ ð. /3€MØ>BÐà×ÑÐ%Ü:À:×CUÑCUÔVÐô :JÜ˜ rÓ*Ø×ÑØ�w�iÐ5Ð#4Ð5ó:
÷ 4
ˆô
 (¨°z×7IÑ7IÈ7ÐTVÓW×WÐWØÑ%Ü-×<Ñ<¸^×=VÑ=VÓ=XÓYŠMØ	×	Ñ	Ñ	*Ü:Ä3ÄtÇzÁzÃ|ÓCTÐak×avÑavÔwÐô G[Ø˜2˜z×4Ñ4óG
÷ A
Ðô Ð Ó! QÒ&Ø*1¨Ð˜gÑ&Ü#3°MÀ2ÐGXÓ#Y×YˆNàÐ)Ü 1× @Ñ @À×AZÑAZÓA\Ó ]‘ÜÐ"Ó# qÒ(Ø0°Ñ3ˆIÜ+¨M¸9×;LÑ;LÈgÐWYÓZ×ZÐZÜ-×<Ñ<¸Y×=QÑ=QÓ=SÓT‰MÜÐ"Ó# aÒ'ÜØØÐ!lÐmôð ô
 8Ø#Ø+Ø!9Ø-Ø%Ø'Ø5Øô	÷ 	€Jñ �mÐ/°M×4IÑ4IÐ4UÜ>TÐUbÐdfÓ>gÐØ1A×1HÑ1Hà"Ø(×0Ñ0Ø'ñð
 ,¨TÐ2ð 2Ió 2
÷ ,
Ð!ô $:×#HÑ#HÐIb×ImÑImÓIoÓ#pÐ àÐÜÐRÓSÐSàÐ2Ð2Ð2ðu4
ùð
 	XùðA
ùð
 Zùð [øð	øð,
ús‚   ‚AI(ÁIÁI(Á2IÁ3BI(Ã8IÃ9(I(Ä!IÄ"AI(Å:I"Å;AI(ÇI$ÇAI(ÈI&È9I(ÉI(ÉI(ÉI(É"I(É$I(É&I(c                 ó  — ddl m} | j                  d«      �r| j                  d«      }t        |t        «      r|j                  |j                  ¬«       t        |t        «      r$|j                  D ]  }|j                  |¬«       Œ y y y ©Nr   )Úuser_api_key_cacherJ   )Úkey)	Úlitellm.proxy.proxy_serverrì   r’   r¿   r"   Údelete_cacher@   r   Úuser_ids)Úkwargsrì   Úupdate_user_requestr@   s       r4   Ú_delete_user_id_from_cacheró   ä  s…   € Ý=à‡z�z�&ÓÐ%Ø%+§Z¡Z°Ó%7ÐÜÐ)Ô+<Ô=Ø×+Ñ+Ð0C×0KÑ0KÐ+ÔLô Ð)Ô+<Ô=Ø.×7Ñ7ò =�Ø"×/Ñ/°GÐ/Õ<ñ=ð >ð &r7   c                 ó0  — ddl m} | j                  d«      �| j                  d«      }t        |t        «      r|j                  |j                  ¬«       t        |t        «      r1|j                  r$|j                  D ]  }|j                  |¬«       Œ y y y y rë   )	rî   rì   r’   r¿   r    rï   rí   r   r©   )rñ   rì   Úupdate_requestrí   s       r4   Ú_delete_api_key_from_cacherö   ò  sŒ   € Ý=à‡z�z�&ÓÐ%Ø &§
¡
¨6Ó 2ˆÜ�nÔ&6Ô7Ø×+Ñ+°×0BÑ0BÐ+ÔCô �n¤jÔ1°n×6IÒ6IØ%×*Ñ*ò 9�Ø"×/Ñ/°CÐ/Õ8ñ9ð 7JÐ1ð &r7   c                 ó  — ddl m} | j                  d«      �r| j                  d«      }t        |t        «      r|j                  |j                  ¬«       t        |t        «      r$|j                  D ]  }|j                  |¬«       Œ y y y rë   )	rî   rì   r’   r¿   r!   rï   rÐ   r   Úteam_ids)rñ   rì   rõ   rÐ   s       r4   Ú_delete_team_id_from_cacherù      s�   € Ý=à‡z�z�&ÓÐ%Ø &§
¡
¨6Ó 2ˆÜ�nÔ&7Ô8Ø×+Ñ+°×0FÑ0FÐ+ÔGô �nÔ&7Ô8Ø)×2Ñ2ò =�Ø"×/Ñ/°GÐ/Õ<ñ=ð 9ð &r7   c                 ó  — ddl m} | j                  d«      �r| j                  d«      }t        |t        «      r|j                  |j                  ¬«       t        |t        «      r$|j                  D ]  }|j                  |¬«       Œ y y y rë   )	rî   rì   r’   r¿   r   rï   r@   r   rð   )rñ   rì   rõ   r@   s       r4   Ú_delete_customer_id_from_cacherû     s�   € Ý=à‡z�z�&ÓÐ%Ø &§
¡
¨6Ó 2ˆÜ�nÔ&;Ô<Ø×+Ñ+°×0FÑ0FÐ+ÔGô �nÔ&;Ô<Ø)×2Ñ2ò =�Ø"×/Ñ/°GÐ/Õ<ñ=ð =ð &r7   Úrequest_kwargsÚfunction_namec           
   ƒ   ó  K  — ddl m} ddlm} |j                  |j
                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  dœ	}|�“|j                  �†||v r�||   }t        |j                  xs d|j                   xs d|j"                  | ¬«      }|j%                  dd	«      j'                  «       }|j                  j)                  |||¬
«      ƒ d{  –—†  yyyy7 Œ­w)z»
    Sends a slack alert when:
    - A virtual key is created, updated, or deleted
    - An internal user is created, updated, or deleted
    - A team is created, updated, or deleted
    r   )Úproxy_logging_obj)Ú	AlertType)	Úgenerate_key_fnÚupdate_key_fnÚdelete_key_fnÚnew_teamÚupdate_teamÚdelete_teamÚnew_userÚuser_updateÚdelete_userNÚUnknown)Úcreated_by_user_idÚcreated_by_user_roleÚcreated_by_key_aliasrü   Ú_ú )Ú	key_eventÚ
event_nameÚ
alert_type)rî   rÿ   Ú)litellm.types.integrations.slack_alertingr   Únew_virtual_key_createdÚvirtual_key_updatedÚvirtual_key_deletedÚnew_team_createdÚteam_updatedÚteam_deletedÚnew_internal_user_createdÚinternal_user_updatedÚinternal_user_deletedÚslack_alerting_instancer$   r@   r�   Ú	key_aliasÚreplaceÚtitleÚsend_virtual_key_event_slack)	rü   r�   rý   rÿ   r   Ú!management_function_to_event_nameÚ_event_namer  r  s	            r4   Úsend_management_endpoint_alertr$    s  è ø€ õ =ÝCð %×<Ñ<Ø"×6Ñ6Ø"×6Ñ6à×.Ñ.Ø ×-Ñ-Ø ×-Ñ-à×7Ñ7Ø ×6Ñ6Ø ×6Ñ6ñ0Ð%ð Ð$Ð):×)RÑ)RÐ)^àÐ=Ñ=Ø,MÈmÑ,\ˆKä.Ø#4×#<Ñ#<Ò#IÀ	Ø%6×%@Ñ%@Ò%MÀIØ%6×%@Ñ%@Ø-ô	 ˆIð !,× 3Ñ 3°C¸Ó =× CÑ CÓ EˆJØ#×;Ñ;×XÑXØ#Ø%Ø&ð Yó ÷ ñ ð >ð *_Ð$ðús   ‚D DÄDÄ	DrÇ   c                 ó*   — t        | t        «      r| S dS )z8Return ``value`` as an opaque mapping when it is a dict.N)r¿   Údict©rÇ   s    r4   Ú_object_mappingr(  N  ó   € ä˜u¤dÔ+ˆ5Ð5°Ð5r7   c                 ó*   — t        | t        «      r| S dS )z9Return ``value`` as an opaque sequence when it is a list.N)r¿   rÀ   r'  s    r4   Ú_object_listr+  S  r)  r7   Úentryc                 óx   ‡ — t        ‰ t        «      r‰ j                  nˆ fd„} |d«       |d«       |d«      ddœS )Nc                 ó   •— t        ‰| d «      S r0   )r«   )r™   r,  s    €r4   ú<lambda>z#_redacted_env_var.<locals>.<lambda>Y  s   ø€ Ô^eÐfkÐmnÐptÓ^u€ r7   ÚnameÚscopeÚdescriptionÚ )r0  r1  r2  rÇ   )r¿   r&  r’   )r,  r’   s   ` r4   Ú_redacted_env_varr4  X  s;   ø€ Ü7AÀ%ÌÔ7N¨%¯)ª)ÓTu€Cá�F“Ù�W“Ù˜=Ó)Øñ	ð r7   Úrecordc                 ó  — t        | «      }t        |�|j                  d«      nt        | dd«      «      }|€| S |D �cg c]  }t	        |«      ‘Œ }}|�i |¥d|i¥S t        | t        «      r| j                  d|i¬«      S | S c c}w )zýReturn ``record`` with its ``env_vars[].value`` blanked.

    Copies rather than mutating, because the record aliases the live response
    object that is also returned to the caller. Records without an ``env_vars``
    list are returned unchanged.
    NÚenv_vars)rƒ   )r(  r+  r’   r«   r4  r¿   r   Ú
model_copy)r5  Ú
record_mapr7  r,  Úredacteds        r4   Ú_redact_record_env_varsr;  b  s¢   € ô (¨Ó/€JÜ"Ø&0Ð&<ˆ
�‰�zÔ"Ä'È&ÐR\Ð^bÓBcó€Hð ÐØˆØ=EÖF°EÔ(¨Õ/ÐF€HÐFØÐØ3�*Ð3˜j¨(Ñ3Ð3Ü�&œ)Ô$Ø× Ñ ¨°XÐ(>Ð Ó?Ð?Ø€Mùò Gs   ½BrÎ   c                 óú   — t        | j                  d«      «      }|�|D �cg c]  }t        |«      ‘Œ c}| d<   t        | j                  d«      «      }|�|D �cg c]  }t        |«      ‘Œ c}| d<   yyc c}w c c}w )a)  Blank ``env_vars[].value`` in a management response before telemetry.

    MCP endpoints return decrypted ``scope="global"`` env var values so the admin
    UI can pre-fill the edit form; those values are upstream credentials and must
    not be serialized verbatim into OTEL spans, where an observability user could
    read them. The values surface both at the top level (single-server
    create/update) and nested under ``items`` (the submissions queue), so both are
    scrubbed. Names, scopes, and descriptions are kept so traces stay useful.
    r7  Nr•   )r+  r’   r4  r;  )rÎ   r7  r,  r•   Úitems        r4   Ú_redact_env_var_valuesr>  w  s}   € ô # 8§<¡<°
Ó#;Ó<€HØÐØFNÖO¸UÔ 1°%Õ 8ÒOˆ�Ñä §¡¨WÓ 5Ó6€EØÐØGLÖM¸tÔ4°TÕ:ÒMˆ�Òð ùò  Pùò Ns   ¡A3ÁA8Úfuncrñ   Úparent_otel_spanÚ
start_timeÚend_timeÚresultÚ	exceptionc              ƒ   ó.  K  — ddl m} |€yt        «       ry|j                  d«      }|�#ddlm}	  |	|«      }
t        |¬«      ƒ d{  –—† }n| j                  }
i }t        h d£«      }d}|€A|�?	 t        |«      }|j                  «       D ��ci c]  \  }}||vsŒ||“Œ }}}t        |«       t        |
|||||¬«      }|€|j                  ||¬	«      ƒ d{  –—†  y|j                  ||¬	«      ƒ d{  –—†  y7 Œ°c c}}w # t        $ r d}Y Œ`w xY w7 Œ:7 Œ ­w)
uî  Stamp + end the parent OTEL SERVER span for a management endpoint.

    Routes the request/response (or exception) through the OTEL success/failure
    hook. Falls back to ``func.__name__`` for the route when the handler has no
    ``http_request`` param â€” endpoints like ``/key/generate`` never receive one,
    and gating the hook on it leaked their SERVER span (created in auth, never
    ended â†’ never exported). Always emitting keeps both success and failure
    paths consistent.
    r   )Úopen_telemetry_loggerNÚhttp_request)Úget_request_route)Úrequest>	   rí   ÚtokenÚsecretÚapi_keyÚpasswordÚprivate_keyÚaccess_tokenÚrefresh_tokenÚservice_account_key)ÚrouteÚrequest_datarÎ   rA  rB  rD  )Úlogging_payloadr@  )rî   rF  r   r’   Úlitellm.proxy.auth.auth_utilsrH  r%   r8   Ú	frozensetr&  r•   r>  rà   r   Ú&async_management_endpoint_success_hookÚ&async_management_endpoint_failure_hook)r?  rñ   r@  rA  rB  rC  rD  rF  rG  rH  rR  Úrequest_bodyÚ_CREDENTIAL_FIELDSÚ	_responseÚrawr™   rš   rT  s                     r4   Ú#_emit_management_endpoint_otel_spanr]  Š  s]  è ø€ õ$ AàÐ$Øô
 ÔØà*0¯*©*°^Ó*D€LØÐõ	
ñ " ,Ó/ˆÜ#5¸lÔ#K×K‰à—‘ˆØˆä )ò
	
ó!Ðð +/€IØÐ˜VÐ/ð	Ü/3°F«|ˆCØ*-¯)©)«+×U¡$ ! Q¸ÐBTÒ9T˜˜A™ÐUˆIÑUÜ" 9Ô-ô >ØØ!ØØØØô€Oð ÐØ#×JÑJØ+Ø-ð Kó 
÷ 	
ñ 	
ð
 $×JÑJØ+Ø-ð Kó 
÷ 	
ñ 	
ðW Lúó. Vøäò 	ØŠIð	úð	
øð
	
úsl   ‚ADÁC8Á&DÁ/D  ÂC:ÂC:ÂD  Â-)DÃDÃDÃ2DÃ3DÃ:D  Ä DÄDÄDÄDÄDc                 ó.   ‡ — t        ‰ «      ˆ fd„«       }|S )z{
    This wrapper does the following:

    1. Log I/O, Exceptions to OTEL
    2. Create an Audit log for success calls
    c            	   Ÿ   ór  •K  — t        j                  «       }	  ‰	| i |¤Žƒ d {  –—† }t        j                  «       }	 |j                  d«      xs
 t        «       }t	        ||‰	j
                  ¬«      ƒ d {  –—†  t        |dd «      }|�t        ‰	|||||¬«      ƒ d {  –—†  t        |¬«       t        |¬«       t        |¬«       t        |¬«       |S 7 Œ°7 Œ`7 Œ:# t        $ r*}t        j                  dt        |«      «       Y d }~|S d }~ww xY w# t        $ rš}t        j                  «       }|j                  d«      xs
 t        «       }t        |dd «      }|�S	 t        ‰	|||||¬«      ƒ d {  –—†7   |‚# t        $ r*}t        j                  dt        |«      «       Y d }~|‚d }~ww xY w|‚d }~ww xY w­w)	Nr�   )rü   r�   rý   r@  )r?  rñ   r@  rA  rB  rC  )rñ   z(Error in management endpoint wrapper: %s)r?  rñ   r@  rA  rB  rD  zHError emitting OTEL span in management endpoint wrapper failure path: %s)r   Únowr’   r#   r$  r8   r«   r]  rö   ró   rù   rû   rà   r   Údebugr<   )
Úargsrñ   rA  rC  rB  r�   r@  ÚeÚotel_excr?  s
            €r4   Úwrapperz,management_endpoint_wrapper.<locals>.wrapperè  sØ  øè ø€ ä$ŸL™L›Nˆ
ð7	Ù"&¨Ð"7°Ñ"7×7ˆFÜ—|‘|“~ˆHðYØ4:·J±JÐ?RÓ4SÒ4gÔWeÓWgÐ!ä4Ø#)Ø&7Ø"&§-¡-ô÷ ð ô
 18Ð8IÐK]Ð_cÓ0dÐ Ø#Ð/Ü=Ø!Ø%Ø)9Ø#-Ø!)Ø%ô÷ ð ô +°&Õ9Ü*°&Õ9Ü*°&Õ9Ü.°fÕ=ð
 ˆMð= 8øð
øðùô ò Yä×$Ñ$Ð%OÔQTÐUVÓQW×XÐXàˆMûð	Yûô
 ò 	Ü—|‘|“~ˆHà06·
±
Ð;NÓ0OÒ0cÔSaÓScÐÜ&Ð'8Ð:LÈdÓSÐØÐ+ðÜ=Ø!Ø%Ø)9Ø#-Ø!)Ø"#ô÷ ñ ð  ˆGøô !ò ô #×(Ñ(ØbÜ˜H›÷ð ð
 ˆGûðúð ˆGûð/	üsÂ   ƒF7™D ¤C¥D ¾8C Á6CÁ7'C ÂCÂ4C ÃD ÃF7ÃD ÃC ÃC Ã	DÃ$D	ÄD ÄF7Ä	DÄD Ä	F4ÄA F/ÅE7Å/E2Å0E7Å5F/Å7	F*Æ F%ÆF/Æ%F*Æ*F/Æ/F4Æ4F7r   )r?  re  s   ` r4   Úmanagement_endpoint_wrapperrf  à  s#   ø€ ô ˆ4ƒ[ó9ó ð9ðv €Nr7   r0   )NN)NNNN)`Úcollections.abcr   r   r   r   r   Ú	functoolsr	   Útypingr
   r   r   Úfastapir   r   Úpydanticr   r�   Úlitellm._loggingr   Úlitellm._uuidr   Ú&litellm.integrations.otel.model.configr   Úlitellm.proxy._typesr   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r$   Ú-litellm.proxy.common_utils.http_parsing_utilsr%   Ú)litellm.proxy.common_utils.timezone_utilsr&   Úlitellm.proxy.utilsr'   r(   Ú&litellm.repositories.budget_repositoryr)   Ú'litellm.repositories.table_repositoriesr*   Ú$litellm.repositories.user_repositoryr+   r-   r?   rE   rI   rT   r^   rc   rp   rr   rt   r<   r   r=   r…   r&  r‰   r›   r³   rº   ÚtuplerC   rÉ   ÚfloatrÀ   rÏ   rÖ   ré   ró   rö   rù   rû   r$  r(  r+  r4  r;  r>  rà   r]  rf  r1   r7   r4   ú<module>rx     sK  ð÷ HÔ GÝ Ý ß 'Ñ 'ç *Ý ã Ý +Ý Ý E÷÷ ÷ ÷ ÷ õ( MÝ Kß <Ý CÝ LÝ ?ô6�Hô 6ô6˜ô 6ô6˜(ô 6ôc˜ô cô	b�xô 	bôm ô môH�Hô Hð$[˜|ð [°ÀÑ1Eð [ÐJZó [ð_ ð _°=À4Ñ3Gð _ÐL^ó _ðj¨,ð j¸MÈDÑ<Pð jÐUoó jð
,Øð
,Ø%2°TÑ%9ð
,ØGJð
,àÐÑ ó
,ðØ ðØ+.ðØ=DÀSÈ&À[Ñ=Qðà˜Ñóð NØðNØ%2°TÑ%9ðNØFJÈ3ÐPVÈ;ÑFWðNà˜ÑóNñ¨Cð ¸SÀ4¹Zð ÐSWÐX[Ð]cÐXcÑSdó ð&L"à˜d™
ðL"ð &ðL"ð  ð	L"ð
 "ðL"ð 	ˆ4�ZóL"ðf	3Ð ˜˜u S¨# X™Ñ/ó 	ð" ,0Ø#ñ2 Øð2 àð2 ð &ð2 ð "ð	2 ð
 " D™jð2 ð 	˜Ñð2 ð 	ˆ4�Zó2 ðz  $ñ/Øð/à%ð/ð "ð/ð  ™ð	/ð
 ˜‘I Ñ$ð/ð ˜4‘Zð/ð   $™Jð/ð 	˜Ñð/ð 	ˆ4�Zó/ðf Y]ñØðØ*-ðØ8;ðØANÐQUÑAUðà	óð2 *.Ø'+Ø"&Ø#ñX3ØðX3à ™ðX3ð  ðX3ð ð	X3ð
 &ðX3ð "ðX3ð   $™JðX3ð ˜‘I Ñ$ðX3ð ˜4‘ZðX3ð 	˜ÑðX3ð ÐÐ4°tÑ;Ð;Ñ<óX3òv=ò9ò=ò=ð/Øð/à%ð/ð ó/ðd6˜6ð 6 g¨c°6¨kÑ&:¸TÑ&Aó 6ð
6˜ð 6 8¨FÑ#3°dÑ#:ó 6ð
˜Vð ¨¨S°&¨[Ñ(9ó ð Fð ¨vó ð*N ^°C¸°KÑ%@ð NÀTó Nð2 Ø"&ñS
Ø
ðS
àðS
ð ˜T‘kðS
ð ð	S
ð
 ðS
ð ðS
ð ˜4ÑðS
ð 
óS
ólDr7   