
    ՑiYJ                        S SK r S SKrS SKrS SKrS SKJrJrJr  S SK	J
r
Jr  S SKJr  SSKJr  SSKJr  / SQr " S	 S
\
5      rS r " S S5      rSqS rS rS rSS\4S jjrSS jr\" S5       " S S\5      5       rg)    N)AnyDictOptional)	BaseModelmodel_validator)Literal   )logging)class_requires_deps)fastdeploy-servervllm-serversglang-servermlx-vlm-serverllama-cpp-serverc                       \ rS rSr% Sr\S   \S'   Sr\\	   \S'   Sr
\\S'   Sr\\\	\4      \S	'   \" S
S9S 5       rSrg)GenAIConfig$   native)r   r   r   r   r   r   backendN
server_url   max_concurrencyclient_kwargsafter)modec                     U R                   [        ;   a/  U R                  c"  [        S[	        U R                   5       S35      eU $ )Nz(`server_url` must not be `None` for the z	 backend.)r   SERVER_BACKENDSr   
ValueErrorreprselfs    e/var/www/html/banglarbhumi/venv/lib/python3.13/site-packages/paddlex/inference/models/common/genai.pycheck_server_urlGenAIConfig.check_server_url1   sA    <<?*t/F:4;M:NiX       )__name__
__module____qualname____firstlineno__r   r   __annotations__r   r   strr   intr   r   r   r   r#   __static_attributes__r&   r%   r"   r   r   $   se     	 W	  !%J$OS.2M8DcN+2'" #r%   r   c                 4    U b  U R                   [        ;   a  gg)NFT)r   r   )genai_configs    r"   need_local_modelr1   :   s    L$8$8O$Kr%   c                   X    \ rS rSrSrS rS rS rSS\4S jjr	S r
S	 r\S
 5       rSrg)_AsyncThreadManagerA   a  
Manages an asyncio event loop running in a dedicated background thread.

This class provides a bridge between synchronous code and async operations,
allowing sync code to submit coroutines to be executed in the background
event loop.

Thread Safety:
    - Only `run_async()` and `stop()` are designed to be called from other threads
    - All internal asyncio operations are executed within the event loop thread
    - Uses `run_coroutine_threadsafe()` and `call_soon_threadsafe()` for cross-thread
      communication as recommended by Python documentation

Lifecycle:
    1. `start()` - Creates a daemon thread running `loop.run_forever()`
    2. `run_async(coro)` - Submits coroutines to the loop (returns Future)
    3. `stop(timeout)` - Gracefully shuts down: waits for tasks, cancels remaining,
       then cleans up resources

Example:
    manager = _AsyncThreadManager()
    manager.start()
    future = manager.run_async(some_async_function())
    result = future.result(timeout=10)
    manager.stop()
