Ë
    uPêj,  ã                   ó¤  — U d Z ddlZddlZddlZddlZddlZddl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mZmZ dZeed	<   d
Zeed<   eez  Z edd¬«       G d„ d«      «       Zdedefd„Zdedz  fd„Zdedeg ef   dz  fd„Zdedefd„Z dede!fd„Z"dede!dz  fd„Z#dede!fd„Z$de
e   ddfd„Z% G d„ d«      Z& e&«       Z'eed <   y)!aA  
Deferred close of HTTP/SDK clients that the LLM client cache has evicted.

Eviction only drops the cache's reference to a client. Every OpenAI/Azure SDK
client is a reference cycle (each resource namespace holds the client back), so
an evicted client and its pooled TCP connections survive until a generational
collection runs, which under load is thousands of requests later.

Closing at eviction time is not an option: a request that was handed the client
just before it was evicted is still using it, and closing it underneath that
request raises ``RuntimeError: Cannot send a request, as the client has been
closed.``

So an evicted client is closed once two conditions hold. A grace window must
have passed since its eviction, which covers a request that holds the client
but is momentarily not on the wire, and the client must report no connection in
flight. The second condition is what keeps the first honest: a request may run
for ``litellm.request_timeout`` seconds, 6000 by default, and a streaming
response is bounded only by how long the upstream keeps sending, so no deadline
on its own can promise that a request has finished.

Only clients litellm itself created are closed; a client the caller supplied is
left alone because litellm does not own its lifecycle.

A client that closes synchronously is closed from wherever the cache is next
used. One whose close is a coroutine needs the event loop it was evicted on, so
it waits for a call from that loop rather than having work scheduled onto a loop
it does not belong to. Queued clients are therefore bucketed by what it takes to
close them, and each bucket is ordered by deadline, so a reap walks the entries
that are due rather than the whole queue.

The queue holds its clients weakly, so waiting out a grace window never keeps
alive anything the collector would have reclaimed first.
é    N)Údeque)Ú	AwaitableÚCallableÚIterator)Ú	dataclassÚreplace)ÚFinal)Ú&EVICTED_LLM_CLIENT_CLOSE_GRACE_SECONDSÚ$EVICTED_LLM_CLIENT_CLOSE_MAX_PENDINGzclosable-anywhereÚ_CLOSABLE_ANYWHEREzclosable-on-any-loopÚ_CLOSABLE_ON_ANY_LOOPT)ÚfrozenÚslotsc                   ó@   — e Zd ZU dZded<   edz  ed<   eed<   eed<   y)Ú_PendingClosea~  A queued close.

    The client is held weakly, so queueing one never keeps alive anything the
    collector would otherwise have reclaimed first.

    ``needs_loop`` is set for a client whose close is a coroutine; those can only
    be closed from the event loop they were evicted on, recorded in ``loop_id``.
    A client that closes synchronously carries neither constraint.
    zweakref.ref[object]Ú
client_refNÚloop_idÚ
needs_loopÚclose_after)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__ÚintÚboolÚfloat© ó    új/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/caching/evicted_client_closer.pyr   r   :   s%   … ñð &Ó%Ø�4‰ZÓØÓØÔr   r   ÚpendingÚreturnc                 ób   — | j                   st        S | j                  €t        S | j                  S )zPWhich reaps can close this entry: any at all, any running a loop, or one loop's.)r   r   r   r   )r!   s    r    Ú_bucket_keyr$   L   s+   € à×ÒÜ!Ð!Ø‡�ÐÜ$Ð$Ø�?‰?Ðr   c                  ó\   — 	 t        t        j                  «       «      S # t        $ r Y y w xY w©N)ÚidÚasyncioÚget_running_loopÚRuntimeErrorr   r   r    Ú_running_loop_idr+   U   s-   € ðÜ”'×*Ñ*Ó,Ó-Ð-øÜò Ùðús   ‚ Ÿ	+ª+Úclientc                 ó>   — t        | dd «      xs t        | dd «      }|S )NÚacloseÚclose©Úgetattr)r,   Úclose_fns     r    Ú_close_functionr3   \   s'   € Ü3:¸6À8ÈTÓ3RÒ3tÔV]Ð^dÐfmÐosÓVt€HØ€Or   c                 ój   — t        | dd«      t        | dd«      | fD ]  }t        |dd«      }|€Œ|c S  y)zOThe httpx transport behind an SDK wrapper, a litellm handler, or a bare client.Ú_clientNr,   Ú
_transportr0   )r,   ÚholderÚ	transports      r    Ú_transport_ofr9   a   sJ   € ä˜6 9¨dÓ3´W¸VÀXÈtÓ5TÐV\Ð]ò ˆÜ# F¨L¸$Ó?ˆ	ØÑ ØÒðð r   Ú
connectionc                 óV   — t        | dd«      }t        |«      rt         |«       «      S dS )z=A pooled connection is idle unless it is servicing a request.Úis_idleNT)r1   Úcallabler   )r:   r<   s     r    Ú_connection_is_idler>   j   s(   € ä$ Z°¸DÓA€GÜ& wÔ/Œ4‘“	‹?Ð9°TÐ9r   r8   c                 ó„   — t        t        | dd«      dd«      }t        |t        t        f«      syt	        d„ |D «       «      S )zžWhether the httpcore pool behind the transport is servicing a request.

    ``None`` when there is no such pool, so the caller can ask the other backend.
    Ú_poolNÚconnectionsc              3   ó4   K  — | ]  }t        |«       –— Œ y ­wr&   )r>   )Ú.0r:   s     r    ú	<genexpr>z,_pool_has_busy_connection.<locals>.<genexpr>x   s    è ø€ ò àô   
