Ë
    uPêjÿ&  ã                   óÀ   — U d Z ddlZddlmZmZmZ ddlmZ erddlm	Z
  G d„ de«      Z G d„ d	e«      Z G d
„ de«      Z edh«      Zeed<   	 ddedz  ded   fd„Zy)aÒ  Bounds the blast radius of a single node's transient connection error on the async
Redis Cluster client.

redis-py's ``RedisCluster._execute_command`` responds to a ``ConnectionError`` or
``TimeoutError`` on ANY one node by tearing down every node's connections and flipping
the client into "needs reinitialization", which forces every other concurrent caller
sharing this client through one reinit lock until the whole cluster topology is
re-walked. Under real proxy load, a client-side socket timeout on a single node is a
routine event (the event loop was too busy to read the response before ``socket_timeout``
elapsed) and does not mean the cluster's topology moved, so treating it as a full-cluster
event turns one slow node into a proxy-wide latency spike while Redis itself stays
healthy -- confirmed live: pausing one of three local cluster nodes made every concurrent
command against the other two, untouched nodes stall for the full pause duration too.

``get_litellm_async_redis_cluster_class`` returns a ``RedisCluster`` subclass that resets
only the node that actually failed (mirroring what a plain, non-cluster Redis client
already does when one of its pooled connections errors), leaving every other node's
connections untouched. Every other branch (MOVED, ASK, CLUSTERDOWN, slot-not-covered,
retry-exhaustion) is unchanged from upstream, since those already carry real evidence the
topology changed.

redis-py 8.x fixed this upstream with gentler machinery than this override's
``node.disconnect()`` (which also kills connections other coroutines are mid-operation
on, so one timeout cascades into a reconnect storm and, with TLS, a fresh handshake per
killed connection): it marks in-use connections for reconnect only after their current
operation completes, disconnects only the idle pooled ones, and defers reinitialization
to the outer retry loop. When the installed ``ClusterNode`` has that per-connection
recovery API, the factory returns the base ``RedisCluster`` unmodified.
é    N)ÚTYPE_CHECKINGÚFinalÚProtocol)Úverbose_logger©ÚRedisClusterc                   ó,   — e Zd ZdZdededefd„Zdd„Zy)	Ú_ClusterNodeAttrsa›  The subset of ``redis.asyncio.cluster.ClusterNode`` this override reads. redis-py
    ships no resolvable stub for these members under the repo's current types-redis pin,
    so a plain attribute access resolves every downstream use to ``Unknown`` under strict
    mode; typing ``target_node`` as this Protocol at the one boundary keeps the override's
    own logic fully typed without a banned ``typing.cast``.ÚargsÚkwargsÚreturnc              �   ó   K  — y ­w©N© )Úselfr   r   s      úq/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/caching/redis_cluster_node_isolation.pyÚexecute_commandz!_ClusterNodeAttrs.execute_command/   s   è ø€ ð ùó   ‚Nc              ƒ   ó   K  — y ­wr   r   ©r   s    r   Ú
disconnectz_ClusterNodeAttrs.disconnect4   ó   è ø� ùr   ©r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Úobjectr   r   r   ó    r   r
   r
   (   s-   „ ñ?ðàðð ðð 
ó	ô
 ,r   r
   c                   ó0   — e Zd ZU eed<   dedededefd„Zy)Ú_NodesManagerAttrsÚ_moved_exceptionÚslotÚread_from_replicasÚload_balancing_strategyr   c                  ó   — y r   r   )r   r#   r$   r%   s       r   Úget_node_from_slotz%_NodesManagerAttrs.get_node_from_slot:   s   € àr   N)	r   r   r   r   Ú__annotations__ÚintÚboolr
   r'   r   r   r   r!   r!   7   s-   … ØÓð Øð Ø-1ð ØLRð à	ô r   r!   c                   ór   — e Zd ZU eed<   eed<   eed<   eed<   eed<   eed<   dede	fd	„Z