c                     S U l         S U l        SU l        [        R                  " 5       U l        [        R                  " 5       U l        SU l        g )NF)loopthreadstopped	threadingEvent_event_start_event_cleanup_done_shutting_downr    s    r"   __init___AsyncThreadManager.__init__]   s=    9=	26%OO-#,??#4 #r%   c                 ^  ^  T R                  5       (       a  gST l        ST l        T R                  R	                  5         T R
                  R	                  5         U 4S jn[        R                  " USS9T l        T R                  R                  5         T R                  R                  5         g)a	  
Start the background event loop thread.

This method is idempotent - calling it multiple times has no effect
if the loop is already running.

The method blocks until the event loop is fully initialized and ready
to accept tasks (synchronized via threading.Event).
NFc                    > [         R                  " 5       T l        [         R                  " T R                  5        T R                  R                  5          T R                  R                  5         T R                  5         T R                  R                  5         ST l	        g ! T R                  5         T R                  R                  5         ST l	        f = f)NT)
asyncionew_event_loopr6   set_event_loopr;   setrun_forever_cleanup_loop_internalr<   r8   r    s   r"   	_run_loop,_AsyncThreadManager.start.<locals>._run_loopz   s    ..0DI""499-!!#	$		%%'
 ++-((,,.# ++-((,,.#s   B# #3CT)targetdaemon)
is_runningr=   r8   r;   clearr<   r9   Threadr7   startwait)r!   rH   s   ` r"   rO   _AsyncThreadManager.startg   s     ?? $!  &&(	$"  &&iE r%   c                 r   U R                   c  g [        R                  " U R                   5      nU(       ak  [        R                  " S[        U5       S35        U H  nUR                  5         M     U R                   R                  [        R                  " USS065        U R                   R                  U R                   R                  5       5        [        U R                   S5      (       a3  U R                   R                  U R                   R                  5       5        U R                   R                  5         [        R                  " S5        g! [         a#  n[        R                  " SU 35         SnANYSnAff = f! U R                   R                  5         [        R                  " S5        f = f)	a  
Perform cleanup operations within the event loop thread.

IMPORTANT: This method MUST be called from the event loop thread
(i.e., in the finally block of _run_loop) because asyncio operations
like `all_tasks()`, `task.cancel()` are NOT thread-safe.

Cleanup sequence:
1. Cancel all remaining tasks
2. Wait for cancellation to complete
3. Shutdown async generators (prevents ResourceWarning)
4. Shutdown default executor (Python 3.9+)
5. Close the event loop
NzCancelling z pending tasks during cleanupreturn_exceptionsTshutdown_default_executorz!Error during event loop cleanup: zEvent loop closed successfully)r6   rB   	all_tasksr
   debuglencancelrun_until_completegathershutdown_asyncgenshasattrrT   	Exceptionwarningclose)r!   pendingtaskes       r"   rG   *_AsyncThreadManager._cleanup_loop_internal   s;    99	<''		2GCL>9VWX $DKKM $
 		,,NNGDtD II(()E)E)GH tyy"=>>		,,TYY-P-P-RS
 IIOOMM:;	  	EOO?sCDD	E IIOOMM:;s*   DE 
FE<7F <FF 2F6timeoutc                 ^  ^ ^ T R                  5       (       d  gST l        U U4S jn [        R                  " U" 5       T R                  5      nUR                  TS-   S9   T R                  R                  T R                  R                  5        T R                  R!                  S	S9nU(       d  [        R                  " S
5        T R"                  bN  T R"                  R%                  SS9  T R"                  R'                  5       (       a  [        R                  " S5        ST l        ST l        g! [        R                  R                   a!    [        R                  " STS-    S35         GN[         a$  n[        R                  " SU 35         SnAGN6SnAff = f! [         a     GNf = f)a!  
Gracefully stop the event loop.

This method performs a graceful shutdown sequence:
1. Sets shutting_down flag to reject new tasks
2. Schedules a graceful shutdown coroutine in the event loop that:
   - Waits for pending tasks to complete (with timeout)
   - Cancels tasks that don't complete in time
3. Signals the loop to stop
4. Waits for cleanup to complete in the loop thread
5. Joins the background thread

Args:
    timeout: Maximum seconds to wait for pending tasks to complete.
            Tasks not completed within this time will be cancelled.
            Default is 5.0 seconds.

Thread Safety:
    This method is safe to call from any thread. It uses only thread-safe
    mechanisms (run_coroutine_threadsafe, call_soon_threadsafe, Events)
    to communicate with the event loop thread.
NTc                    >#    [         R                  " 5       n [         R                  " TR                  5       Vs/ s H"  nXLd  M	  UR	                  5       (       a  M   UPM$     nnU(       d  [
        R                  " S5        g[
        R                  " S[        U5       ST S35        [         R                  " UT[         R                  S9I Sh  vN u  p4U(       a]  [
        R                  " S[        U5       ST S	35        U H  nUR                  5         M     [         R                  " US
S06I Sh  vN   [
        R                  " S5        gs  snf  N N"7f)z
Graceful shutdown coroutine that runs inside the event loop.

All asyncio operations here are thread-safe because this coroutine
executes in the event loop thread.
z,No pending tasks to wait for during shutdownNzGraceful shutdown: waiting for z pending tasks (timeout=zs))rd   return_whenzGraceful shutdown: cancelling z$ tasks that did not complete within srS   Tz%Graceful shutdown coroutine completed)rB   current_taskrU   r6   doner
   rV   rW   rP   ALL_COMPLETEDr^   rX   rZ   )ri   tr`   rj   still_pendingra   r!   rd   s         r"   _graceful_shutdown4_AsyncThreadManager.stop.<locals>._graceful_shutdown   s:     #//1L !**49955A( 12 5   LMMM1#g, @#9B( )0g6K6K) #D 4S5G4H I44;9A?
 *DKKM *
 nnmLtLLLMMABC # Ms<   8EEEE$A-EEA%E7E8!EEg       @rd   z"Graceful shutdown timed out after zs, forcing loop stopz Error during graceful shutdown:       @z-Event loop cleanup did not complete within 5szYBackground thread did not terminate in time. Some resources may not be properly released.)rL   r=   rB   run_coroutine_threadsafer6   result
concurrentfuturesTimeoutErrorr
   r^   r]   call_soon_threadsafestopRuntimeErrorr<   rP   r7   joinis_alive)r!   rd   rn   futurerb   cleanup_completeds   ``    r"   rx   _AsyncThreadManager.stop   sq   .    #*	CX	D556H6JDIIVF MM'C-M0	II**499>>: !4499#9F OOKL ;;"KKS){{##%%C
 	= !!.. 	OO4Ws]O D$ $  	DOO>qcBCC	D  		s/   8D0 !/F 0;F.	F7FF
F,+F,c                     U R                  5       (       d  [        S5      eU R                  (       a  [        S5      e[        R                  " XR
                  5      nU$ )a)  
Submit a coroutine to be executed in the background event loop.

This is the primary method for bridging sync and async code.
The coroutine will be scheduled to run in the background thread's
event loop.

Args:
    coro: A coroutine object to be executed

Returns:
    concurrent.futures.Future: A future that can be used to:
        - Wait for the result: future.result(timeout=...)
        - Check completion: future.done()
        - Cancel the task: future.cancel()

Raises:
    RuntimeError: If the event loop is not running or is shutting down

Thread Safety:
    This method is safe to call from any thread. It uses
    `asyncio.run_coroutine_threadsafe()` which is explicitly
    documented as thread-safe.

Example:
    future = manager.run_async(fetch_data())
    result = future.result(timeout=30)
zEvent loop is not runningzxEvent loop is shutting down, cannot accept new tasks. Please ensure all async operations complete before calling stop().)rL   ry   r=   rB   rr   r6   )r!   coror|   s      r"   	run_async_AsyncThreadManager.run_async3  sV    :   :;; U 
 11$		Br%   c                     U R                   SL=(       a7    U R                   R                  5       (       + =(       a    U R                  (       + $ )z
Check if the event loop is currently running and accepting tasks.

Returns:
    bool: True if the loop is running and not closed/stopped
N)r6   	is_closedr8   r    s    r"   rL   _AsyncThreadManager.is_running]  s4     yy$WTYY-@-@-B)BW4<<GWWr%   c                     U R                   $ )z
Check if the event loop is in the process of shutting down.

During shutdown, new tasks will be rejected but existing tasks
are still being processed.

Returns:
    bool: True if shutdown has been initiated
)r=   r    s    r"   is_shutting_down$_AsyncThreadManager.is_shutting_downf  s     """r%   )r<   r;   r=   r6   r8   r7   Nrq   )r'   r(   r)   r*   __doc__r>   rO   rG   floatrx   r   rL   propertyr   r.   r&   r%   r"   r3   r3   A   sG    6$'!R0<doE ob(TX 
# 
#r%   r3   c                  0    [         c
  [        5       q [         $ N)_async_thread_managerr3   r&   r%   r"   get_async_managerr   w  s    $ 3 5  r%   c                  f    [        5       n U R                  5       =(       a    U R                  (       + $ )z
Check if the async event loop is ready to accept tasks.

Returns:
    bool: True if the event loop is running and not shutting down
)r   rL   r   managers    r"   is_aio_loop_readyr   ~  s)      !G@(@(@$@@r%   c                      [        5       n U R                  5       (       d1  U R                  5         [        R                  " U R
                  5        gg)a*  
Start the global async event loop if not already running.

This function also registers an atexit handler to ensure graceful
shutdown when the program exits.

Note:
    The atexit handler calls stop() which performs graceful shutdown,
    waiting for pending tasks to complete before terminating.
N)r   rL   rO   atexitregisterrx   r   s    r"   start_aio_loopr     s9      !G%  r%   rd   c                 b    [        5       nUR                  5       (       a  UR                  U S9  gg)a}  
Gracefully close the global async event loop.

