Ë
    uPêj&�  ã                   ó"  — U d Z ddlZddlZddlmZmZmZ ddlmZ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 dd	lmZ dd
l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  ddl!m"Z"m#Z#m$Z$ ddl!m"Z% dZ&ee'd<    G d„ d«      Z( G d„ de«      Z)y)aè  
Provider budget limiting

Use this if you want to set $ budget limits for each provider.

Note: This is a filter, like tag-routing. Meaning it will accept healthy deployments and then filter out deployments that have exceeded their budget limit.

This means you can use this with weighted-pick, lowest-latency, simple-shuffle, routing etc

Example:
```
openai:
        budget_limit: 0.000000000001
        time_period: 1d
anthropic:
        budget_limit: 100
        time_period: 7d
```
é    N)ÚdatetimeÚ	timedeltaÚtimezone)ÚAnyÚFinal)Úverbose_router_logger)Ú	DualCache)ÚRedisPipelineIncrementOperation)ÚCustomLoggerÚSpan)Ú&get_metadata_variable_name_from_kwargs)Úduration_in_seconds)Ú_get_tags_from_request_kwargs)Ú%_get_prometheus_logger_from_callbacks)ÚAllMessageValues)ÚDeploymentTypedDictÚLiteLLM_ParamsÚRouterErrors)ÚBudgetConfigÚGenericBudgetConfigTypeÚStandardLoggingPayload)r   é   ÚDEFAULT_REDIS_SYNC_INTERVALc                   óÔ   — e Zd ZdZdZdeeef   fd„Zdedefd„Z	dedefd„Z
dedefd	„Zddededefd„Zd„ Zd„ Zd„ Zd„ Zdefd„Zdeeef   fd„Zdej
                  eef   fd„Zy
)Ú_LiteLLMParamsDictViewzÐ
    Lightweight attribute view over `litellm_params` dict.

    This avoids pydantic construction in request hot-path while preserving
    attribute-style access used by `litellm.get_llm_provider(...)`.
    ©Ú_paramsÚparamsc                 ó   — || _         y ©Nr   )Úselfr   s     úk/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/router_strategy/budget_limiter.pyÚ__init__z_LiteLLMParamsDictView.__init__9   s	   € Øˆ�ó    ÚkeyÚreturnc                 ó8   — | j                   j                  |«      S r    ©r   Úget©r!   r%   s     r"   Ú__getattr__z"_LiteLLMParamsDictView.__getattr__<   ó   € Ø�|‰|×Ñ Ó$Ð$r$   c                 ó8   — | j                   j                  |«      S r    r(   r*   s     r"   Ú__getitem__z"_LiteLLMParamsDictView.__getitem__?   r,   r$   c                 ó   — || j                   v S r    r   r*   s     r"   Ú__contains__z#_LiteLLMParamsDictView.__contains__B   s   € Ø�d—l‘lÐ"Ð"r$   NÚdefaultc                 ó:   — | j                   j                  ||«      S r    r(   )r!   r%   r1   s      r"   r)   z_LiteLLMParamsDictView.getE   s   € Ø�|‰|×Ñ  WÓ-Ð-r$   c                 ó6   — | j                   j                  «       S r    )r   Úkeys©r!   s    r"   r4   z_LiteLLMParamsDictView.keysH   s   € Ø�|‰|× Ñ Ó"Ð"r$   c                 ó6   — | j                   j                  «       S r    )r   Úvaluesr5   s    r"   r7   z_LiteLLMParamsDictView.valuesK   s   € Ø�|‰|×"Ñ"Ó$Ð$r$   c                 ó6   — | j                   j                  «       S r    )r   Úitemsr5   s    r"   r9   z_LiteLLMParamsDictView.itemsN   s   € Ø�|‰|×!Ñ!Ó#Ð#r$   c                 ó,   — t        | j                  «      S r    )Úiterr   r5   s    r"   Ú__iter__z_LiteLLMParamsDictView.__iter__Q   ó   € Ü�D—L‘LÓ!Ð!r$   c                 ó,   — t        | j                  «      S r    )Úlenr   r5   s    r"   Ú__len__z_LiteLLMParamsDictView.__len__T   s   € Ü�4—<‘<Ó Ð r$   c                 ó,   — t        | j                  «      S r    ©Údictr   r5   s    r"   rC   z_LiteLLMParamsDictView.dictW   r=   r$   c                 ó,   — t        | j                  «      S r    rB   r5   s    r"   Ú
model_dumpz!_LiteLLMParamsDictView.model_dumpZ   r=   r$   r    )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú	__slots__rC   Ústrr   r#   r+   r.   Úboolr0   r)   r4   r7   r9   r<   Úintr@   ÚbuiltinsrE   © r$   r"   r   r   /   sÂ   „ ñð €Ið˜t C¨ H™~ó ð%˜sð % só %ð%˜sð % só %ð# ð #¨ó #ñ.�sð . Sð .°Có .ò#ò%ò$ò"ð!˜ó !ð"�d˜3 ˜8‘nó "ð"˜HŸM™M¨#¨s¨(Ñ3ô "r$   r   c                   óP  — e Zd Z	 d8dededz  deeeeef   z     dz  fd„Z		 	 d9dededee
   dz  d	edz  d
