Ë
    tPêjƒ‡  ã                   ó"  — d Z ddl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
 ddl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mZ ddlmZmZmZmZmZmZ ddl m!Z!m"Z" ddl#m$Z$ ddl%m&Z& ddl'm(Z( erddl'm)Z) dd„Z* G d„ de«      Z+y)zI
Dynamic rate limiter v3 - Saturation-aware priority-based rate limiting
é    N)ÚCallable)Údatetime)ÚTYPE_CHECKINGÚFinalÚLiteral)ÚHTTPException)ÚModelResponseÚRouter)Úverbose_proxy_logger)Ú	DualCache)ÚCustomLogger)ÚUserAPIKeyAuth)ÚProxyRateLimitErrorÚmap_v3_rate_limit_type)ÚRateLimitDescriptorÚ"RateLimitDescriptorRateLimitObjectÚRateLimitResponseÚ$_PROXY_MaxParallelRequestsHandler_v3Úclaim_request_stash_for_dataÚget_or_create_request_stash)Úconvert_priority_to_percentÚ#resolve_llm_provider_for_rate_limit)ÚInternalUsageCache)ÚModelGroupInfo)ÚCallTypesLiteral©ÚPriorityReservationSettingsc                  óD   — t         j                  } | €ddlm}  |«       S | S )zÙ
    Get the priority reservation settings, guaranteed to be non-None.

    The settings are lazy-loaded in litellm.__init__ and always return an instance.
    This helper provides proper type narrowing for mypy.
    r   r   )ÚlitellmÚpriority_reservation_settingsÚlitellm.types.utilsr   )Úsettingsr   s     úp/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/hooks/dynamic_rate_limiter_v3.pyÚ_get_priority_settingsr$   *   s&   € ô ×;Ñ;€HØÐåCá*Ó,Ð,Ø€Oó    c                   ó²  — e Zd ZdZ	 d"dedeg ef   dz  fd„Zdefd„Z	de
fd	„Zd
ededz  fd„Zd"dedz  dedz  defd„Zdededz  fd„Zdedeeef   fd„Z	 d"dededz  deeef   dedz  deeef   f
d„Zdededefd„Zdedededz  dee   fd„Z	 d#dedede
defd„Zdededededz  deddfd„Zdededededeez  ez  dz  f
d„Zdedefd „Z d!„ Z!y)$Ú _PROXY_DynamicRateLimitHandlerV3aM  
    Saturation-aware priority-based rate limiter using v3 infrastructure.

    Key features:
    1. Model capacity ALWAYS enforced at 100% (prevents over-allocation)
    2. Priority usage tracked from first request (accurate accounting)
    3. Priority limits only enforced when saturated >= threshold
    4. Three-phase checking prevents partial counter increments
    5. Reuses v3 limiter's Redis-based tracking (multi-instance safe)

    How it works:
    - Phase 1: Read-only check of ALL limits (no increments)
    - Phase 2: Decide enforcement based on saturation
    - Phase 3: Increment counters only if request allowed
    - When under-saturated: priorities can borrow unused capacity (generous)
    - When saturated: strict priority-based limits enforced (fair)
    - Uses v3 limiter's atomic Lua scripts for race-free increments
    NÚinternal_usage_cacheÚtime_providerc                 ó^   — t        |¬«      | _        t        | j                  |¬«      | _        y )N)Ú
dual_cache)r)   )r   r(   r   Ú
v3_limiter)Úselfr(   r)   s      r#   Ú__init__z)_PROXY_DynamicRateLimitHandlerV3.__init__N   s'   € ô
 %7ÐBVÔ$WˆÔ!Ü>¸t×?XÑ?XÐhuÔvˆ�r%   Ú
llm_routerc                 ó   — || _         y ©N)r/   )r-   r/   s     r#   Úupdate_variablesz1_PROXY_DynamicRateLimitHandlerV3.update_variablesV   s	   € Ø$ˆ�r%   Úreturnc                 ó*   — t        «       j                  S )zHGet the configurable TTL for local cache when reading saturation values.)r$   Úsaturation_check_cache_ttl)r-   s    r#   Ú_get_saturation_check_cache_ttlz@_PROXY_DynamicRateLimitHandlerV3._get_saturation_check_cache_ttlY   s   € ä%Ó'×BÑBÐBr%   Úcounter_keyc              ƒ   ó|   K  — | j                  «       }| j                  j                  |dd|¬«      ƒ d{  –—† S 7 Œ­w)aŽ  
        Get saturation value with configurable local cache TTL.

        Uses DualCache with configurable TTL for local cache storage.
        TTL is configurable via litellm.priority_reservation_settings.saturation_check_cache_ttl

        Args:
            counter_key: The cache key for the saturation counter

        Returns:
            Counter value as string, or None if not found
        NF)ÚkeyÚlitellm_parent_otel_spanÚ
