Ë
    uPêjm&  ã                   ón   — d 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	 ddl
mZ ddlmZ  G d	„ d
e«      Zy)zb
Base class across routing strategies to abstract commmon functions like batch incrementing redis
é    N)ÚABC)ÚFinal)Úverbose_router_logger)Ú	DualCache)ÚRedisPipelineIncrementOperation)ÚDEFAULT_REDIS_SYNC_INTERVALc                   óâ   — e Zd Zdedededz  fd„Zdedz  fd„Zd„ Zde	e
eef      d	ed
e	e   fd„Zdeded	efd„Zdedz  fd„Zd„ Zdefd„Zd
edz  fd„Zd
ee   fd„Zd
ee   fd„Zd„ Zd„ Zy)ÚBaseRoutingStrategyÚ
dual_cacheÚshould_batch_redis_writesÚdefault_sync_intervalNc                 ór   — || _         g | _        d | _        |r| j                  |«       t	        «       | _        y ©N)r   Úredis_increment_operation_queueÚ
_sync_taskÚsetup_sync_taskÚsetÚin_memory_keys_to_update)Úselfr   r   r   s       úr/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/router_strategy/base_routing_strategy.pyÚ__init__zBaseRoutingStrategy.__init__   s6   € ð %ˆŒØVXˆÔ,Ø59ˆŒÙ$Ø× Ñ Ð!6Ô7ä25³%ˆÕ%ó    c                 óê   — 	 t        j                  «       }|j                  | j                  |¬«      «      | _        y# t        $ r, t        j                  «       }t        j                  |«       Y Œ[w xY w)z;Setup the sync task in a way that's compatible with FastAPI)r   N)ÚasyncioÚget_running_loopÚRuntimeErrorÚnew_event_loopÚset_event_loopÚcreate_taskÚ(periodic_sync_in_memory_spend_with_redisr   )r   r   Úloops      r   r   z#BaseRoutingStrategy.setup_sync_task   sh   € ð	)Ü×+Ñ+Ó-ˆDð
 ×*Ñ*Ø×9Ñ9ÐPeÐ9Ófó