edz  dee   fd„Zdeeeef      deeeef      deeef   deeef   deedz     deeef   dee   deeeeef      ef   fd„Z	 d8deeeef      d	edz  deee   eeef   eeef   eedz     f   fd„Zdedededefd„Zdedededededefd„Zdededefd„Zd„ Zdedededefd „Zd!„ Zd"„ Zd#„ Zd$ededz  fd%„Zd&ededz  fd'„Zd(ededz  fd)„Zd*ededz  fd+„Zd&ed,ed-efd.„Zd&ededz  fd/„Z d&ededz  fd0„Z!d&edefd1„Z"e#	 d8dedz  dee   eeeef      z  dz  fd2„«       Z$d3„ Z%	 d8deeeeef   z     dz  fd4„Z&d*eeef   ez  ddfd5„Z'd$eddfd6„Z(d7„ Z)y):ÚRouterBudgetLimitingNÚ
dual_cacheÚprovider_budget_configÚ
model_listc                 óp  — || _         g | _        t        j                  | j	                  «       «       || _        d | _        d | _        | j                  «        | j                  |¬«       | j                  «        t        t        j                  t        «      r t        j                  j!                  | «       y y )N©rT   )rR   Úredis_increment_operation_queueÚasyncioÚcreate_taskÚ(periodic_sync_in_memory_spend_with_redisrS   Údeployment_budget_configÚtag_budget_configÚ_init_provider_budgetsÚ_init_deployment_budgetsÚ_init_tag_budgetsÚ
isinstanceÚlitellmÚ	callbacksÚlistÚlogging_callback_managerÚadd_litellm_callback)r!   rR   rS   rT   s       r"   r#   zRouterBudgetLimiting.__init___   s–   € ð %ˆŒØVXˆÔ,Ü×Ñ˜D×IÑIÓKÔLØF\ˆÔ#ØHLˆÔ%ØAEˆÔØ×#Ñ#Ô%Ø×%Ñ%°Ð%Ô<Ø×ÑÔ ô ”g×'Ñ'¬Ô.Ü×,Ñ,×AÑAÀ$ÕGð /r$   ÚmodelÚhealthy_deploymentsÚmessagesÚrequest_kwargsÚparent_otel_spanr&   c              ƒ   óV  K  — t        |t        «      r|g}t        |«      dk(  r|S g }| j                  ||¬«      ƒ d{  –—† \  }}}	}
t        |«      dkD  rÍ| j                  j                  ||¬«      ƒ d{  –—† }|xs dgt        |«      z  }i }t        |«      D ]  \  }}t        ||   xs d«      ||<   Œ | j                  |||	|
||t        |t        |xs i «      ¬«      ¬«      \  }}t        |«      dk(  r(t        t        j                  j                  › d|› �«      ‚|S |S 7 Œæ7 Œ°­w)	zõ
        Filter out deployments that have exceeded their provider budget limit.


        Example:
        if deployment = openai/gpt-3.5-turbo
            and openai spend > openai budget limit
                then skip this deployment
        r   )rg   ri   N)r4   rj   ç        ©ri   Úmetadata_variable_name)rg   Úprovider_configsÚdeployment_configsÚdeployment_providersÚ	spend_mapÚpotential_deploymentsÚrequest_tagsú: )r`   rC   r?   Ú0_async_get_cache_keys_for_router_budget_limitingrR   Úasync_batch_get_cacheÚ	enumerateÚfloatÚ$_filter_out_deployments_above_budgetr   r   Ú
ValueErrorr   Ú+no_deployments_with_provider_budget_routingÚvalue)r!   rf   rg   rh   ri   rj   rs   Ú
cache_keysro   rp   rq   Ú_current_spendsÚcurrent_spendsrr   Úidxr%   Údeployment_above_budget_infos                    r"   Úasync_filter_deploymentsz-RouterBudgetLimiting.async_filter_deploymentss   sŽ  è ø€ ô& Ð)¬4Ô0Ø#6Ð"7Ðô Ð"Ó# qÒ(Ø&Ð&à,.Ðð ×GÑGØ 3Ø)ð Hó 
÷ 
ñ	
ØØØØ ô ˆz‹?˜QÒØ+/¯?©?×+PÑ+PØØ!1ð ,Qó ,÷ &ˆOð +:Ò*T¸c¸UÄSÈÃ_Ñ=TˆNð 24ˆIÜ% jÓ1ò C‘��SÜ!& ~°cÑ':Ò'A¸cÓ!B�	˜#’ðCð ×9Ñ9Ø$7Ø!1Ø#5Ø%9Ø#Ø&;Ü:Ø#1Ü+QÐR`ÒRfÐdfÓ+gôð :ó ñØ%Ø,ô Ð(Ó)¨QÒ.Ü Ü#×OÑO×UÑUÐVÐVXÐYuÐXvÐwóð ð )Ð(à&Ð&ðS
øð&ús"   ‚<D)¾D%¿7D)Á6D'Á7B/D)Ä'D)rs   ro   rp   rq   rr   rt   c                 óò  — d}t        |«      D �]c  \  }	}
d}| j                  r¶|	t        |«      k  r||	   }n| j                  |
«      }||v r�||   }|j                  €ŒP|j                  d|› d|j                  › �d«      }| j                  |||j                  ¬«       |j                  r/||j                  k\  r d|› d|› d	|j                  › �}||› d
�z  }d}ŒË| j                  rÜ|rÚ|
j                  d«      }|
j                  d«      xs i }|j                  d«      }|
j                  di «      j                  d«      }||v r~||   }|j                  d|› d|j                  › �d«      }|j                  rK||j                  k\  r<d|› d|› d|› d|› d	|j                  › �
}t        j                  |«       ||› d
�z  }d}�Œ³| j                  r�|rŽ|D ]‰  }| j                  |«      }|sŒ|j                  d|› d|j                  › �d«      }|j                  sŒF||j                  k\  sŒVd|› d|› d|j                  › �}t        j                  |«       ||› d
�z  }d}Œ‹ |s�ŒS|j                  |
«       �Œf ||fS )aÃ  
        Filter out deployments that have exceeded their budget limit.
        Follow budget checks are run here:
            - Provider budget
            - Deployment budget
            - Request tags budget
        Returns:
            Tuple[List[Dict[str, Any]], str]:
                - A tuple containing the filtered deployments
                - A string containing debug information about deployments that exceeded their budget limit.
        Ú Túprovider_spend:ú:rl   ©ÚproviderÚspendÚbudget_limitzExceeded budget for provider ru   z >= ú
