
    `i4                        d dl Z d dlZd dlZd dlZd dlmZmZ d dlmZmZ d dl	m
Z
mZmZmZmZmZmZmZ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 d d	lmZmZm Z m!Z! d d
l"m#Z#m$Z$  ej%        e&          Z'e G d d                      Z( ej)                    Z*de(fdZ+d Z,ddZ-ddZ. G d de/          Z0 G d d          Z1	 d dl2m3Z3 e3j4        e1_5        e1j4        e3_4        n # e6$ r e'7                    d           Y nw xY wee8e9e:e#e
f         Z;ee;         Z<ee;         Z=ee=e<f         Z> G d de          Z?dS )    N)	dataclassfield)datetimetimezone)
AnyAsyncIterable	AwaitableCallable	CoroutineIteratorMappingOptionalSetUnion)BackgroundTask)iterate_in_threadpool)MutableHeaders)Response)ReceiveScopeSendMessage)ServerSentEventensure_bytesc                   Z    e Zd ZU dZ ee          Zeej	                 e
d<   dZee
d<   dS )_ShutdownStatezPer-thread state for shutdown coordination.

    Issue #152 fix: Uses threading.local() instead of ContextVar to ensure
    one watcher per thread rather than one per async context.
    )default_factoryeventsFwatcher_startedN)__name__
__module____qualname____doc__r   setr   r   anyioEvent__annotations__r   bool     e/home/jaya/work/projects/VOICE-AGENT/VIET/agent-env/lib/python3.11/site-packages/sse_starlette/sse.pyr   r   !   sR            %uS999FC999!OT!!!!!r*   r   returnc                  j    t          t          dd          } | t                      } | t          _        | S )z4Get or create shutdown state for the current thread.shutdown_stateN)getattr_thread_stater   r.   )states    r+   _get_shutdown_stater2   1   s1    M#3T::E}  ',$Lr*   c                      	 t          j        t           j                  } t          | d          r| j        }t          |d          r|S n# t
          $ r Y nw xY wdS )am  
    Try to get uvicorn Server instance via signal handler introspection.

    When uvicorn registers signal handlers, they're bound methods on the Server instance.
    We can retrieve the Server from the handler's __self__ attribute.

    Returns None if:
    - Not running under uvicorn
    - Signal handler isn't a bound method
    - Any introspection fails
    __self__should_exitN)signal	getsignalSIGTERMhasattrr4   	Exception)handlerservers     r+   _get_uvicorn_serverr=   :   sp    "6>227J'' 	%Fv}--    4s   AA
 

AAc                  X  K   t                      } t                      }	 	 t          j        rn=t          j        r||j        rdt          _        nt          j        d           d{V  Jt          | j                  D ]}|	                                 	 d| _
        dS # d| _
        w xY w)ag  
    Poll for shutdown and broadcast to all events in this context.

    One watcher runs per thread (event loop). Checks two shutdown sources:
    1. AppStatus.should_exit - set when our monkey-patch works
    2. uvicorn Server.should_exit - via signal handler introspection (Issue #132 fix)

    When either becomes True, signals all registered events.
    TNg      ?F)r2   r=   	AppStatusr5   enable_automatic_graceful_drainr%   sleeplistr   r$   r   )r1   uvicorn_serverevents      r+   _shutdown_watcherrE   Q   s        !!E(**N&	#$  9".". / )-	%+c"""""""""	# %,'' 	 	EIIKKKK	 !&%%%%s   A6B   	B)c                      t                      } | j        sVd| _        	 t          j                    }|                    t                                 dS # t          $ r d| _        Y dS w xY wdS )zDEnsure the shutdown watcher is running for this thread (event loop).TFN)r2   r   asyncioget_running_loopcreate_taskrE   RuntimeError)r1   loops     r+   $_ensure_watcher_started_on_this_looprL   u   s    !!E  * $	*+--D.0011111 	* 	* 	*$)E!!!!	** *s   4A A)(A)c                       e Zd ZdS )SendTimeoutErrorN)r    r!   r"   r)   r*   r+   rN   rN      s        Dr*   rN   c                   x    e Zd ZU dZdZdZdZee         e	d<   e
d             Ze
d             Ze
d             ZdS )	r?   z\Helper to capture a shutdown signal from Uvicorn so we can gracefully terminate SSE streams.FTNoriginal_handlerc                      dt           _        dS )aJ  
        Prevent automatic SSE stream termination on server shutdown.

        WARNING: When disabled, you MUST set AppStatus.should_exit = True
        at some point during shutdown, or streams will never close and the
        server will hang indefinitely (or until uvicorn's graceful shutdown
        timeout expires).
        FNr?   r@   r)   r*   r+    disable_automatic_graceful_drainz*AppStatus.disable_automatic_graceful_drain   s     5:	111r*   c                      dt           _        dS )a  
        Re-enable automatic SSE stream termination on server shutdown.

        This restores the default behavior where SIGTERM triggers immediate
        stream draining. Call this to undo a previous call to
        disable_automatic_graceful_drain().
        TNrR   r)   r*   r+   $enable_automatic_graceful_drain_modez.AppStatus.enable_automatic_graceful_drain_mode   s     59	111r*   c                  t    t           j        rdt           _        t           j        t          j        | i | d S d S )NT)r?   r@   r5   rP   )argskwargss     r+   handle_exitzAppStatus.handle_exit   sB    4 	)$(I!%1&777777 21r*   )r    r!   r"   r#   r5   r@   rP   r   r
   r'   staticmethodrS   rU   rY   r)   r*   r+   r?   r?      s         ffK&*#+/hx(///	: 	: \	: 9 9 \9 8 8 \8 8 8r*   r?   )ServerzHUvicorn not installed. Graceful shutdown on server termination disabled.c                      e Zd ZdZdZdZ	 	 	 	 	 	 	 	 	 	 d#deded	ee	e
e
f                  d
e
dee         dee         dee
         deeg ef                  deeg ed         f                  dee         deeeged         f                  ddfdZedeeef         fd            Zej        deeef         ddfd            Zd$deddfdZdeddfdZdeddfdZed%d            Zdeddfd Zd!e dededdfd"Z!dS )&EventSourceResponsezf
    Streaming response that sends data conforming to the SSE (Server-Sent Events) specification.
       
   Ntext/event-streamcontentstatus_codeheaders
media_type
backgroundpingsepping_message_factorydata_sender_callable)NNNsend_timeoutclient_close_handler_callabler,   c                 6   |dvrt          d|           |p| j        | _        t          |t                    r|| _        nt          |          | _        || _        || j        n|| _        || _	        |	| _
        |
| _        t                      }||                    |           |                    dd           d|d<   d|d<   |                     |           || j        n|| _        || _        || _        d	| _        t+          j                    | _        d S )
N)Nr_   
z'sep must be one of: \r\n, \r, \n, got: zCache-Controlzno-storez
keep-alive
ConnectionnozX-Accel-BufferingT)
ValueErrorDEFAULT_SEPARATORrh   
isinstancer   body_iteratorr   rc   re   rf   rj   rk   r   update
setdefaultinit_headersDEFAULT_PING_INTERVALping_intervalri   rl   activer%   Lock
_send_lock)selfrb   rc   rd   re   rf   rg   rh   ri   rj   rk   rl   _headerss                r+   __init__zEventSourceResponse.__init__   s9   & 000P3PPQQQ0$0 g}-- 	@!(D!6w!?!?D&-7-?$//Z$$8!( "##OOG$$$ 	OZ888!-(,$%(###;?<T77T$8!-J**,,r*   c                     | j         S N)_ping_interval)r~   s    r+   rz   z!EventSourceResponse.ping_interval  s    ""r*   valuec                     t          |t          t          f          st          d          |dk     rt	          d          || _        d S )Nzping interval must be intr   z$ping interval must be greater than 0)rt   intfloat	TypeErrorrr   r   )r~   r   s     r+   rz   z!EventSourceResponse.ping_interval  sM    %#u.. 	97888199CDDD#r*   Fforcec                      t          d          )Nz-Compression is not supported for SSE streams.)NotImplementedError)r~   r   s     r+   enable_compressionz&EventSourceResponse.enable_compression  s    !"QRRRr*   sendc                   K    |d| j         | j        d           d{V  | j        2 3 d{V }t          || j                  }t
                              d|           t          j        | j	                  5 } |d|dd           d{V  ddd           n# 1 swxY w Y   |rI|j
        rBt          | j        d          r| j                                         d{V  t                      6 | j        4 d{V  d	| _         |dd
d	d           d{V  ddd          d{V  dS # 1 d{V swxY w Y   dS )zHSend out SSE data to the client as it becomes available in the iterator.zhttp.response.start)typestatusrd   Nz	chunk: %shttp.response.bodyTr   body	more_bodyacloseFr*   )rc   raw_headersru   r   rh   loggerdebugr%   move_on_afterrk   cancel_calledr9   r   rN   r}   r{   )r~   r   datachunkcancel_scopes        r+   _stream_responsez$EventSourceResponse._stream_response  s     d-*+ 
 
 	
 	
 	
 	
 	
 	
 	
 , 	) 	) 	) 	) 	) 	) 	)$ tx00ELLe,,,$T%677 <d15tTT                      
  ) : )4-x88 6,33555555555&((( - ? 	X 	X 	X 	X 	X 	X 	X 	XDK$ 4cPUVVWWWWWWWWW	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	Xs)   C29BB	"B	 D00
D:=D:receivec                    K   | j         rj |             d{V }|d         dk    rEd| _         t                              d           | j        r|                     |           d{V  dS | j         hdS dS )z/Watch for a disconnect message from the client.Nr   zhttp.disconnectFz+Got event: http.disconnect. Stop streaming.)r{   r   r   rl   )r~   r   messages      r+   _listen_for_disconnectz*EventSourceResponse._listen_for_disconnect+  s      k 	#GIIooooooGv"333#JKKK5 F<<WEEEEEEEEE k 	 	 	 	 	r*   c                    K   t           j        rdS t                       t                      } t	          j                    }| j                            |           	 t           j        r	 | j                            |           dS |	                                 d{V  | j                            |           dS # | j                            |           w xY w)z0Wait for shutdown signal via the shared watcher.N)
r?   r5   rL   r2   r%   r&   r   adddiscardwait)r1   rD   s     r+   _listen_for_exit_signalz+EventSourceResponse._listen_for_exit_signal6  s         	F,...#%%	($  L  ''''' **,,L  '''''EL  ''''s   B: B: :Cc                   K   | j         rt          j        | j                   d{V  | j        r|                                 n4t          dt          j        t          j	                   | j
                  }t          || j
                  }t                              d|           | j        4 d{V  | j         r |d|dd           d{V  ddd          d{V  n# 1 d{V swxY w Y   | j         dS dS )zPeriodically send ping messages to keep the connection alive on proxies.
        - frequenccy ca every 15 seconds.
        - Alternatively one can send periodically a comment line (one starting with a ':' character)
        Nzping - )commentrh   zping: %sr   Tr   )r{   r%   rA   r   ri   r   r   nowr   utcrh   r   r   r   r}   )r~   r   sse_ping
ping_bytess       r+   _pingzEventSourceResponse._pingJ  s     
 k 	+d1222222222 ,))+++$Bhl8<&@&@BB    &h99JLLZ000        ; $$8$.)-                                    k 	 	 	 	 	s   6C%%
C/2C/scopec                 &   K   t          j                    4 d{V dt          g t          d         f         ffd}                    | fd                               | fd                               | j                    j        r                     j                                       | fd           ddd          d{V  n# 1 d{V swxY w Y    j                                          d{V  dS dS )a  Entrypoint for Starlette's ASGI contract. We spin up tasks:
        - _stream_response to push events
        - _ping to keep the connection alive
        - _listen_for_exit_signal to respond to server shutdown
        - _listen_for_disconnect to respond to client disconnect
        Ncoroc                 ^   K    |              d {V  j                                          d S r   )r   cancel)r   
task_groups    r+   cancel_on_finishz6EventSourceResponse.__call__.<locals>.cancel_on_finishn  s9      dff'..00000r*   c                  .                                    S r   )r   r~   r   s   r+   <lambda>z.EventSourceResponse.__call__.<locals>.<lambda>r  s    D<Q<QRV<W<W r*   c                  .                                    S r   )r   r   s   r+   r   z.EventSourceResponse.__call__.<locals>.<lambda>s  s    DJJt<L<L r*   c                  .                                    S r   )r   )r   r~   s   r+   r   z.EventSourceResponse.__call__.<locals>.<lambda>{  s    $*E*Eg*N*N r*   )r%   create_task_groupr
   r	   
start_soonr   rj   rf   )r~   r   r   r   r   r   s   ` `` @r+   __call__zEventSourceResponse.__call__e  s      *,, 	 	 	 	 	 	 	
1Xb)D/6I-J 1 1 1 1 1 1 !!"24W4W4W4W4WXXX!!"24L4L4L4L4LMMM!!"2D4PQQQ( A%%d&?@@@ !! "N"N"N"N"N  	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	$ ?&//########### '&s   B+C
C'*C')
r`   Nra   NNNNNNN)Fr,   N)"r    r!   r"   r#   ry   rs   ContentStreamr   r   r   strr   r
   r   r   r   r   r	   r   propertyr   rz   setterr(   r   r   r   r   r   rZ   r   r   r   r   r)   r*   r+   r]   r]      s         
 /3-/3"!HL (, !;' ;';' ;' '#s(+,	;'
 ;' ^,;' sm;' c];' 'xO0C'DE;' 'R#3445
;' uo;' (0gY	$/0(
;'" 
#;' ;' ;' ;'z #uS%Z0 # # # X# $5e#4 $ $ $ $ $S S S S S S SX4 XD X X X X6	G 	 	 	 	 	 ( ( ( \(&     6$E $G $4 $D $ $ $ $ $ $r*   r]   r   )@rG   loggingr6   	threadingdataclassesr   r   r   r   typingr   r   r	   r
   r   r   r   r   r   r   r%   starlette.backgroundr   starlette.concurrencyr   starlette.datastructuresr   starlette.responsesr   starlette.typesr   r   r   r   sse_starlette.eventr   r   	getLoggerr    r   r   localr0   r2   r=   rE   rL   TimeoutErrorrN   r?   uvicorn.mainr[   rY   rP   ImportErrorr   r   bytesdictContentSyncContentStreamAsyncContentStreamr   r]   r)   r*   r+   <module>r      sL          ( ( ( ( ( ( ( ( ' ' ' ' ' ' ' '                         / / / / / / 7 7 7 7 7 7 3 3 3 3 3 3 ( ( ( ( ( ( 9 9 9 9 9 9 9 9 9 9 9 9 = = = = = = = = 
	8	$	$ " " " " " " " "  	!!^      .!& !& !& !&H
* 
* 
* 
*	 	 	 	 	| 	 	 	#8 #8 #8 #8 #8 #8 #8 #8L######!'!3I".F   
LLR    
 UD/36
7W% "7+ (*;;<C$ C$ C$ C$ C$( C$ C$ C$ C$ C$s   
C) )DD