local_onlyÚttl)r6   r(   Úasync_get_cache)r-   r7   Úlocal_cache_ttls      r#   Ú _get_saturation_value_from_cachezA_PROXY_DynamicRateLimitHandlerV3._get_saturation_value_from_cache]   sJ   è ø€ ð  "&×!EÑ!EÓ!Gˆà×.Ñ.×>Ñ>ØØ%)ØØð	 ?ó 
÷ 
ð 	
ð 
ús   ‚3<µ:¶<ÚpriorityÚ
model_infoc                 ó\  — t        «       j                  }t        j                  �|t        j                  vrt	        j
                  d«       |S |�\t        j                  �Lt        j                  dd«      €t	        j                  d«       |S t        j                  |   }t        ||«      }|S )zEGet the weight for a given priority from litellm.priority_reservationNz4Priority Reservation not set for the given priority.ÚLITELLM_LICENSEz¾PREMIUM FEATURE: Reserving tpm/rpm by priority is a premium feature. Please add a 'LITELLM_LICENSE' to your .env to enable this.
Get a license: https://docs.litellm.ai/docs/proxy/enterprise.)
r$   Údefault_priorityr   Úpriority_reservationr   ÚdebugÚosÚgetenvÚerrorr   )r-   r@   rA   ÚweightÚvalues        r#   Ú_get_priority_weightz5_PROXY_DynamicRateLimitHandlerV3._get_priority_weightv   s¢   € ä.Ó0×AÑAˆÜ×'Ñ'Ð/°8Ä7×C_ÑC_Ñ3_Ü ×&Ñ&Ð']Ô^ð ˆð Ð!¤g×&BÑ&BÐ&NÜ�y‰yÐ*¨DÓ1Ð9Ü$×*Ñ*ð Vôð ˆô  '×;Ñ;¸HÑE�Ü4°U¸JÓG�Øˆr%   Úuser_api_key_dictc                 ó–   — d}|j                   �|j                   j                  dd«      }|€|j                  j                  dd«      }|S )a  
        Get priority from user_api_key_dict.

        Checks team metadata first (takes precedence), then falls back to key metadata.

        Args:
            user_api_key_dict: User authentication info

        Returns:
            Priority string if found, None otherwise
        Nr@   )Úteam_metadataÚgetÚmetadata)r-   rM   r@   s      r#   Ú$_get_priority_from_user_api_key_dictzE_PROXY_DynamicRateLimitHandlerV3._get_priority_from_user_api_key_dict…   sT   € ð  $ˆð ×*Ñ*Ð6Ø(×6Ñ6×:Ñ:¸:ÀtÓLˆHð ÐØ(×1Ñ1×5Ñ5°jÀ$ÓGˆHàˆr%   c                 ó^  — t         j                  €i S i }t         j                  j                  «       D ]  \  }}t        ||«      ||<   Œ t	        |j                  «       «      }|dkD  r?|j                  «       D ��ci c]  \  }}|||z  “Œ }}}t        j                  d||«       |S |S c c}}w )zí
        Normalize priority weights if they sum to > 1.0

        Handles over-allocation: {key_a: 0.60, key_b: 0.80} -> {key_a: 0.43, key_b: 0.57}
        Converts absolute rpm/tpm values to percentages based on model capacity.
        g      ð?z.Normalized over-allocated priorities: %s -> %s)r   rE   Úitemsr   ÚsumÚvaluesr   rF   )r-   rA   ÚweightsÚkÚvÚtotal_weightÚ