Ó+Ô+ñùs   ‚)r1   Ú
isinstanceÚlistÚtupleÚany)r8   Úpooleds     r    Ú_pool_has_busy_connectionrJ   p   sG   € ô
 $¤G¨I°wÀÓ$EÀ}ÐVZÓ[€FÜ�fœt¤U˜mÔ,ØÜñ à ôó ð r   c           	      ó²   — 	 t        | «      }t        |«      }|�|S t        |dd«      }t        t        t        |dd«      dd«      «      S # t        $ r Y yw xY w)a?  Whether the client is servicing a request right now.

    Both connection backends litellm uses already account for the connections
    they have handed out, so this reads the client's own lease accounting rather
    than inferring it from elapsed time: httpcore reports a non-idle connection
    for the whole of a response including a stream, and aiohttp holds the
    connection in ``_acquired`` over the same span.

    A client that cannot answer is reported as idle, which leaves the grace
    window as the only guard, exactly as it was before this check existed.
    Nr,   Ú	connectorÚ	_acquiredF)r9   rJ   r1   r   Ú	Exception)r,   r8   Úpooled_busyÚsessions       r    Ú_has_connection_in_flightrQ   ~   sd   € ðÜ(¨Ó0ˆ	Ü6°yÓAˆØÐ"ØÐÜ!(¨°H¸dÓ!CˆÜ”GœG G¨[¸$Ó?ÀÈdÓSÓTÐTøÜò Ùðús   ‚A
 œ-A
 Á
	AÁAÚclosingc              ƒ   ó€   K  — t        j                  t        «      5  | ƒ d {  –—†  d d d «       y 7 Œ# 1 sw Y   y xY w­wr&   )Ú
contextlibÚsuppressrN   )rR   s    r    Ú_close_quietlyrV   •   s8   è ø€ Ü	×	Ñ	œYÓ	'ñ Ø�ˆ÷ð Øø÷ð üs    ‚>œ2¢0£2§	>°2²;·>c            	       óú   — e Zd ZdZeeej                  fdede	de
g ef   ddfd„Zdeddfd	„Zdedefd
„Zdeddfd„Zdd„Zede	fd„«       Zdeddfd„Zde	dz  dedeedf   fd„Zdededee   fd„Zdeddfd„Zy)ÚEvictedClientCloserzJCloses evicted, litellm-owned clients once they are idle and out of grace.Úgrace_secondsÚmax_pendingÚclockr"   Nc                 óÌ   — || _         || _        || _        t        j                  «       | _        i | _        d| _        t        j                  «       | _
        t        «       | _        y )Nr   )Ú_grace_secondsÚ_max_pendingÚ_clockÚweakrefÚWeakSetÚ_ownedÚ_bucketsÚ_pending_countÚ	threadingÚLockÚ_queue_lockÚsetÚ_close_tasks)ÚselfrY   rZ   r[   s       r    Ú__init__zEvictedClientCloser.__init__�   sQ   € ð ,ˆÔØ'ˆÔØˆŒÜ/6¯©Ó/@ˆŒØ@BˆŒØˆÔÜ$Ÿ>™>Ó+ˆÔÜ58³UˆÕr   r,   c                 óZ   — 	 | j                   j                  |«       y# t        $ r Y yw xY w)zIRecord that litellm created this client, so it may be closed on eviction.N)rb   ÚaddÚ	TypeError©rj   r,   s     r    Ú
mark_ownedzEvictedClientCloser.mark_owned¬   s)   € ð	Ø�K‰K�O‰O˜FÕ#øÜò 	Ùð	ús   ‚ ž	*©*c                 ó>   — 	 || j                   v S # t        $ r Y yw xY w)NF)rb   rn   ro   s     r    Ú	_is_ownedzEvictedClientCloser._is_owned³   s(   € ð	Ø˜TŸ[™[Ð(Ð(øÜò 	Ùð	ús   ‚ �	›c           	      óJ  — |�| j                  |«      syt        |«      }|€y| j                  | j                  k\  ry| j	                  t        t        j                  |«      t        «       t        j                  |«      | j                  «       | j                  z   ¬«      «       y)a~  Queue an evicted client for closing once it is idle and out of grace.

        Past ``max_pending`` the client is left to the collector instead, so a
        workload that churns the cache cannot grow this queue without bound.
        Every queued entry comes due within one grace window, so the capacity it
        occupies is returned within that window rather than held.
        N)r   r   r   r   )rr   r3   rd   r^   Ú_enqueuer   r`   Úrefr+   ÚinspectÚiscoroutinefunctionr_   r]   )rj   r,   r2   s      r    ÚschedulezEvictedClientCloser.schedule¹   s‰   € ð ˆ> §¡°Ô!7ØÜ)¨&Ó1ˆØÐØØ×Ñ $×"3Ñ"3Ò3ØØ�‰ÜÜ"Ÿ;™; vÓ.Ü(Ó*Ü"×6Ñ6°xÓ@Ø ŸK™K›M¨D×,?Ñ,?Ñ?ô	õ	
r   c                 ó.  — | j                   sy| j                  «       }| j                  t        «       |«      D ][  }|j	                  «       }|€Œt        |«      r*| j                  t        ||| j                  z   ¬«      «       ŒK| j                  |«       Œ] y)zñClose every queued client that is due, idle, and closable from here.

        Called from the cache's read path, so the empty-queue exit comes first and
        the work done past it is proportional to what is due, not to the queue.
        N)r   )
rd   r_   Ú	_take_duer+   r   rQ   rt   r   r]   Ú_close)rj   Únowr!   r,   s       r    ÚreapzEvictedClientCloser.reapÑ   s„   € ð ×"Ò"ØØ—[‘[“]ˆØ—~‘~Ô&6Ó&8¸#Ó>ò 	 ˆGØ×'Ñ'Ó)ˆFØˆ~ØÜ(¨Ô0Ø—‘œg g¸3À×ATÑATÑ;TÔUÔVØØ�K‰K˜Õñ	 r   c                 ó   — | j                   S r&   )rd   )rj   s    r    Úpending_countz!EvictedClientCloser.pending_countã   s   € à×"Ñ"Ð"r   r!   c                 óŒ  — | j                   5  | j                  j                  t        |«      t	        «       «      }|rN|d   j                  «       €;|j                  «        | xj                  dz  c_        |r|d   j                  «       €Œ;|j                  |«       | xj                  dz  c_        ddd«       y# 1 sw Y   yxY w)a-  Append to the entry's bucket, dropping any dead entries it queues behind.

        Deadlines only ever move forward, so appending keeps each bucket ordered
        by deadline, and entries whose client the collector already took sit at
        the front rather than having to be searched for.
        r   Né   )	rg   rc   Ú
setdefaultr$   r   r   Úpopleftrd   Úappend)rj   r!   Úbuckets      r    rt   zEvictedClientCloser._enqueueç   s¥   € ð ×Ññ 	%Ø ŸM™M×4Ñ4´[ÀÓ5IÌ5Ë7ÓSˆFÙ˜V A™Y×1Ñ1Ó3Ð;Ø—‘Ô Ø×#Ò# qÑ(Õ#ñ ˜V A™Y×1Ñ1Ó3Ñ;ð �M‰M˜'Ô"Ø×Ò 1Ñ$Õ÷	%÷ 	%ñ 	%ús   �A=B:Â&B:Â:Cr   r|   .c                 ó    ‡ ‡— |€t         fnt         t        |f}‰ j                  5  t        ˆˆ fd„|D «       «      cd d d «       S # 1 sw Y   y xY w)Nc              3   óP   •K  — | ]  }‰j                  |‰«      D ]  }|–— Œ Œ y ­wr&   )Ú_drain_locked)rC   Úkeyr!   r|   rj   s      €€r    rD   z0EvictedClientCloser._take_due.<locals>.<genexpr>ù   s,   øè ø€ Ò` SÀ4×CUÑCUÐVYÐ[^ÓC_Ò`¸œÐ`˜Ñ`ùs   ƒ#&)r   r   rg   rG   )rj   r   r|   Úbucketss   ` ` r    rz   zEvictedClientCloser._take_dueö   sL   ù€ Ø+2¨?Ô%Ñ'ÔASÔUjÐlsÐ@tˆØ×Ññ 	aÜÔ`¨GÔ`Ó`÷	a÷ 	aò 	aús   ¥AÁAr‰   c              #   ó  K  — | j                   j                  |«      }|€y |rN|d   j                  |k  r<| xj                  dz  c_        |j	                  «       –— |r|d   j                  |k  rŒ<|s| j                   |= y y ­w)Nr   r�   )rc   Úgetr   rd   rƒ   )rj   r‰   r|   r…   s       r    rˆ   z!EvictedClientCloser._drain_lockedû   s€   è ø€ ØŸ™×)Ñ)¨#Ó.ˆØˆ>ØÙ˜ ™×.Ñ.°#Ò5Ø×Ò 1Ñ$ÕØ—.‘.Ó"Ò"ñ ˜ ™×.Ñ.°#Ó5ñ Ø—‘˜cÑ"ð ùs   ‚A.BÁ1Bc                 óR  — t        |«      }|€y 	  |«       }t        j                  |«      sy t	        j
                  «       j                  t        |«      «      }| j                  j                  |«       |j                  | j                  j                  «       y # t        $ r Y y w xY wr&   )r3   rN   rv   Úisawaitabler(   r)   Úcreate_taskrV   ri   rm   Úadd_done_callbackÚdiscard)rj   r,   r2   rR   Útasks        r    r{   zEvictedClientCloser._close  s”   € Ü)¨&Ó1ˆØÐØð	Ù%›ZˆGô ×"Ñ" 7Ô+ØÜ×.Ñ.Ó0×<Ñ<¼^ÈGÓ=TÓUˆØ×Ñ×Ñ˜dÔ#Ø×Ñ˜t×0Ñ0×8Ñ8Õ9øô ò 	Ùð	ús   �B Â	B&Â%B&)r"   N)r   r   r   r   r
   r   ÚtimeÚ	monotonicr   r   r   rk   Úobjectrp   r   rr   rx   r}   Úpropertyr   r   rt   rG   rz   Ú