FÚ
model_nameÚlitellm_paramsrf   Ú
model_infoÚidúdeployment_spend:z+Exceeded budget for deployment model_name: z, litellm_params.model: z, model_id: ú
tag_spend:zExceeded budget for tag='z', tag_spend=z, tag_budget_limit=)rx   rS   r?   Ú _get_llm_provider_for_deploymentÚ
max_budgetr)   Úbudget_durationÚ+_track_provider_remaining_budget_prometheusr[   r   Údebugr\   Ú_get_budget_config_for_tagÚappend)r!   rs   rg   ro   rp   rq   rr   rt   r‚   r�   Ú
deploymentÚis_within_budgetr‰   ÚconfigÚcurrent_spendÚ	debug_msgÚ_model_nameÚ_litellm_paramsÚ_litellm_model_nameÚmodel_idÚ_tagÚ_tag_budget_configÚ
_tag_spends                          r"   rz   z9RouterBudgetLimiting._filter_out_deployments_above_budget¿   sR  € ð, -/Ð$Ü(Ð)<Ó=ó 9	9‰OˆC�Ø#Ðð ×*Ò*ØœÐ1Ó2Ò2Ø3°CÑ8‘Hà#×DÑDÀZÓP�HØÐ/Ñ/Ø-¨hÑ7�FØ×(Ñ(Ð0Ø Ø$-§M¡M°OÀHÀ:ÈQÈv×OeÑOeÐNfÐ2gÐilÓ$m�MØ×DÑDØ!)Ø+Ø%+×%6Ñ%6ð Eô ð ×(Ò(¨]¸f×>OÑ>OÒ-OØ&CÀHÀ:ÈRÐP]ÈÐ^bÐci×ctÑctÐbuÐ$v˜	Ø4¸9¸+ÀRÐ8HÑHÐ4Ø+0Ð(Ø ð ×,Ò,Ñ1AØ(Ÿn™n¨\Ó:�Ø",§.¡.Ð1AÓ"BÒ"HÀb�Ø&5×&9Ñ&9¸'Ó&BÐ#Ø%Ÿ>™>¨,¸Ó;×?Ñ?ÀÓE�ØÐ1Ñ1Ø/°Ñ9�FØ$-§M¡MÐ4EÀhÀZÈqÐQW×QgÑQgÐPhÐ2iÐknÓ$o�MØ×(Ò(¨]¸f×>OÑ>OÒ-OØ&QÐR]ÐQ^Ð^vð  xKð  wLð  LXð  Yað  Xbð  bdð  erð  dsð  swð  x~÷  xNñ  xNð  wOð  %P˜	Ü-×3Ñ3°IÔ>Ø4¸9¸+ÀRÐ8HÑHÐ4Ø+0Ð(Ù à×%Ò%Ñ*:Ø(ò %�DØ)-×)HÑ)HÈÓ)NÐ&Ú)Ø%.§]¡]Ø(¨¨¨aÐ0B×0RÑ0RÐ/SÐTØó&˜
ð .×8Ó8¸ZÐK]×KhÑKhÓ=hØ*CÀDÀ6ÈÐWaÐVbÐbuð  wI÷  wTñ  wTð  vUð  )V˜IÜ1×7Ñ7¸	ÔBØ8¸y¸kÈÐ<LÑLÐ8Ø/4Ð,Ø$ð%ó  Ø%×,Ñ,¨ZÖ8ðs9	9ðv %Ð&BÐBÐBr$   c              ƒ   ó¼  K  — g }i }i }g }g }| j                   rt        |t        |xs i «      ¬«      }|D ]â  }| j                  ri| j	                  |«      }	|j                  |	«       |	�E| j                  |	«      }
|
�2|
j                  �&|
||	<   |j                  d|	› d|
j                  › �«       | j                  sŒ…|j                  di «      j                  d«      }|€Œ©| j                  |«      }
|
€Œ½|
||<   |j                  d|› d|
j                  › �«       Œä |D ]7  }| j                  |«      }|sŒ|j                  d|› d|j                  › �«       Œ9 ||||fS ­w)aB  
        Returns list of cache keys to fetch from router cache for budget limiting and provider and deployment configs

        Returns:
            Tuple[List[str], Dict[str, GenericBudgetInfo], Dict[str, GenericBudgetInfo], List[Optional[str]]]:
                - List of cache keys to fetch from router cache for budget limiting
                - Dict of provider budget configs `provider_configs`
                - Dict of deployment budget configs `deployment_configs`
                - List of resolved providers aligned by deployment index `deployment_providers`
        rm   r†   r‡   r�   r�   r‘   r’   )r\   r   r   rS   r“   r™   Ú_get_budget_config_for_providerr•   r[   r)   Ú!_get_budget_config_for_deploymentr˜   )r!   rg   ri   r~   ro   rp   rq   Ú_request_tagsrš   r‰   Úbudget_configr¢   r£   r¤   s                 r"   rv   zERouterBudgetLimiting._async_get_cache_keys_for_router_budget_limiting  sª  è ø€ ð( (*ˆ
Ø@BÐØBDÐØ8:Ðð $&ˆØ×!Ò!Ü9Ø-Ü'MÈnÒNbÐ`bÓ'côˆMð
 .ò 	jˆJà×*Ò*Ø×@Ñ@ÀÓL�Ø$×+Ñ+¨HÔ5ØÐ'Ø$(×$HÑ$HÈÓ$R�MØ$Ð0°]×5RÑ5RÐ5^Ø5BÐ(¨Ñ2Ø"×)Ñ)¨O¸H¸:ÀQÀ}×GdÑGdÐFeÐ*fÔgð ×,Ó,Ø%Ÿ>™>¨,¸Ó;×?Ñ?ÀÓE�ØÑ'Ø$(×$JÑ$JÈ8Ó$T�MØ$Ñ0Ø7DÐ*¨8Ñ4Ø"×)Ñ)Ð,=¸h¸ZÀqÈ×IfÑIfÐHgÐ*hÕið%	jð* "ò 	\ˆDØ!%×!@Ñ!@ÀÓ!FÐÚ!Ø×!Ñ! J¨t¨f°AÐ6H×6XÑ6XÐ5YÐ"ZÕ[ð	\ð
 ØØØ ð	
ð 	
ùs   ‚B7EÂ:#EÃEÃ2A EÄ3)EÚstart_time_keyÚcurrent_timeÚttl_secondsc              ƒ   óÂ   K  — | j                   j                  |«      ƒ d{  –—† }|€(| j                   j                  |||¬«      ƒ d{  –—†  |S t        |«      S 7 Œ97 Œ­w)zÑ
        Checks if the key = `provider_budget_start_time:{provider}` exists in cache.

        If it does, return the value.
        If it does not, set the key to `current_time` and return the value.
        N©r%   r}   Úttl)rR   Úasync_get_cacheÚasync_set_cachery   )r!   r«   r¬   r­   Úbudget_starts        r"   Ú_get_or_set_budget_start_timez2RouterBudgetLimiting._get_or_set_budget_start_timeT  s`   è ø€ ð %)§O¡O×$CÑ$CÀNÓ$S×SˆØÐØ—/‘/×1Ñ1°nÈLÐ^iÐ1Ój×jÐjØÐÜ�\Ó"Ð"ð	 Tøàjús!   ‚A¡A¢'AÁ	AÁ