normalizeds          r#   Ú_normalize_priority_weightsz<_PROXY_DynamicRateLimitHandlerV3._normalize_priority_weights�   s¸   € ô ×'Ñ'Ð/ØˆIð ,.ˆÜ×0Ñ0×6Ñ6Ó8ò 	D‰DˆAˆqÜ4°Q¸
ÓCˆG�AŠJð	Dô " '§.¡.Ó"2Ó3ˆà˜#ÒØAHÇÁÃ× Q¹¸¸A  A¨Ñ$4Ñ!4Ð QˆJÑ QÜ ×&Ñ&Ð'WÐY`ÐblÔmØÐàˆùó	 !Rs   Á;B)ÚmodelÚnormalized_weightsc                 óö   — |duxr& t         j                  duxr |t         j                  v }|r/|�-|j                  || j                  ||«      «      }|› d|› �}||fS t	        «       j
                  }|› d�}||fS )a!  
        Get priority weight and pool key for a given priority.

        For explicit priorities: returns specific allocation and unique pool key
        For default priority: returns default allocation and shared pool key

        Args:
            model: Model name
            priority: Priority level (None for default)
            normalized_weights: Pre-computed normalized weights
            model_info: Model configuration (optional, for fallback conversion)

        Returns:
            tuple: (priority_weight, priority_key)
        Nú:ú:default_pool)r   rE   rP   rL   r$   rD   )r-   r]   r@   r^   rA   Úhas_explicit_priorityÚpriority_weightÚpriority_keys           r#   Ú_get_priority_allocationz9_PROXY_DynamicRateLimitHandlerV3._get_priority_allocationµ   s¦   € ð0 ˜DÐ ò 9Ü×,Ñ,°DÐ8ò9àœG×8Ñ8Ð8ð 	ñ ! XÐ%9à0×4Ñ4°X¸t×?XÑ?XÐYaÐcmÓ?nÓoˆOà#˜W A h ZÐ0ˆLð  Ð,Ð,ô	 5Ó6×GÑGˆOà#˜W MÐ2ˆLà Ð,Ð,r%   Úmodel_group_infoc              ƒ   óP  K  — 	 d}|j                   �œ|j                   dkD  r�| j                  j                  d|d¬«      }| j                  |¬«      ƒ d{  –—† }|�St	        |«      }||j                   z  }t        ||«      }t        j                  d|› d	|› d
|j                   › d|d›d�	«       |j                  �œ|j                  dkD  r�| j                  j                  d|d¬«      }| j                  |¬«      ƒ d{  –—† }|�St        |«      }||j                  z  }	t        ||	«      }t        j                  d|› d|› d
|j                  › d|	d›d�	«       t        j                  d|› d|d›�«       |S 7 �Œ 7 Œz# t        $ r!}
t        j                  d||
«       Y d}
~
yd}
~
ww xY w­w)aC  
        Check current saturation by directly querying v3 limiter's cache keys.

        Reuses v3 limiter's Redis-based tracking (works across multiple instances).
        Reads counters WITHOUT incrementing them.

        Returns:
            float: Saturation ratio (0.0 = empty, 1.0 = at capacity, >1.0 = over)
        g        Nr   Úmodel_saturation_checkÚrequests©r9   rK   Úrate_limit_type)r7   zModel z RPM: ú/z (ú.1%ú)Útokensz TPM: z overall saturation: z$Error checking saturation for %s: %s)Úrpmr,   Úcreate_rate_limit_keysr?   ÚintÚmaxr   rF   ÚtpmÚfloatÚ	ExceptionrI   )r-   r]   rf   Úmax_saturationr7   Úcounter_valueÚcurrent_requestsÚrpm_saturationÚcurrent_tokensÚtpm_saturationÚes              r#   Ú_check_model_saturationz8_PROXY_DynamicRateLimitHandlerV3._check_model_saturationß   sò  è ø€ ð2	Ø ˆNð  ×#Ñ#Ð/Ð4D×4HÑ4HÈ1Ò4Là"Ÿo™o×DÑDØ0ØØ$.ð Eó �ð '+×&KÑ&KÐXcÐ&KÓ&d× d�à Ð,Ü.1°-Ó.@Ð$Ø,<Ð?O×?SÑ?SÑ,S�NÜ%(¨¸Ó%H�Nä(×.Ñ.Ø    vÐ.>Ð-?¸qÐAQ×AUÑAUÐ@VÐVXÐYgÐhkÐXlÐlmÐnôð
  ×#Ñ#Ð/Ð4D×4HÑ4HÈ1Ò4LØ"Ÿo™o×DÑDØ0ØØ$,ð Eó �ð '+×&KÑ&KÐXcÐ&KÓ&d× d�à Ð,Ü,1°-Ó,@�NØ,:Ð=M×=QÑ=QÑ,Q�NÜ%(¨¸Ó%H�Nä(×.Ñ.Ø    v¨nÐ-=¸QÐ?O×?SÑ?SÐ>TÐTVÐWeÐfiÐVjÐjkÐlôô !×&Ñ&¨°¨wÐ6KÈNÐ[^ÐK_Ð'`Ôaà!Ð!ðA !eùð& !eùô ò 	Ü ×&Ñ&Ð'MÈuÐVWÔXäûð	üsU   ‚F&„AE9 ÁE4ÁB'E9 Ã<E7Ã=A6E9 Å3F&Å4E9 Å7E9 Å9	F#ÆFÆF&ÆF#Æ#F&c                 óØ  — g }t         j                  €|S | j                  j                  |¬«      }|€|S | j	                  |«      }| j                  ||||¬«      \  }}i }	|j                  �t        |j                  |z  «      }