ˆ�øô	 ò 	)Ü×)Ñ)Ó+ˆDÜ×"Ñ" 4Ö(ð	)ús   ‚= ½2A2Á1A2c              ƒ   óº   K  — | j                   �0| j                   j                  «        	 | j                   ƒ d{  –—†  yy7 Œ# t        j                  $ r Y yw xY w­w)z.Cleanup method to be called when shutting downN)r   Úcancelr   ÚCancelledError©r   s    r   ÚcleanupzBaseRoutingStrategy.cleanup*   sS   è ø€ à�?‰?Ð&Ø�O‰O×"Ñ"Ô$ðØ—o‘o×%Ñ%ð 'ð &ùÜ×)Ñ)ò Ùðüs7   ‚'AªA ¹A ºA ¾AÁ A ÁAÁAÁAÁAÚincrement_listÚttlÚreturnc              ƒ   ó„   K  — g }|D ]2  \  }}| j                  |||¬«      ƒ d{  –—† }|j                  |«       Œ4 |S 7 Œ­w)zB
        Increment a list of values in the current window
        ©ÚkeyÚvaluer(   N)Ú"_increment_value_in_current_windowÚappend)r   r'   r(   Úresultsr,   r-   Úresults          r   Ú'_increment_value_list_in_current_windowz;BaseRoutingStrategy._increment_value_list_in_current_window3   sU   è ø€ ð ˆØ(ò 	#‰JˆC�Ø×BÑBÀsÐRWÐ]`ÐBÓa×aˆFØ�N‰N˜6Õ"ð	#ð ˆð bús   ‚"A ¤>¥A r,   r-   c              ƒ   óè   K  — | j                   j                  j                  |||¬«      ƒ d{  –—† }t        |||¬«      }| j                  j                  |«       | j                  |¬«       |S 7 ŒA­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(   ©r,   )r   Úin_memory_cacheÚasync_incrementr   r   r/   Úadd_to_in_memory_keys_to_update)r   r,   r-   r(   r1   Úincrement_ops         r   r.   z6BaseRoutingStrategy._increment_value_in_current_window?   s|   è ø€ ð #Ÿo™o×=Ñ=×MÑMØØØð Nó 
÷ 
ˆô
 >ØØ!Øô
ˆð 	×,Ñ,×3Ñ3°LÔAØ×,Ñ,°Ð,Ô5Øˆð
ús   ‚,A2®A0¯AA2c              ƒ   ó*  K  — |xs t         }	 	 | j                  «       ƒ d{  –—†  t        j                  |«      ƒ d{  –—†  Œ77 Œ"7 Œ# t        $ r>}t        j                  d|«       t        j                  |«      ƒ d{  –—†7   Y d}~ŒGd}~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)r   Ú _sync_in_memory_spend_with_redisr   ÚsleepÚ	Exceptionr   Úerror)r   r   Úes      r   r    z<BaseRoutingStrategy.periodic_sync_in_memory_spend_with_redisW   s�   è ø€ ð !6Ò TÔ9TÐØð	Ø×;Ñ;Ó=×=Ð=Ü—m‘mØ)ó÷ ð ð à=øðùô ò Ü%×+Ñ+Ð,MÈqÔQÜ—m‘mØ)ó÷ ö ûðüs[   ‚B�A	 ¢A£A	 ¿AÁ A	 ÁBÁA	 ÁA	 Á		BÁ.BÂ BÂBÂBÂBÂBc              ƒ   óä  K  — 	 | j                   j                  syt        | j                  «      dkD  rûi }g }t	        | j                  «      D ]<  \  }}|d   |v r||d      dxx   |d   z  cc<   n|||d   <   |j                  |«       Œ> t        |j                  «       «      }| j                   j                  j                  |¬«      ƒ d{  –—† }t	        | j                  «      D ��cg c]  \  }}||vsŒ|‘Œ c}}| _        |�$t        ||«      D ��ci c]  \  }}|d   |“Œ }}}|S i }|S y7 Œac c}}w c c}}w # t        $ r'}	t        j                  d|	«       g | _        Y d}	~	yd}	~	ww xY w­w)av  
        How this works:
        - async_log_success_event collects all provider spend increments in `redis_increment_operation_queue`
        - This function compresses multiple increments for the same key into a single operation
        - Then pushes all increments to Redis in a batched pipeline to optimize performance

        Only runs if Redis is initialized
        Nr   r,   r4   )r'   ú,Error syncing in-memory cache with Redis: %s)r   Úredis_cacheÚlenr   Ú	enumerater/   ÚlistÚvaluesÚasync_increment_pipelineÚzipr=   r   r>   )
r   Úcompressed_opsÚops_to_removeÚidxÚopÚcompressed_queueÚincrement_resultr,   Úreturn_resultr?   s
             r   Ú#_push_in_memory_increments_to_redisz7BaseRoutingStrategy._push_in_memory_increments_to_redisj   s—  è ø€ ð$	6Ø—?‘?×.Ò.Øä�4×7Ñ7Ó8¸1Ò<àTV�Ø')�Ü(¨×)MÑ)MÓNò .‘G�C˜Ø˜%‘y NÑ2à& r¨%¡yÑ1Ð2CÓDÈÐK\ÑH]Ñ]ÔDà46˜ r¨%¡yÑ1à!×(Ñ(¨Õ-ð.ô +/¨~×/DÑ/DÓ/FÓ*GÐ à04·±×0KÑ0K×0dÑ0dØ#3ð 1eó 1÷ +Ð ô
 '0°×0TÑ0TÓ&U÷8Ù"˜3 ÐY\ÐdqÒYq’Bó8�Ô4ð $Ð/ÜCFÐGWÐYiÓCj×$k¹¸¸R S¨¡Z°¡^Ð$k�MÑ$kð %Ð$ð %'�MØ$Ð$ð9 =ð +úó8ùó
 %løô
 ò 	6Ü!×'Ñ'Ð(VÐXYÔZØ35ˆD×0Ñ0ûð	6üs{   ‚E0„D= šE0›B2D= ÃD/ÃD= Ã*D1Ã7D1Ã;D= ÄD7Ä%D= Ä)E0Ä*D= Ä-E0Ä/D= Ä1D= Ä=	E-ÅE(Å#E0Å(E-Å-E0c                 ó:   — | j                   j                  |«       y r   )r   Úadd)r   r,   s     r   r8   z3BaseRoutingStrategy.add_to_in_memory_keys_to_update™   s   € Ø×%Ñ%×)Ñ)¨#Õ.r   c                  ó   — y)z-
        Get the key pattern to sync
        N© r%   s    r   Úget_key_pattern_to_syncz+BaseRoutingStrategy.get_key_pattern_to_syncœ   s   € ð r   c                 ó   — | j                   S r   )r   r%   s    r   Úget_in_memory_keys_to_updatez0BaseRoutingStrategy.get_in_memory_keys_to_update¢   s   € Ø×,Ñ,Ð,r   c                 ó<   — | j                   }t        «       | _         |S )z-Atomic get and reset in-memory keys to update)r   r   )r   Úkeyss     r   Ú&get_and_reset_in_memory_keys_to_updatez:BaseRoutingStrategy.get_and_reset_in_memory_keys_to_update¥   s   € à×3Ñ3ˆÜ(+«ˆÔ%Øˆr   c                 ó"   — t        «       | _        y r   )r   r   r%   s    r   Úreset_in_memory_keys_to_updatez2BaseRoutingStrategy.reset_in_memory_keys_to_update«   s   € Ü(+«ˆÕ%r   c              ƒ   ó>  K  — 	 | j                   j                  €y| j                  «       }t        |«      }i }| j                   j                  j                  |¬«      ƒ d{  –—† }t        ||«      D ]  \  }}t        |xs d«      ||<   Œ | j                  «       ƒ d{  –—† }|€y|D ]»  }t        |j                  |d«      xs d«      }	t        |j                  |d«      xs d«      }
t        | j                   j                  j                  |¬«      ƒ d{  –—† xs d«      }||
z
  }||	k  r|	|z   }nŒ�| j                   j                  j                  ||¬«      ƒ d{  –—†  Œ½ y7 �Œ7 ŒË7 ŒT7 Œ# 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
        N)rY   r   r5   )r,   r-   rA   )r   rB   rW   rE   r6   Úasync_batch_get_cacherH   ÚfloatrP   ÚgetÚasync_get_cacheÚasync_set_cacher=   r   Ú	exception)r   Ú