AÁAÚ	spend_keyÚresponse_costc              ƒ   ó®   K  — | j                   j                  |||¬«      ƒ d{  –—†  | j                   j                  |||¬«      ƒ d{  –—†  |S 7 Œ,7 Œ­w)aò  
        Handle start of new budget window by resetting spend and start time

        Enters this when:
        - The budget does not exist in cache, so we need to set it
        - The budget window has expired, so we need to reset everything

        Does 2 things:
        - stores key: `provider_spend:{provider}:1d`, value: response_cost
        - stores key: `provider_budget_start_time:{provider}`, value: current_time.
            This stores the start time of the new budget window
        r¯   N)rR   r²   )r!   rµ   r«   r¬   r¶   r­   s         r"   Ú_handle_new_budget_windowz.RouterBudgetLimiting._handle_new_budget_windowa  sX   è ø€ ð( �o‰o×-Ñ-°)À=ÐVaÐ-Ób×bÐbØ�o‰o×-Ñ-°.ÈÐZeÐ-Óf×fÐfØÐð 	cøØfús!   ‚"A¤A¥%AÁ
AÁAÁAr°   c              ƒ   óÂ   K  — | j                   j                  j                  |||¬«      ƒ d{  –—†  t        |||¬«      }| j                  j                  |«       y7 Œ.­w)a²  
        Increment spend within existing budget window

        Runs once the budget start time exists in Redis Cache (on the 2nd and subsequent requests to the same provider)

        - Increments the spend in memory cache (so spend instantly updated in memory)
        - Queues the increment operation to Redis Pipeline (using batched pipeline to optimize performance. Using Redis for multi instance environment of LiteLLM)
        r¯   N)r%   Úincrement_valuer°   )rR   Úin_memory_cacheÚasync_incrementr
   rW   r™   )r!   rµ   r¶   r°   Úincrement_ops        r"   Ú"_increment_spend_in_current_windowz7RouterBudgetLimiting._increment_spend_in_current_windowy  sh   è ø€ ð �o‰o×-Ñ-×=Ñ=ØØØð >ó 
÷ 	
ð 	
ô
 >ØØ)Øô
ˆð
 	×,Ñ,×3Ñ3°LÕAð	