|
|	d<   |j                  �t        |j                  |z  «      }||	d<   |	r6| j                  j                  |	d<   |j                  t        d||	¬«      «       |S )aU  
        Create rate limit descriptors with normalized priority weights.

        Uses normalized weights to handle over-allocation scenarios.

        For explicit priorities: each priority gets its own pool (e.g., prod gets 75%)
        For default priority: ALL keys without explicit priority share ONE pool (e.g., all share 25%)
        ©Úmodel_group)r]   r@   r^   rA   Útokens_per_unitÚrequests_per_unitÚwindow_sizeÚpriority_model©r9   rK   Ú
rate_limit)r   rE   r/   Úget_model_group_infor\   re   rt   rr   rp   r,   r„   Úappendr   )r-   r]   rM   r@   Údescriptorsrf   r^   rc   rd   Úrate_limit_configÚreserved_tpmÚreserved_rpms               r#   Ú"_create_priority_based_descriptorszC_PROXY_DynamicRateLimitHandlerV3._create_priority_based_descriptors!  s$  € ð 9;ˆä×'Ñ'Ð/ØÐð :>¿¹×9]Ñ9]ÐjoÐ9]Ó9pÐØÐ#ØÐð %)×$DÑ$DÐEUÓ$VÐØ(,×(EÑ(EØØØ1Ø'ð	 )Fó )
Ñ%ˆ˜ð HJÐð ×ÑÐ+Ü"%Ð&6×&:Ñ&:¸_Ñ&LÓ"MˆLØ3?ÐÐ/Ñ0à×ÑÐ+Ü"%Ð&6×&:Ñ&:¸_Ñ&LÓ"MˆLØ5AÐÐ1Ñ2áØ/3¯©×/JÑ/JÐ˜mÑ,à×ÑÜ#Ø(Ø&Ø0ôôð Ðr%   Úhigh_limit_multiplierc                 óº   — t        d||j                  r|j                  |z  nd|j                  r|j                  |z  nd| j                  j                  dœ¬«      S )aW  
        Create a descriptor for tracking model-wide usage.

        Args:
            model: Model name
            model_group_info: Model configuration with RPM/TPM limits
            high_limit_multiplier: Multiplier for limits (use >1 for tracking-only)

        Returns:
            Rate limit descriptor for model-wide tracking
        rh   N)rƒ   r‚   r„   r†   )r   rp   rt   r,   r„   )r-   r]   rf   r�   s       r#   Ú!_create_model_tracking_descriptorzB_PROXY_DynamicRateLimitHandlerV3._create_model_tracking_descriptorZ  s_   € ô" #Ø(ØàVf×VjÒVjÐ&6×&:Ñ&:Ð=RÒ&RÐptØTd×ThÒThÐ$4×$8Ñ$8Ð;PÒ$PÐnrØ#Ÿ™×:Ñ:ñô
ð 	
r%   Ú
saturationc              ƒ   ó
  K  — ddl }t        «       j                  }||k\  }g }	| j                  ||d¬«      }
|	j	                  |
«       | j                  |||¬«      }|r|	j                  |«       |
g}|r|r|j                  |«       dddœ}| j                  j                  ||D �cg c]  }|‘Œ c}|j                  ¬«      ƒ d{  –—† }t        j                  d|j                  |d	t        ¬
«      «       |d   dk(  �rt        |«      \  }}|d   D �]n  }|d   dk7  rŒ|d   }|dk(  r—t        dd|› d|› d|d   › d|j                   �|j                   nd› d|j"                  �|j"                  nd› d|d   › �it%        | j                  j&                  «      t%        |d   «      |xs ddœt)        |d   «      ||¬«      ‚|dk(  sŒ´t        j                  d|› d |d!›d"|› �«       t        dd#|› d$|› d|d   › d|j                   �|j                   nd› d|j"                  �|j"                  nd› d|d   › d%|d!›�it%        | j                  j&                  «      t%        |d   «      |xs d|d&›d'œt)        |d   «      ||¬«      ‚ t+        d(„ |d   D «       d«      }t        j,                  d)|«       t        d*|r|d   nd+|rt%        |d   «      nd+d,œt)        |r|d   nd«      t%        | j                  j&                  «      |xs dd-œ||¬.«      ‚|rY|sW| j                  j/                  ||j                  d/¬0«      ƒ d{  –—† }t1        |d   |d   |d   z   ¬1«      t3        «       _        y|t3        «       _        yc c}w 7 �Œº7 ŒC­w)2ad  
        Check rate limits using THREE-PHASE approach to prevent partial increments.

        Phase 1: Read-only check of ALL limits (no increments)
        Phase 2: Decide which limits to enforce based on saturation
        Phase 3: Increment ALL counters atomically (model + priority)

        This prevents the bug where:
        - Model counter increments in stage 1
        - Priority check fails in stage 2
        - Request blocked but model counter already incremented

        Key behaviors:
        - All checks performed first (read-only)
        - Only increment counters if request will be allowed
        - Model capacity: Always enforced at 100%
        - Priority limits: Only enforced when saturated >= threshold
        - Both counters tracked from first request (accurate accounting)

        Args:
            model: Model name
            model_group_info: Model configuration
            user_api_key_dict: User authentication info
            priority: User's priority level
            saturation: Current saturation level

        Raises:
            HTTPException: If any limit is exceeded
        r   Né   )r]   rf   r�   )r]   rM   r@   )ri   ro   )rŠ   Ú