This function initiates a graceful shutdown sequence that:
1. Waits for pending tasks to complete (up to timeout)
2. Cancels any remaining tasks
3. Cleans up resources (async generators, executor)
4. Closes the event loop

Args:
    timeout: Maximum seconds to wait for pending tasks to complete.
            Default is 5.0 seconds.
rp   N)r   rL   rx   )rd   r   s     r"   close_aio_loopr     s.      !GW% r%   c                    [        5       nUR                  5       (       d
  [        5         UR                  5       (       d  [        S5      eUR                  (       a  [        S5      eUR                  U 5      nU(       a  U$  UR                  US9$ ! [        R                  R                   a    [        R                  " SU S35        e [         a  n[        R                  " SU 35        e SnAff = f)a  
Execute a coroutine in the background event loop.

This is the main entry point for running async code from sync contexts.
It automatically starts the event loop if not already running.

Args:
    coro: The coroutine to execute
    return_future: If True, return a Future immediately without waiting.
                  If False (default), block until the coroutine completes.
    timeout: Maximum seconds to wait for completion (only used when
            return_future=False). None means wait indefinitely.

Returns:
    If return_future=True: concurrent.futures.Future
    If return_future=False: The result of the coroutine

Raises:
    RuntimeError: If the event loop fails to start or is shutting down
    concurrent.futures.TimeoutError: If timeout is exceeded
    Exception: Any exception raised by the coroutine

Example:
    # Blocking call
    result = run_async(fetch_data(), timeout=30)

    # Non-blocking call
    future = run_async(fetch_data(), return_future=True)
    # ... do other work ...
    result = future.result()
zFailed to start event loopz4Event loop is shutting down, cannot accept new tasksrp   zTask timed out after z secondszTask failed with error: N)r   rL   r   ry   r   r   rs   rt   ru   rv   r
   r^   r]   error)r   return_futurerd   r   r|   rb   s         r"   r   r     s    @  !G
 788 QRRt$F}}W}--** /yAB 045s   B AC1C,,C1openaic                   X   ^  \ rS rSr S
U 4S jjr\S 5       rSS.S jrS rS r	S	r
U =r$ )GenAIClienti  c                   > SSK Jn  [        TU ]  5         Xl        X0l        Uc  [        U R                  5       SS9nX@l        SU;  a  SUS'   U" SSU0UD6U l	        [        R                  " U R
                  5      U l        g )	Nr   )AsyncOpenAI
   rp   api_keynullbase_urlr&   )r   r   superr>   r   _max_concurrencyr   _get_model_name_model_name_clientrB   	Semaphore