ús   ‚,A®A¯/Ac              ƒ   ól  K  — t        j                  d«       |j                  d«      dv ry|j                  dd«      }|€t        d«      ‚|j                  dd«      }t	        |j                  d	d
«      «      }|j                  di «      j                  dd«      }|€t        d«      ‚| j                  |«      }	|	r4d|› d|	j                  › �}
d|› �}| j                  |	|
||¬«      ƒ d{  –—†  | j                  |«      }|r4d|› d|j                  › �}d|› �}| j                  ||||¬«      ƒ d{  –—†  t        |t        |xs i «      ¬«      }t        |«      dkD  rP|D ]J  }| j                  |«      }|sŒd|› d|j                  › �}d|› �}| j                  ||||¬«      ƒ d{  –—†  ŒL yy7 ŒÄ7 Œ7 Œ­w)z)Original method now uses helper functionsz/in RouterBudgetLimiting.async_log_success_eventÚ	call_type)Ú_aresponses_websocketÚ
_arealtimeNÚstandard_logging_objectz$standard_logging_payload is requiredr¶   r   r¢   r…   rŽ   Úcustom_llm_providerzcustom_llm_provider is requiredr†   r‡   úprovider_budget_start_time:)rª   rµ   r«   r¶   r‘   zdeployment_budget_start_time:)rn   r’   ztag_budget_start_time:)r   r—   r)   r{   rK   r§   r•   Ú_increment_spend_for_keyr¨   r   r   r?   r˜   )r!   ÚkwargsÚresponse_objÚ
start_timeÚend_timeÚstandard_logging_payloadr¶   r¢   rÄ   rª   rµ   r«   r[   Údeployment_spend_keyÚdeployment_start_time_keyrt   r£   r¤   Ú_tag_spend_keyÚ_tag_start_time_keys                       r"   Úasync_log_success_eventz,RouterBudgetLimiting.async_log_success_eventŽ  s/  è ø€ ä×#Ñ#Ð$UÔVà�:‰:�kÓ"Ð&MÑMØØIOÏÉÐTmÐosÓItÐ Ø#Ð+ÜÐCÓDÐDà&>×&BÑ&BÀ?ÐTUÓ&VˆÜ"Ð#;×#?Ñ#?À
ÈBÓ#OÓPˆØ*0¯*©*Ð5EÀrÓ*J×*NÑ*NÐOdÐfjÓ*kÐØÐ&ÜÐ>Ó?Ð?à#×CÑCÐDWÓXˆÙà!0Ð1DÐ0EÀQÀ}×GdÑGdÐFeÐfˆIØ&AÐBUÐAVÐ$WˆNØ×/Ñ/Ø+Ø#Ø-Ø+ð	 0ó ÷ ð ð +/×*PÑ*PÐQYÓ*ZÐ Ù#à,=¸h¸ZÀqÐIa×IqÑIqÐHrÐ*sÐ Ø1NÈxÈjÐ/YÐ%Ø×/Ñ/Ø6Ø.Ø8Ø+ð	 0ó ÷ ð ô <ØÜ#IÈ&Ê,ÐTVÓ#Wô
ˆô ˆ|Ó˜qÒ Ø$ò 
�Ø%)×%DÑ%DÀTÓ%JÐ"Ú%Ø'1°$°°qÐ9K×9[Ñ9[Ð8\Ð%]�NØ,BÀ4À&Ð*IÐ'Ø×7Ñ7Ø&8Ø"0Ø':Ø&3ð	 8ó ÷ ñ ñ
ð !ð/øðøð"ús>   ‚C'F4Ã)F.Ã*AF4Ä0F0Ä1AF4Å6/F4Æ%F2Æ&	F4Æ0F4Æ2F4rª   c              ƒ   ó@  K  — |j                   €y t        j                  t        j                  «      j                  «       }t        |j                   «      }| j                  |||¬«      ƒ d {  –—† }|€| j                  |||||¬«      ƒ d {  –—† }nk||z
  |kD  r4t        j                  d«       | j                  |||||¬«      ƒ d {  –—† }n/|||z
  z
  }t        |«      }	| j                  |||	¬«      ƒ d {  –—†  t        j                  d||«       y 7 Œ¨7 ŒŠ7 ŒP7 Œ"­w)N)r«   r¬   r­   )rµ   r«   r¬   r¶   r­   z,Budget window expired - resetting everything)rµ   r¶   r°   zIncremented spend for %s by %s)r•   r   Únowr   ÚutcÚ	timestampr   r´   r¸   r   r—   rM   r¾   )
r!   rª   rµ   r«   r¶   r¬   r­   r³   Úremaining_timeÚttl_for_increments
             r"   rÆ   z-RouterBudgetLimiting._increment_spend_for_keyÇ  sO  è ø€ ð ×(Ñ(Ð0Øä&Ÿl™l¬8¯<©<Ó8×BÑBÓDˆÜ0°×1NÑ1NÓOˆà!×?Ñ?Ø)Ø%Ø#ð @ó 
÷ 
ˆð Ðà!%×!?Ñ!?Ø#Ø-Ø)Ø+Ø'ð "@ó "÷ ‰Lð ˜\Ñ)¨[Ò8ä!×'Ñ'Ð(VÔWØ!%×!?Ñ!?Ø#Ø-Ø)Ø+Ø'ð "@ó "÷ ‰Lð %0°<À,Ñ3NÑ$OˆNÜ'*¨>Ó':Ðà×9Ñ9Ø#°=ÐFWð :ó ÷ ð ô 	×#Ñ#Ð$DÀiÐQ^Õ_ðE
øðøðøðúsH   ‚A+DÁ-DÁ.DÂDÂ;DÃ	DÃ
/DÃ9DÃ:DÄDÄDÄDc              ƒ   ó&  K  — 	 	 | j                  «       ƒ d{  –—†  t        j                  t        «      ƒ d{  –—†  Œ;7 Œ&7 Œ# t        $ rB}t        j                  d|«       t        j                  t        «      ƒ d{  –—†7   Y d}~ŒKd}~ww xY w­w)z¼
        Handler that triggers sync_in_memory_spend_with_redis every DEFAULT_REDIS_SYNC_INTERVAL seconds

        Required for multi-instance environment usage of provider budgets
        NzError in periodic sync task: %s)Ú _sync_in_memory_spend_with_redisrX   Úsleepr   Ú	Exceptionr   Úerror©r!   Úes     r"   rZ   z=RouterBudgetLimiting.periodic_sync_in_memory_spend_with_redisø  s„   è ø€ ð ð	Ø×;Ñ;Ó=×=Ð=Ü—m‘mÜ/ó÷ ð ð à=øðùô ò Ü%×+Ñ+Ð,MÈqÔQÜ—m‘mÜ/ó÷ ö ûðüsW   ‚B…A ˜?™ A ¹AºA ¾B¿A ÁA Á	BÁ2B	Á>BÁ?B	ÂBÂ	BÂBc              ƒ   ó˜  K  — 	 | j                   j                  syt        j                  d| j                  «       t        | j                  «      dkD  rCt        j                  | j                   j                  j                  | j                  ¬«      «       g | _        y# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w­w)a  
        How this works:
        - async_log_success_event collects all provider spend increments in `redis_increment_operation_queue`
        - This function pushes all increments to Redis in a batched pipeline to optimize performance

        Only runs if Redis is initialized
        Nz.Pushing Redis Increment Pipeline for queue: %sr   )Úincrement_listú,Error syncing in-memory cache with Redis: %s)rR   Úredis_cacher   r—   rW   r?   rX   rY   Úasync_increment_pipelinerÚ   rÛ   rÜ   s     r"   Ú#_push_in_memory_increments_to_redisz8RouterBudgetLimiting._push_in_memory_increments_to_redis
  s°   è ø€ ð	[Ø—?‘?×.Ò.Øä!×'Ñ'Ø@Ø×4Ñ4ôô �4×7Ñ7Ó8¸1Ò<Ü×#Ñ#Ø—O‘O×/Ñ/×HÑHØ'+×'KÑ'Kð Ió ôð 46ˆDÕ0øäò 	[Ü!×'Ñ'Ð(VÐXY×ZÑZûð	[üs9   ‚C
„B šC
›BB ÂC
Â	CÂ'CÂ=C
ÃCÃC
c              ƒ   ó  K  — 	 | j                   j                  €y| j                  «       ƒ d{  –—†  g }| j                  �F| j                  j	                  «       D ])  \  }}|€Œ	|j                  d|› d|j                  › �«       Œ+ | j                  �F| j                  j	                  «       D ])  \  }}|€Œ	|j                  d|› d|j                  › �«       Œ+ | j                  �F| j                  j	                  «       D ])  \  }}|€Œ	|j                  d|› d|j                  › �«       Œ+ | j                   j                  j                  |¬«      ƒ d{  –—† }t        |t        «      rk|j	                  «       D ]W  \  }}|€Œ	| j                   j                  j                  |t        |«      ¬«      ƒ d{  –—†  t        j                   d||«       ŒY yy7 �Œ§7 Œƒ7 Œ$# t"        $ r }	t        j$                  d	|	«       Y d}	~	yd}	~	ww xY w­w)
a  
        Ensures in-memory cache is updated with latest Redis values for all provider spends.

        Why Do we need this?
        - Optimization to hit sub 100ms latency. Performance was impacted when redis was used for read/write per request
        - Use provider budgets in multi-instance environment, we use Redis to sync spend across all instances

        What this does:
        1. Push all provider spend increments to Redis
        2. Fetch all current provider spend from Redis to update in-memory cache
        Nr†   r‡   r‘   r’   )Úkey_list)r%   r}   z"Updated in-memory cache for %s: %srà   )rR   rá   rã   rS   r9   r™   r•   r[   r\   rw   r`   rC   r»   r²   ry   r   r—   rÚ   rÛ   )