incrementsÚparent_otel_spanz#Atomic check+increment response: %sé   )ÚindentÚdefaultÚoverall_codeÚ
OVER_LIMITÚstatusesÚcodeÚdescriptor_keyrh   rI   zModel capacity reached for z. Priority: z, Rate limit type: rk   z, Model TPM: znot configuredz, Model RPM: z, Remaining: Úlimit_remainingr™   )úretry-afterrk   úx-litellm-priority)ÚdetailÚheadersrk   r]   Úllm_providerr…   zEnforcing priority limits for z, saturation: rm   z, priority: z+Priority-based rate limit exceeded. Model: z, Priority: z, Model saturation: z.2%)r    rk   r¡   zx-litellm-saturationc              3   ó2   K  — | ]  }|d    dk(  sŒ|–— Œ y­w)r�   r›   N© )Ú.0Úss     r#   ú	<genexpr>zF_PROXY_DynamicRateLimitHandlerV3._check_rate_limits.<locals>.<genexpr>  s   è ø€ ÒU�q¸1¸V¹9ÈÓ;T”ÑUùs   ‚�uj   Dynamic rate limiter: OVER_LIMIT response with unknown descriptor_key(s) â€” refusing request. response=%szRate limit exceededÚunknown)rI   rž   rk   )r    r¡   )r¢   rk   r£   r]   r¤   F)rŠ   r–   Ú	read_only)rš   rœ   )Újsonr$   Úsaturation_thresholdr‘   r‰   rŽ   Úextendr,   Úatomic_check_and_increment_by_nr–   r   rF   ÚdumpsÚlistr   r   rt   rp   Ústrr„   r   ÚnextrI   Úshould_rate_limitr   r   Úrate_limit_response)r-   r]   rf   rM   r@   r’   r¬   r­   Úshould_enforce_priorityÚdescriptors_to_checkÚmodel_wide_descriptorÚpriority_descriptorsÚenforced_descriptorsÚper_request_incrementÚ_Úatomic_responseÚresolved_modelr¤   Ústatusrž   Ú	offendingÚpriority_tracking_responses                         r#   Ú_check_rate_limitsz3_PROXY_DynamicRateLimitHandlerV3._check_rate_limitsu  s¶  è ø€ óJ 	ä&<Ó&>×&SÑ&SÐØ)3Ð7KÑ)KÐð BDÐð (,×'MÑ'MØØ-Ø"#ð (Nó (
Ðð
 	×#Ñ#Ð$9Ô:ð '+×&MÑ&MØØ/Øð 'Nó '