d
edefd„Zdd„Zy)Ú_ClusterAttrsÚRedisClusterRequestTTLÚreinitialize_counterÚreinitialize_stepsr$   r%   Únodes_managerÚ	node_namer   c                  ó   — y r   r   )r   r1   s     r   Úget_nodez_ClusterAttrs.get_nodeG   s   � r   r   c              ‡   ó   K  — y ­wr   r   )r   r   s     r   Ú_determine_slotz_ClusterAttrs._determine_slotH   r   r   Nc              ƒ   ó   K  — y ­wr   r   r   s    r   Úaclosez_ClusterAttrs.acloseI   r   r   r   )r   r   r   r)   r(   r*   r   r!   Ústrr
   r3   r5   r7   r   r   r   r,   r,   ?   sD   … ØÓØÓØÓØÓØ#Ó#Ø%Ó%à@ #Ð@Ð*;Ó@Ø>¨6Ð>°cÓ>Ü'r   r,   z5.3.1Ú_VERIFIED_REDIS_VERSIONSÚcluster_node_classr   Ú_AsyncRedisClusterTypec                 ó®  ‡‡‡‡	‡
‡‡‡‡‡‡‡— ddl }ddlm} ddlm} ddlmŠ ddlmŠ ddlm	Šm
ŠmŠmŠ	mŠ
mŠmŠmŠ ddlmŠ dd	lmŠ | �| n|}t'        |d
«      r"t)        j*                  d|j,                  «       |S |j,                  t.        vr.t)        j0                  d|j,                  t3        t.        «      «        G ˆˆˆˆ	ˆ
ˆˆˆˆˆˆˆfd„d|«      }|S )a¶  Returns the base ``RedisCluster`` when the installed redis-py already recovers a
    node-level connection error per-connection (8.x+), else builds the ``RedisCluster``
    subclass with the per-node isolation fix for older versions whose upstream branch
    tears down the whole cluster client.

    ``cluster_node_class`` exists for dependency injection in tests; production callers
    leave it unset and the installed ``ClusterNode`` is used.

    Imported lazily because this module is reachable from a base ``import litellm`` while
    redis is not a base dependency. Cheap to call repeatedly: the underlying redis
    submodules are cached in ``sys.modules`` after the first import.
    r   N)ÚClusterNoder   )Úget_node_name)ÚREAD_COMMANDS)ÚAskErrorÚBusyLoadingErrorÚClusterDownErrorÚClusterErrorÚMaxConnectionsErrorÚ