r!   r~   r‰   rœ   r¢   ÚtagÚredis_valuesr%   r}   rÝ   s
             r"   rØ   z5RouterBudgetLimiting._sync_in_memory_spend_with_redis&  s"  è ø€ ð(	[à�‰×*Ñ*Ð2Øð ×:Ñ:Ó<×<Ð<ð !#ˆJà×*Ñ*Ð6Ø(,×(CÑ(C×(IÑ(IÓ(Kò ]Ñ$�H˜fØ�~Ø Ø×%Ñ%¨¸°zÀÀ6×CYÑCYÐBZÐ&[Õ\ð]ð
 ×,Ñ,Ð8Ø(,×(EÑ(E×(KÑ(KÓ(Mò _Ñ$�H˜fØ�~Ø Ø×%Ñ%Ð(9¸(¸À1ÀV×E[ÑE[ÐD\Ð&]Õ^ð_ð
 ×%Ñ%Ð1Ø#'×#9Ñ#9×#?Ñ#?Ó#Aò S‘K�C˜Ø�~Ø Ø×%Ñ%¨
°3°%°q¸×9OÑ9OÐ8PÐ&QÕRðSð )-¯©×(CÑ(C×(YÑ(YÐcmÐ(YÓ(n×"nˆLô ˜,¬Ô-Ø".×"4Ñ"4Ó"6ò f‘J�C˜ØÑ(Ø"Ÿo™o×=Ñ=×MÑMÐRUÔ]bÐchÓ]iÐMÓj×jÐjÜ-×3Ñ3Ð4XÐZ]Ð_dÕeñfð .ð7 =ùð0 #oøð kùô ò 	[Ü!×'Ñ'Ð(VÐXY×ZÑZûð	[üss   ‚H„G šH›G ®G¯D%G ÅGÅ,G Æ3G Æ5GÆ6G ÇHÇG ÇG ÇG Ç	HÇ%H Ç;HÈ HÈHr¢   c                 óT   — | j                   €y | j                   j                  |d «      S r    )r[   r)   ©r!   r¢   s     r"   r¨   z6RouterBudgetLimiting._get_budget_config_for_deployment]  s,   € ð ×(Ñ(Ð0ØØ×,Ñ,×0Ñ0°¸4Ó@Ð@r$   r‰   c                 óT   — | j                   €y | j                   j                  |d «      S r    )rS   r)   )r!   r‰   s     r"   r§   z4RouterBudgetLimiting._get_budget_config_for_providere  s*   € Ø×&Ñ&Ð.ØØ×*Ñ*×.Ñ.¨x¸Ó>Ð>r$   ræ   c                 óT   — | j                   €y | j                   j                  |d «      S r    )r\   r)   )r!   ræ   s     r"   r˜   z/RouterBudgetLimiting._get_budget_config_for_tagj  s*   € Ø×!Ñ!Ð)ØØ×%Ñ%×)Ñ)¨#¨tÓ4Ð4r$   rš   c                 óˆ  — 	 |j                  d«      xs i }t        |t        «      r|j                  xs d}|}n>t        |t        «      r!|j                  d«      xs d}t        |«      }nd}t        i «      }t        j                  t        |«      |¬«      \  }}}}|S # t        $ r t        j                  d|«       Y y w xY w)NrŽ   r…   rf   )rf   rŽ   z-Error getting LLM provider for deployment: %s)r)   r`   r   rf   rC   r   ra   Úget_llm_providerrK   rÚ   r   rÛ   )r!   rš   Údeployment_litellm_paramsrf   Úprovider_resolution_paramsÚ_rÄ   s          r"   r“   z5RouterBudgetLimiting._get_llm_provider_for_deploymento  sÌ   € ð	Ø/9¯~©~Ð>NÓ/OÒ/UÐSUÐ%äÐ3´^ÔDØ1×7Ñ7Ò=¸2�Ø2KÑ*ÜÐ5´tÔ<Ø1×5Ñ5°gÓ>ÒDÀ"�Ü-CÐD]Ó-^Ñ*à�Ü-CÀBÓ-GÐ*ä+2×+CÑ+CÜ˜%“jØ9ô,Ñ(ˆAÐ" A qð #Ð"øô ò 	Ü!×'Ñ'Ð(WÐYcÔdÙð	ús   ‚BB ÂCÃ CrŠ   r‹   c                 óF   — t        «       }|r|j                  |||¬«       yy)z¥
        Optional helper - emit provider remaining budget metric to Prometheus

        This is helpful for debugging and monitoring provider budget limits.
        rˆ   N)r   Útrack_provider_remaining_budget)r!   r‰   rŠ   r‹   Úprometheus_loggers        r"   r–   z@RouterBudgetLimiting._track_provider_remaining_budget_prometheus†  s2   € ô $IÓ#JÐÙØ×=Ñ=Ø!ØØ)ð >õ ð r$   c              ƒ   óJ  K  — | j                  |«      }|€yd|› d|j                  › �}| j                  j                  r.| j                  j                  j	                  |«      ƒ d{  –—† }n#| j                  j	                  |«      ƒ d{  –—† }|�t        |«      S dS 7 Œ77 Œ­w)ab  
        GET the current spend for a provider from cache

        used for GET /provider/budgets endpoint in spend_management_endpoints.py

        Args:
            provider (str): The provider to get spend for (e.g., "openai", "anthropic")

        Returns:
            Optional[float]: The current spend for the provider, or None if not found
        Nr†   r‡   rl   )r§   r•   rR   rá   r±   ry   )r!   r‰   rª   rµ   r�   s        r"   Ú_get_current_provider_spendz0RouterBudgetLimiting._get_current_provider_spend•  sŸ   è ø€ ð  $×CÑCÀHÓMˆØÐ Øà,¨X¨J°a¸×8UÑ8UÐ7VÐWˆ	à�?‰?×&Ò&à"&§/¡/×"=Ñ"=×"MÑ"MÈiÓ"X×X‰Mð #'§/¡/×"AÑ"AÀ)Ó"L×LˆMØ'4Ð'@Œu�]Ó#ÐIÀcÐIð	 Yøð Mús$   ‚A%B#Á'BÁ(#B#ÂB!ÂB#Â!B#c              ƒ   ó®  K  — | j                  |«      }|€y d|› d|j                  › �}| j                  j                  r.| j                  j                  j	                  |«      ƒ d {  –—† }n#| j                  j	                  |«      ƒ d {  –—† }|€y t        j                  t        j                  «      t        |¬«      z   j                  «       S 7 Œi7 ŒG­w)Nr†   r‡   )Úseconds)r§   r•   rR   rá   Úasync_get_ttlr   rÒ   r   rÓ   r   Ú	isoformat)r!   r‰   rª   rµ   r­   s        r"   Ú%_get_current_provider_budget_reset_atz:RouterBudgetLimiting._get_current_provider_budget_reset_at¯  s²   è ø€ Ø#×CÑCÀHÓMˆØÐ Øà,¨X¨J°a¸×8UÑ8UÐ7VÐWˆ	Ø�?‰?×&Ò&Ø $§¡× ;Ñ ;× IÑ IÈ)Ó T×T‰Kà $§¡× =Ñ =¸iÓ H×HˆKàÐØä—‘œXŸ\™\Ó*¬Y¸{Ô-KÑK×VÑVÓXÐXð UøàHús%   ‚A%CÁ'CÁ(#CÂCÂACÃCc              ƒ   ó   K  — d|› d|j                   › �}d|› �}d}|j                   �t        |j                   «      }| j                  j                  |«      ƒ d{  –—† }|€Wt	        j
                  t        j                  «      j                  «       }| j                  j                  |||¬«      ƒ d{  –—†  | j                  j                  |«      ƒ d{  –—† }|€'| j                  j                  |d|¬«      ƒ d{  –—†  yy7 Œª7 ŒS7 Œ27 Œ­w)a!  
        Initialize provider budget in cache by storing the following keys if they don't exist:
        - provider_spend:{provider}:{budget_config.time_period} - stores the current spend
        - provider_budget_start_time:{provider} - stores the start time of the budget window

        r†   r‡   rÅ   Nr¯   rl   )
r•   r   rR   r±   r   rÒ   r   rÓ   rÔ   r²   )r!   r‰   rª   rµ   r«   r­   r³   Ú
_spend_keys           r"   Ú_init_provider_budget_in_cachez3RouterBudgetLimiting._init_provider_budget_in_cache¿  sý   è ø€ ð -¨X¨J°a¸×8UÑ8UÐ7VÐWˆ	Ø"=¸h¸ZÐ HˆØ"&ˆØ×(Ñ(Ð4Ü-¨m×.KÑ.KÓLˆKà!Ÿ_™_×<Ñ<¸^ÓL×LˆØÐÜ#Ÿ<™<¬¯©Ó5×?Ñ?ÓAˆLØ—/‘/×1Ñ1°nÈLÐ^iÐ1Ój×jÐjà"&§/¡/×"AÑ"AÀ)Ó"L×Lˆ
ØÐØ—/‘/×1Ñ1°iÀsÐP[Ð1Ó\×\Ñ\ð ð Møð køàLøà\úsI   ‚ADÁDÁADÂ4DÂ5"DÃD
Ã'DÃ?DÄ DÄDÄ
DÄDc                 ó¬   — | �yt         j                  �y|€y|D ]8  }|j                  di «      }|j                  d«      s|j                  d«      €Œ8 y y)a  
        Returns `True` if the router budget routing settings are set and RouterBudgetLimiting should be initialized

        Either:
         - provider_budget_config is set
         - budgets are set for deployments in the model_list
         - tag_budget_config is set
        TFrŽ   r”   r•   )ra   r\   r)   )rS   rT   Ú_modelr    s       r"   Ú!should_init_router_budget_limiterz6RouterBudgetLimiting.should_init_router_budget_limiterÖ  sl   € ð "Ð-Øä×$Ñ$Ð0ØàÐØà ò 	ˆFØ$Ÿj™jÐ)9¸2Ó>ˆOØ×"Ñ" <Ô0°O×4GÑ4GÐHYÓ4ZÑ4fÙð	ð r$   c                 óÒ  — | j                   �Û| j                   j                  «       D ]�  \  }}|€t        d|› d| j                   › �«      ‚t        |t        «      s8t	        |j                  d«      |j                  d«      ¬«      | j                   |<   t        j                  | j                  || j                   |   ¬«      «       ŒŸ t        j                  d| j                   «       y y )Nz$No budget config found for provider z, provider_budget_config: r‹   Útime_period)r‹   r  )r‰   rª   z%Initalized Provider budget config: %s)rS   r9   r{   r`   ÚGenericBudgetInfor)   rX   rY   rý   r   r—   )r!   r‰   rœ   s      r"   r]   z+RouterBudgetLimiting._init_provider_budgetsò  sì   € Ø×&Ñ&Ð2à$(×$?Ñ$?×$EÑ$EÓ$Gò Ñ �˜&Ø�>Ü$Ø>¸x¸jÐHbÐcg×c~Ñc~Ðbð  Aóð ô " &Ô*;Ô<Ü<MØ%+§Z¡Z°Ó%?Ø$*§J¡J¨}Ó$=ô=�D×/Ñ/°Ñ9ô ×#Ñ#Ø×7Ñ7Ø!)Ø&*×&AÑ&AÀ(Ñ&Kð 8ó õðô$ "×'Ñ'Ð(OÐQU×QlÑQlÕmð) 3r$   c                 ó¬  — |€y |D ]¬  }|j                  di «      }|j                  d«      xs i }|j                  d«      }|j                  d«      }|j                  d«      }t        j                  d|||«       |€Œx|€Œ{|€Œ~t        ||¬«      }| j                  €i | _        || j                  |<   Œ® t        j                  d| j                  «       y )	NrŽ   r�   r�   r”   r•   zIInit Deployment Budget: max_budget: %s, budget_duration: %s, model_id: %s©r  r‹   z(Initialized Deployment Budget Config: %s)r)   r   r—   r  r[   )	r!   rT   rÿ   r    Ú_model_infoÚ	_model_idÚ_max_budgetÚ_budget_durationÚ_budget_configs	            r"   r^   z-RouterBudgetLimiting._init_deployment_budgets	  sï   € ð ÐØØ ò 	JˆFØ$Ÿj™jÐ)9¸2Ó>ˆOØ &§
¡
¨<Ó 8Ò >¸BˆKØ#Ÿ™¨Ó-ˆIØ)×-Ñ-¨lÓ;ˆKØ.×2Ñ2Ð3DÓEÐä!×'Ñ'Ø[ØØ Øô	ð Ñ&Ð+;Ñ+GÈIÑLaÜ!2Ø 0Ø!,ô"�ð ×0Ñ0Ð8Ø46�DÔ1Ø;I�×-Ñ-¨iÒ8ð)	Jô, 	×#Ñ#Ð$NÐPT×PmÑPmÕnr$   c                 ó*   — | j                  |g¬«       y)zd
        Register or refresh deployment-level budget config for a runtime-added deployment.
        rV   N)r^   )r!   rš   s     r"   Úregister_deployment_budgetz/RouterBudgetLimiting.register_deployment_budget'  s   € ð 	×%Ñ%°*°Ð%Õ>r$   c                 ó–   — | j                   €y | j                   j                  |d «       t        | j                   «      dk(  rd | _         y y )Nr   )r[   Úpopr?   ré   s     r"   Úunregister_deployment_budgetz1RouterBudgetLimiting.unregister_deployment_budget0  sG   € Ø×(Ñ(Ð0ØØ×%Ñ%×)Ñ)¨(°DÔ9Üˆt×,Ñ,Ó-°Ò2Ø,0ˆDÕ)ð 3r$   c                 ó¶  — t         j                  €y ddlm}m} |durt        d|j                  › �«      ‚| j                  €i | _        t         j                  j                  «       D ]P  \  }}t        |t        «      rt        di |¤Ž}t        |j                  |j                  ¬«      }|| j                  |<   ŒR t        j                  d| j                  «       y )Nr   )ÚCommonProxyErrorsÚpremium_userTz,Tag budgets are an Enterprise only feature, r  z!Initialized Tag Budget Config: %srO   )ra   r\   Úlitellm.proxy.proxy_serverr  r  r{   Únot_premium_userr9   r`   rC   r   r  r•   r”   r   r—   )r!   r  r  r£   r¤   Ú_generic_budget_configs         r"   r_   z&RouterBudgetLimiting._init_tag_budgets7  sÔ   € Ü×$Ñ$Ð,ØßNà˜tÑ#ÜÐKÐL]×LnÑLnÐKoÐpÓqÐqà×!Ñ!Ð)Ø%'ˆDÔ"ä(/×(AÑ(A×(GÑ(GÓ(Iò 	BÑ$ˆDÐ$ÜÐ,¬dÔ3Ü%1Ñ%GÐ4FÑ%GÐ"Ü%6Ø.×>Ñ>Ø/×:Ñ:ô&Ð"ð ,BˆD×"Ñ" 4Ò(ð	Bô 	×#Ñ#Ð$GÈ×I_ÑI_Õ`r$   r    )NN)*rF   rG   rH   r	   rC   rc   r   rK   r   r#   r   r   rƒ   r  ry   Útuplerz   rv   rM   r´   r¸   r¾   rÐ   rÆ   rZ   rã   rØ   r¨   r§   r˜   r“   r–   rõ   rú   rý   Ústaticmethodr   r]   r^   r  r  r_   rO   r$   r"   rQ   rQ   ^   s?  „ ð
 IMñ	HàðHð !% t¡ðHð Ð,¨t°C¸°H©~Ñ=Ñ>ÀÑEó	Hð2 '+Ø(,ñJ'àðJ'ð "ðJ'ð Ð'Ñ(¨4Ñ/ð	J'ð
 ˜t™ðJ'ð  ™+ðJ'ð 
ˆd‰óJ'ðXRCà# D¨¨c¨¡NÑ3ðRCð " $ s¨C x¡.Ñ1ðRCð ˜sÐ$5Ð5Ñ6ð	RCð
 ! Ð&7Ð!7Ñ8ðRCð # 3¨¡:Ñ.ðRCð ˜˜U˜
Ñ#ðRCð ˜3‘iðRCð 
ˆt�D˜˜c˜‘NÑ# SÐ(Ñ	)óRCðn '+ñ?
à! $ s¨C x¡.Ñ1ð?
ð ˜t™ð?
ð 
ØˆS‰	ØˆSÐ#Ð#Ñ$ØˆSÐ#Ð#Ñ$ØˆS�4‰ZÑð	ñ
ó	?
ðB#À#ð #ÐUZð #Ðilð #Ðqvó #ðàðð ðð ð	ð
 ðð ðð 
óð0BÀ#ð BÐV[ð BÐbeó Bò*7ðr/`à(ð/`ð ð/`ð ð	/`ð
 ó/`òbò$[ò85[ðnAàðAð 
˜TÑ	!óAð?¸ð ?Ð@QÐTXÑ@Xó ?ð
5¨cð 5Ð6GÈ$Ñ6Nó 5ð
#¸4ð #ÀCÈ$ÁJó #ð.ÀCð ÐPUð Ðejó ðJ¸#ð JÀ%È$Á,ó Jð4YÀCð YÈCÐRVÉJó Yð ]¸Sð ]ÐQbó ]ð. ð OSñØ $ t¡ðàÐ,Ñ-°°T¸#¸s¸(±^Ñ0DÑDÀtÑKòó ðò6nð2 IMñoàÐ,¨t°C¸°H©~Ñ=Ñ>ÀÑEóoð<?à˜˜c˜‘NÐ%8Ñ8ð?ð 
ó?ð1°Sð 1¸Tó 1óar$   rQ   )*rI   rX   rN   r   r   r   Útypingr   r   ra   Úlitellm._loggingr   Úlitellm.caching.cachingr	   Úlitellm.caching.redis_cacher
   Ú"litellm.integrations.custom_loggerr   r   Ú'litellm.litellm_core_utils.core_helpersr   Ú*litellm.litellm_core_utils.duration_parserr   Ú)litellm.router_strategy.tag_based_routingr   Ú'litellm.router_utils.cooldown_callbacksr   Úlitellm.types.llms.openair   Úlitellm.types.routerr   r   r   Úlitellm.types.utilsr   r   r   r  r   Ú__annotations__r   rQ   rO   r$   r"   ú<module>r%     sw   ðòó( Û ß 2Ñ 2ß ã Ý 2Ý -Ý Gß Aõõ KÝ Sõõ 7ß RÑ Rß ]Ñ ]Ý Aà%&Ð ˜UÓ &÷,"ñ ,"ô^ma˜<õ mar$   