Ðñ
  Ø ×'Ñ'Ð(<Ô=ð CXÐAXÐÙÑ$;Ø ×'Ñ'Ð(<Ô=ð ØñR
Ðð (,§¡×'VÑ'VØ,Ø7KÖL°!Ò-ÒLØ.×?Ñ?ð (Wó (
÷ "
ˆô 	×"Ñ"Ø1°4·:±:¸oÐVWÔae°:Ó3fô	
ð ˜>Ñ*¨lÓ:Ü+NÈuÓ+UÑ(ˆN˜LØ)¨*Ñ5ó /�Ø˜&‘> \Ò1ØØ!'Ð(8Ñ!9�Ø!Ð%=Ò=Ü-à#Ð'BÀ5À'ð J)Ø)1¨
ð 30Ø06Ð7HÑ0IÐ/Jð K*ØBR×BVÑBVÐBbÐ*:×*>Ò*>ÐhxÐ)yð z*ØBR×BVÑBVÐBbÐ*:×*>Ò*>ÐhxÐ)yð z*Ø*0Ð1BÑ*CÐ)Dð&Fð ô ,/¨t¯©×/JÑ/JÓ+KÜ/2°6Ð:KÑ3LÓ/MØ2:Ò2G¸iñ!ô
 )?¸vÐFWÑ?XÓ(YØ,Ø%1ô!ð ð$ "Ð%5Ó5Ü(×.Ñ.Ø8¸¸¸~ÈjÐY\ÐM]Ð]iÐjrÐisÐtôô .à#ð (&Ø&+ Wð -)Ø)1¨
ð 30Ø06Ð7HÑ0IÐ/Jð K*ØBR×BVÑBVÐBbÐ*:×*>Ò*>ÐhxÐ)yð z*ØBR×BVÑBVÐBbÐ*:×*>Ò*>ÐhxÐ)yð z*Ø*0Ð1BÑ*CÐ)Dð E1Ø1;¸CÐ0@ð&Bð	 ô ,/¨t¯©×/JÑ/JÓ+KÜ/2°6Ð:KÑ3LÓ/MØ2:Ò2G¸iØ7AÀ#Ð6Fñ	!ô )?¸vÐFWÑ?XÓ(YØ,Ø%1ô'ð ð7/ôn  $ÙU˜O¨JÑ7ÔUØó ˆIô !×&Ñ&Ø|Øôô &à2ÙFO yÐ1AÒ'BÐU^ÙMV¬¨IÐ6GÑ,HÔ(IÐ\eñô
 !7ÑW`°yÐARÒ7SÐfjÓ kä#& t§¡×'BÑ'BÓ#CØ*2Ò*?°iñð %Ø)ôð ñ2  Ñ(?Ø6:·o±o×6WÑ6WØ0Ø!2×!CÑ!CØð 7Xó 7÷ 1Ð&ô
 ARØ,¨^Ñ<Ø(¨Ñ4Ð7QÐR\Ñ7]Ñ]ôAÔ'Ó)Õ=ð
 APÔ'Ó)Õ=ùò[ Mð"
ùðJ1ús9   ‚B#NÂ%	M9
Â.NÃM>ÃC?NÇE8NÌ=NÌ>ANÎNÚcacheÚdataÚ	call_typec           
   ƒ   ó  K  — d|vryt        |«       |d   }| j                  |¬«      }| j                  j                  |¬«      }|€t	        j
                  d|«       y	 | j                  ||«      ƒ d{  –—† }t        «       j                  }	t	        j
                  d|› d|d›d	|	d›d
|› �«       | j                  |||||¬«      ƒ d{  –—†  y7 ŒZ7 Œ# t        $ r ‚ t        $ r }
t	        j                  d|
«       Y d}
~
yd}
~
ww xY w­w)u/  
        Saturation-aware pre-call hook for priority-based rate limiting.

        Flow:
        1. Check current saturation level
        2. THREE-PHASE rate limit check:
           - PHASE 1: Read-only check of ALL limits (no increments)
           - PHASE 2: Decide which limits to enforce based on saturation
           - PHASE 3: Increment ALL counters atomically if request allowed

        This three-phase approach ensures:
        - Model capacity is NEVER exceeded (always enforced at 100%)
        - Priority usage tracked from first request (accurate metrics)
        - Counters only increment when request will be allowed (prevents phantom usage)
        - When under-saturated: priorities can borrow unused capacity (generous)
        - When saturated: fair allocation based on normalized priority weights (strict)

        Example with 100 RPM model, 60% priority allocation, 80% threshold:
        - Saturation < 80%: Priority can use up to 100 RPM (model limit enforced only)
        - Saturation >= 80%: Priority limited to 60 RPM (both limits enforced)

        Prevents bugs where:
        - Model counter increments but priority check fails â†’ model over-capacity
        - Priority counter increments but not enforced â†’ inaccurate metrics

        Args:
            user_api_key_dict: User authentication and metadata
            cache: Dual cache instance
            data: Request data containing model name
            call_type: Type of API call being made

        Returns:
            None if request is allowed, otherwise raises HTTPException
        r]   N©rM   r€   z,No model group info for %s, allowing requestz[Dynamic Rate Limiter] Model=z, Saturation=rm   z, Threshold=z, Priority=)r]   rf   rM   r@   r’   z3Error in dynamic rate limiter: %s, allowing request)r   rR   r/   rˆ   r   rF   r~   r$   r­   rÂ   r   rv   rI   )r-   rM   rÃ   rÄ   rÅ   r]   r@   rf   r’   r­   r}   s              r#   Úasync_pre_call_hookz4_PROXY_DynamicRateLimitHandlerV3.async_pre_call_hook4  sA  è ø€ ðR ˜$ÑØä$ TÔ*Ø˜G‘}ˆØ×CÑCÐVgÐCÓhˆð :>¿¹×9]Ñ9]ÐjoÐ9]Ó9pÐØÐ#Ü ×&Ñ&Ð'UÐW\Ô]Øð	à&*×&BÑ&BÀ5ÐJZÓ&[× [ˆJä*@Ó*B×*WÑ*WÐ ä ×&Ñ&Ø/°¨w°mÀJÈsÐCSð TØ1°#Ð6°kÀ(ÀðMôð ×)Ñ)ØØ!1Ø"3Ø!Ø%ð *ó ÷ ð ð ð; !\øðùô ò 	ØÜò 	Ü ×&Ñ&Ð'\Ð^_Ô`äûð	üsU   ‚AD	Á C Á5CÁ6AC Ã