_semaphore)r!   r   r   r   
model_namekwargsr   	__class__s          r"   r>   GenAIClient.__init__  s}     	' /"4#7#7#92FJ%F" &F9"?H??!++D,A,ABr%   c                     U R                   $ r   )r   r    s    r"   openai_clientGenAIClient.openai_client  s    ||r%   Fr   c                J   ^  U 4S jn[        U" ST R                  US.UD6US9$ )Nc                    >#    TR                    IS h  vN   TR                  R                  R                  R                  " U 0 UD6I S h  vN sS S S 5      IS h  vN   $  NM N N	! , IS h  vN  (       d  f       g = f7fr   )r   r   chatcompletionscreate)argsr   r!   s     r"   &_create_chat_completion_with_semaphoreRGenAIClient.create_chat_completion.<locals>._create_chat_completion_with_semaphore  sV     !\\..::AA  ' 'sV   BA"B4A(A$A(BA&B$A(&B(A?.A1/A?;B)modelmessagesr   r&   )r   r   )r!   r   r   r   r   s   `    r"   create_chat_completion"GenAIClient.create_chat_completion  s?    	 2 &&! 
 (
 	
r%   c                 H    [        U R                  R                  5       SS9  g )N   rp   )r   r   r_   r    s    r"   r_   GenAIClient.close  s    $,,$$&2r%   c                    #     U R                   R                  R                  5       I S h  vN nUR
                  S   R                  $  N! [         a  n[	        SU 35      UeS nAff = f7f)Nz@Failed to get the model list from the OpenAI-compatible server: r   )r   modelslistr]   ry   dataid)r!   r   rb   s      r"   r   GenAIClient._get_model_name  sk     	<<..3355F
 {{1~    6 	RSTRUV	s7   A,'A A	A A,	A 
A)A$$A))A,)r   r   r   r   r   )r   N)r'   r(   r)   r*   r>   r   r   r   r_   r   r.   __classcell__)r   s   @r"   r   r     s?     BFC&   AF 
"3! !r%   r   r   )FN)rB   r   concurrent.futuresrt   r9   typingr   r   r   pydanticr   r   typing_extensionsr   utilsr
   
utils.depsr   r   r   r1   r3   r   r   r   r   r   r   r   objectr   r&   r%   r"   <module>r      s        & & / %  .) ,p# p#f	  !	A&$&E &&;| X4!& 4! 4!r%   