cache_keysÚcache_keys_listÚin_memory_before_dictÚin_memory_beforeÚkÚvÚredis_valuesr,   Ú	redis_valÚbeforeÚafterÚdeltaÚmergedr?   s                  r   r;   z4BaseRoutingStrategy._sync_in_memory_spend_with_redis®   s¯  è ø€ ð+	_à�‰×*Ñ*Ð2Øð ×1Ñ1Ó3ð ô &*¨*Ó%5ˆOð ,.Ð!Ø,0¯O©O×,KÑ,K×,aÑ,aÐgvÐ,aÓ,w×&wÐÜ˜OÐ-=Ó>ò 9‘��1Ü+0°²°a«=Ð% aÒ(ð9ð )-×(PÑ(PÓ(R×"RˆLØÐ#Øð 'ò ]�Ü! ,×"2Ñ"2°3¸Ó":Ò"?¸aÓ@�	ÜÐ4×8Ñ8¸¸aÓ@ÒEÀAÓF�Ü D§O¡O×$CÑ$C×$SÑ$SÐX[Ð$SÓ$\×\ÒaÐ`aÓb�Ø ™�Ø˜IÒ%Ø&¨Ñ.‘Fàð —o‘o×5Ñ5×EÑEÈ#ÐU[ÐEÓ\×\Ñ\ñ!]ð 'xùð
 #Søð ]øð ]ùäò 	_Ü!×+Ñ+Ð,ZÐ\]×^Ñ^ûð	_üs�   ‚F„E1 šF›AE1 Á!E(Á"=E1 ÂE+Â E1 Â&FÂ'A1E1 ÄE-
ÄAE1 Å E/Å!E1 Å'FÅ(E1 Å+E1 Å-E1 Å/E1 Å1	FÅ:FÆFÆFÆF)Ú__name__Ú
__module__Ú__qualname__r   Úboolr_   r   r   r&   rE   ÚtupleÚstrÚintr2   r.   r    rP   r8   rU   r   rW   rZ   r\   r;   rT   r   r   r
   r
      så   „ ð8àð8ð $(ð8ð  % t™|ó	8ð

°U¸T±\ó 

òð
Ø" 5¨¨c¨¡?Ñ3ð
Ø:=ð
à	ˆe‰ó
ð¸Cð Èð ÐTWó ð0ÐTYÐ\`ÑT`ó ò&-6ð^/°3ó /ð¨¨t©ó ð-¨c°#©hó -ð¸¸C¹ó ò.ó8_r   r
   )Ú__doc__r   Úabcr   Útypingr   Úlitellm._loggingr   Úlitellm.caching.cachingr   Úlitellm.caching.redis_cacher   Úlitellm.constantsr   r
   rT   r   r   ú<module>r~      s1   ðñó Ý Ý å 2Ý -Ý GÝ 9ôW_˜#õ W_r   