CÃC ÃD	ÃC ÃC ÃDÃ&DÃ<D	ÄDÄD	c              ƒ   ó¢  K  — 	 | j                   j                  |||¬«      ƒ d{  –—†  t        |t        «      rc| j	                  |¬«      }t        |di «      j                  di «      xs i }|xs d|d<   d|d	<   t        |d«      si |_        ||j                  d<   |S 7 Œy# t        $ r"}t        j                  d
|«       |cY d}~S d}~ww xY w­w)z„
        Post-call hook to add rate limit headers to response.
        Leverages v3 limiter's post-call hook functionality.
        )rÄ   rM   ÚresponseNrÇ   Ú_hidden_paramsÚadditional_headersr™   r¡   Úv3zx-litellm-rate-limiter-versionz3Error in dynamic rate limiter v3 post-call hook: %s)r,   Úasync_post_call_success_hookÚ
isinstancer	   rR   ÚgetattrrP   ÚhasattrrË   rv   r   Ú	exception)r-   rÄ   rM   rÊ   r@   rÌ   r}   s          r#   rÎ   z=_PROXY_DynamicRateLimitHandlerV3.async_post_call_success_hook‹  sô   è ø€ ð
	à—/‘/×>Ñ>ØÐ->Èð ?ó ÷ ð ô
 ˜(¤MÔ2Ø"&×"KÑ"KÐ^oÐ"KÓ"p�ô -4°HÐ>NÐPRÓ,S×,WÑ,WÐXlÐnpÓ,qÒ,wÐuwÐ"ð <DÒ;PÀyÐ"Ð#7Ñ8ØGKÐ"Ð#CÑDô ˜xÐ)9Ô:Ø.0�HÔ+Ø@R�×'Ñ'Ð(<Ñ=àˆOð)ùô, ò 	Ü ×*Ñ*Ð+`ÐbcÔdØ�Oûð	üsE   ‚C„!B! ¥B¦A8B! ÂCÂB! Â!	CÂ*CÃCÃCÃCÃCc              ƒ   óÔ  K  — ddl m} ddlm} ddlm} ddlm} 	 t        j                  d«        ||«      }	|j                  d«      xs i }
|
j                  d«      xs i } ||«      }|sy	|j                  d
«      xs i }|j                  d«      }d}| j                  j                  «       }t        |t        «      rPt        |dd	«      }|rAt        ||«      r5|dk(  r|j                   }n#|dk(  r|j"                  }n|dk(  r|j$                  }|dk(  ry	g }| j                  j'                  d|d¬«      }|j)                   |||| j                  j*                  ¬«      «       |d	uxr& t,        j.                  d	uxr |t,        j.                  v }|r
|�|› d|› �}n|› d�}| j                  j'                  d|d¬«      }|j)                   |||| j                  j*                  ¬«      «       |rJ| j                  j1                  ||	¬«      ƒ d	{  –—†  h d£}||v r|nd}t        j                  d|||«       y	y	7 Œ*# t2        $ r }t        j4                  d|«       Y d	}~y	d	}~ww xY w­w)a   
        Update token usage for priority-based rate limiting after successful API calls.

        Increments token counters for:
        - model_saturation_check: Model-wide token tracking
        - priority_model: Priority-specific token tracking
        r   )Ú!_get_parent_otel_span_from_kwargs)Ú#get_model_group_from_litellm_kwargs)ÚRedisPipelineIncrementOperation)ÚUsagez1INSIDE dynamic rate limiter ASYNC SUCCESS LOGGINGÚstandard_logging_objectrQ   NÚuser_api_key_auth_metadatar@   ÚusageÚoutputÚinputÚtotalrh   ro   rj   )r9   Úincrement_valuer<   r`   ra   r…   )Úpipeline_operationsr–   >   ÚlowÚhighÚmediumr™   ÚREDACTEDzI[Dynamic Rate Limiter] Incremented tokens by %s for model=%s, priority=%sz/Error in dynamic rate limiter success event: %s)Ú'litellm.litellm_core_utils.core_helpersrÔ   Ú)litellm.proxy.common_utils.callback_utilsrÕ   Úlitellm.types.cachingrÖ   r!   r×   r   rF   rP   r,   Úget_rate_limit_typerÏ   r	   rÐ   Úcompletion_tokensÚprompt_tokensÚtotal_tokensrq   r‰   r„   r   rE   Ú,async_increment_tokens_with_ttl_preservationrv   rÒ   )r-   ÚkwargsÚresponse_objÚ