MovedErrorÚSlotNotCoveredErrorÚTryAgainError)ÚConnectionError)ÚTimeoutErrorÚ'update_active_connections_for_reconnectz’redis-py %s recovers a node-level connection error per-connection upstream; using the base RedisCluster without litellm's node-isolation override.z¾redis-py %s is not in the set this cluster-teardown-storm fix was verified against (%s). The per-node-isolation override may not match the installed library's real _execute_command behavior.c                   ó@   •— e Zd Zdedededefˆ ˆˆˆˆˆˆˆˆˆ	ˆ
ˆfd„Zy)úGget_litellm_async_redis_cluster_class.<locals>.LiteLLMAsyncRedisClusterÚtarget_noder   r   r   c              Ÿ   ój  •K  — | }|}dx}}d }|j                   }	|	dkD  r²|	dz  }		 |r2|€J ‚|j                  |¬«      }|j                  d«      ƒ d {  –—†  d}n^|r\ |j                  |Ž ƒ d {  –—† }
|j                  j                  |
|j                  xr |d   ‰v |d   ‰v r|j                  nd «      }d} |j                  |i |¤Žƒ d {  –—† S  ‰d«      ‚7 Œ‡7 Œm7 Œ# ‰‰f$ r ‚ ‰‰f$ r |j                  «       ƒ d {  –—†7   ‚ ‰‰f$ r9 |j                  «       ƒ d {  –—†7   t        j                  d«      ƒ d {  –—†7   ‚ ‰$ r{}|xj                  dz  c_        |j                  r=|j                  |j                  z  dk(  r!|j                  «       ƒ d {  –—†7   d|_        n||j                  _        d}Y d }~nkd }~w‰$ r*} ‰|j                  |j                   ¬«      }d}Y d }~n>d }~w‰$ r3 |	|j                   d	z  k  rt        j                  d
«      ƒ d {  –—†7   Y nw xY w|	dkD  r�Œ�Œb­w)NFr   é   )r1   ÚASKINGg      Ð?T)ÚhostÚporté   gš™™™™™©?zTTL exhausted.)r-   r3   r   r5   r0   r'   r$   r%   r   r7   ÚasyncioÚsleepr.   r/   r"   rQ   rR   )r   rM   r   r   ÚclusterÚnodeÚaskingÚmovedÚredirect_addrÚttlr#   Úer@   rA   rB   rC   rD   rE   r?   rF   rG   Ú_RedisConnectionErrorÚ_RedisTimeoutErrorr>   s               €€€€€€€€€€€€r   Ú_execute_commandzXget_litellm_async_redis_cluster_class.<locals>.LiteLLMAsyncRedisCluster._execute_command�   sP  øè ø€ ð &*ˆGØˆDà"Ð"ˆF�UØ(,ˆMØ×0Ñ0ˆCà˜’'Ø�q‘�ð)2ÙØ,Ð8Ð8Ð8Ø&×/Ñ/¸-Ð/ÓH˜Ø"×2Ñ2°8Ó<×<Ð<Ø!&™ÙØ%< W×%<Ñ%<¸dÐ%C×C˜Ø&×4Ñ4×GÑGØ Ø#×6Ñ6ÒS¸4À¹7ÀmÐ;SØ@DÀQÁÈ=Ñ@X˜W×<Ò<Ð^bó ˜ð
 !&˜à!5 ×!5Ñ!5°tÐ!F¸vÑ!F×FÐFñ8 Ð/Ó0Ð0ðO =øð  Døð GùØ(Ð*=Ð>ò ØØ-Ð/AÐBò ð Ÿ/™/Ó+×+Ñ+ØØ(Ð*=Ð>ò Ø!Ÿ.™.Ó*×*Ñ*Ü!Ÿ-™-¨Ó-×-Ñ-ØØ!ò !Ø×0Ò0°AÑ5Õ0Ø×1Ò1°g×6RÑ6RÐU\×UoÑUoÑ6oÐstÒ6tØ%Ÿn™nÓ.×.Ñ.Ø78˜Õ4àAB˜×-Ñ-Ô>Ø •EûØò "Ù$1°q·v±vÀAÇFÁFÔ$K�MØ!•FûØ$ò 2Ø˜W×;Ñ;¸aÑ?Ò?Ü%Ÿm™m¨DÓ1×1Ñ1ùð2úðS ˜”'ûsÀ   ƒ!H3¥,C ÁCÁC Á-CÁ.AC ÃCÃC Ã	H3ÃC ÃC ÃC Ã!H'Ä DÄH'Ä D#Ä!H'Ä>EÄ?
H'Å	AF?ÆFÆ F?Æ:H3Æ?H'Ç G,Ç'H3Ç,2H'ÈH!ÈH'È$H3È&H'È'H3N)r   r   r   r
   r   r_   )r@   rA   rB   rC   rD   rE   r?   rF   rG   r]   r^   r>   s   €€€€€€€€€€€€r   ÚLiteLLMAsyncRedisClusterrL   Š   s8   ø„ ð:	1à*ð:	1ð ð:	1ð ð	:	1ð
 ÷:	1÷ :	1ñ :	1r   r`   )ÚredisÚredis.asyncio.clusterr=   r   Úredis.clusterr>   Úredis.commandsr?   Úredis.exceptionsr@   rA   rB   rC   rD   rE   rF   rG   rH   rI   Úhasattrr   ÚdebugÚ__version__r9   ÚwarningÚsorted)r:   ra   Ú_AsyncClusterNodeÚ_BaseAsyncRedisClusterÚ
node_classr`   r@   rA   rB   rC   rD   rE   r?   rF   rG   r]   r^   r>   s         @@@@@@@@@@@@r   Ú%get_litellm_async_redis_cluster_classrn   S   sÇ   ÿû€ ó õõõ ,Ý,÷	÷ 	ó 	õ JÝCà.@Ð.LÑ*ÐRc€JÜˆzÐDÔEÜ×ÑðUà×Ñô	
ð
 &Ð%à×ÑÔ 8Ñ8Ü×Ñð.ð ×ÑÜÔ+Ó,ô	
÷=1÷ =1ñ =1Øô=1ð~ $Ð#r   r   )r   rT   Útypingr   r   r   Úlitellm._loggingr   rb   r   r;   r
   r!   r,   Ú	frozensetr9   r(   Útypern   r   r   r   ú<module>rs      s}   ðòó< ß 1Ñ 1å +áÝLô,˜ô ,ô ˜ô  ô
(�Hô 
(ñ" #,¨W¨IÓ"6Ð ˜%Ó 6ð '+ñv$Ø˜t™ðv$à	Ð
"Ñ#ôv$r   