_BucketKeyr   rˆ   r{   r   r   r    rX   rX   š   s  „ ÙTð  FØ?Ø%)§^¡^ñ	;àð;ð ð;ð ˜˜E˜	Ñ"ð	;ð
 
ó;ð ð ¨Dó ð ð ¨4ó ð
˜vð 
¨$ó 
ó0 ð$ ð#˜sò #ó ð#ð% ð %°$ó %ða  t¡ð a°%ð a¸EÀ-ÐQTÐBTÑ<Uó að
# ð #°%ð #¸HÀ]Ñ<Só #ð:˜Vð :¨ô :r   rX   Údefault_evicted_client_closer)(r   r(   rT   rv   re   r“   r`   Úcollectionsr   Úcollections.abcr   r   r   Údataclassesr   r   Útypingr	   Úlitellm.constantsr
   r   r   r   r   Ústrr   r—   r   r$   r+   r•   r3   r9   r   r>   rJ   rQ   rV   rX   r˜   r   r   r    ú<module>rŸ      s=  ðò!óF Û Û Û Û Û Ý ß 9Ñ 9ß *Ý ÷ð
 0Ð �EÓ /Ø5Ð �uÓ 5à�3‰Y€
ñ �$˜dÔ#÷ð ó $ðð"˜ð ¨:ó ð˜# ™*ó ð˜Fð  x°°F°
Ñ';¸dÑ'Bó ð
˜&ð  Vó ð: Fð :¨tó :ð¨ð °D¸4±Kó ð fð °ó ð. )¨FÑ"3ð ¸ó ÷
w:ñ w:ñt (;Ó'<Ð ˜uÔ <r   