start_timeÚend_timerÔ   rÕ   rÖ   r×   r:   rØ   Ústandard_logging_metadatar�   rÙ   Úkey_priorityrê   rk   Ú_usagerß   Úmodel_token_keyrb   rd   Úpriority_token_keyÚSAFE_PRIORITIESÚlogged_priorityr}   s                             r#   Úasync_log_success_eventz8_PROXY_DynamicRateLimitHandlerV3.async_log_success_event¬  s¿  è ø€ õ	
õ	
õ 	JÝ-ða	aÜ ×&Ñ&Ð'ZÔ[á.OÐPVÓ.WÐ$ð .4¯Z©ZÐ8QÓ-RÒ-XÐVXÐ#Ø/F×/JÑ/JÈ:Ó/VÒ/\ÐZ\Ð%ñ "EÀVÓ!LˆKÙØð 1J×0MÑ0MÐNjÓ0kÒ0qÐoqÐ&Ø.H×.LÑ.LÈZÓ.XˆLð ˆLØ%)§_¡_×%HÑ%HÓ%JˆOä˜,¬Ô6Ü '¨°g¸tÓ D�Ùœj¨°Ô7Ø&¨(Ò2Ø'-×'?Ñ'?™Ø(¨GÒ3Ø'-×';Ñ';™Ø(¨GÒ3Ø'-×':Ñ':˜à˜qÒ Øð QSÐð &*§_¡_×%KÑ%KØ,Ø!Ø (ð &Ló &ˆOð
  ×&Ñ&Ù/Ø'Ø$0ØŸ™×3Ñ3ôôð  DÐ(ò AÜ×0Ñ0¸Ð<òAà ¤G×$@Ñ$@Ð@ð "ñ %¨Ð)AØ"- ¨a°¨~Ð>‘à"- ¨mÐ<�à(,¯©×(NÑ(NØ$Ø"Ø (ð )Oó )Ðð
  ×&Ñ&Ù/Ø*Ø$0ØŸ™×3Ñ3ôôñ #Ø—o‘o×RÑRØ(;Ø%=ð Só ÷ ð ò *N�Ø9EÈÑ9X©Ð^h�Ü$×*Ñ*Ø_Ø ØØ#õ	ð #ðùô ò 	aÜ ×*Ñ*Ð+\Ð^_×`Ñ`ûð	aüs[   ‚I(œAH< Á-I(Á.B'H< ÄI(ÄC9H< ÈH:È(H< È8I(È:H< È<	I%ÉI ÉI(É I%É%I(r1   )r”   )"Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r   r.   r
   r2   rr   r6   r²   r?   r   ru   rL   r   rR   Údictr\   Útuplere   r~   r±   r   rŽ   r‘   rÂ   r   rv   rÈ   rÎ   r÷   r¦   r%   r#   r'   r'   :   s_  „ ñð, 8<ñwà'ðwð    H Ñ-°Ñ4ówð%¨6ó %ðC°ó Cð
àð
ð 
ˆt‰ó
ñ2¨S°4©Zð À^ÐVZÑEZð Ðfkó ðÀnð ÐY\Ð_cÑYcó ð0°nð ÈÈcÐSXÈjÑIYó ð: -1ñ(-àð(-ð ˜‘*ð(-ð !  e Ñ,ð	(-ð
 # TÑ)ð(-ð 
ˆu�cˆzÑ	ó(-ðT@àð@ð )ð@ð 
ó	@ðD7àð7ð *ð7ð ˜‘*ð	7ð
 
Ð!Ñ	"ó7ðz &'ñ	
àð
ð )ð
ð  #ð	
ð
 
ó
ð6}Pàð}Pð )ð}Pð *ð	}Pð
 ˜‘*ð}Pð ð}Pð 
ó}Pð~Uà)ðUð ðUð ð	Uð
 $ðUð 
�S‰˜4Ñ	 $Ñ	&óUðn°tð ÐP^ó óBrar%   r'   )r3   r   ),rû   rG   Úcollections.abcr   r   Útypingr   r   r   Úfastapir   r   r	   r
   Úlitellm._loggingr   Úlitellm.caching.cachingr   Ú"litellm.integrations.custom_loggerr   Úlitellm.proxy._typesr   Ú1litellm.proxy.common_utils.proxy_rate_limit_errorr   r   Ú/litellm.proxy.hooks.parallel_request_limiter_v3r   r   r   r   r   r   Ú&litellm.proxy.hooks.rate_limiter_utilsr   r   Úlitellm.proxy.utilsr   Úlitellm.types.routerr   r!   r   r   r$   r'   r¦   r%   r#   ú<module>r
     sj   ðñó 
Ý $Ý ß 0Ñ 0å !ã ß )Ý 1Ý -Ý ;Ý /÷÷÷ ÷õ 3Ý /Ý 0áÝ?óô da |õ dar%   