
    kKjR                       d Z ddlm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	m
Z
 ddlmZmZ ddlmZmZmZ ddlZddlmZmZ ddlmZmZ dd	lmZ dd
lmZ  ee      ZerddlmZm Z   G d de      Z! ed      Z" G d dejF                  ee"         Z$ G d de$d         Z% G d de$ejL                  jN                           Z( G d de$e)ejL                  jT                  ejL                  jV                  z              Z,y)zSEP-1686 client Task classes.    )annotationsN)	AwaitableCallable)datetimetimezone)TYPE_CHECKINGGenericTypeVar)GetTaskResultTaskStatusNotification)MessageMessageHandler)	ToolError)
get_logger)CallToolResultClientc                  0     e Zd ZdZd fdZd fdZ xZS )TaskNotificationHandlerzEMessageHandler that routes task status notifications to Task objects.c                V    t         |           t        j                  |      | _        y N)super__init__weakrefref_client_ref)selfclient	__class__s     e/Users/ahmed/devFolder/Ultron/claude-voice/.venv/lib/python3.12/site-packages/fastmcp/client/tasks.pyr   z TaskNotificationHandler.__init__   s    07F0C    c                  K   t        |t        j                  j                        rGt        |j                  t
              r-| j                         }|r|j                  |j                         t        | %  |       d{    y7 w)z7Dispatch messages, including task status notifications.N)

isinstancemcptypesServerNotificationrootr   r    _handle_task_status_notificationr   dispatch)r   messager   r   s      r   r(   z TaskNotificationHandler.dispatch"   s`     gsyy;;<',,(>?))+;;GLLIgw'''s   A>B	BB	)r   r   )r)   r   returnNone)__name__
__module____qualname____doc__r   r(   __classcell__r   s   @r   r   r      s    OD( (r    r   TaskResultTc                      e Zd ZdZ	 d	 	 	 	 	 ddZddZedd       Zedd       ZddZ		 	 	 	 ddZ
dd	Zej                  dd
       Zddd	 	 	 	 	 ddZdddZddZd Zy)Taska  
    Abstract base class for MCP background tasks (SEP-1686).

    Provides a uniform API whether the server accepts background execution
    or executes synchronously (graceful degradation per SEP-1686).

    Subclasses:
        - ToolTask: For tool calls (result type: CallToolResult)
        - PromptTask: For prompts (future, result type: GetPromptResult)
        - ResourceTask: For resources (future, result type: ReadResourceResult)
    Nc                x    || _         || _        || _        |du| _        d| _        d| _        g | _        d| _        y)z
        Create a Task wrapper.

        Args:
            client: The FastMCP client
            task_id: The task identifier
            immediate_result: If server executed synchronously, the immediate result
        N)_client_task_id_immediate_result_is_immediate_status_cache_status_event_status_callbacks_cached_result)r   r   task_idimmediate_results       r   r   zTask.__init__=   sP     !1-T9 4837  	 37r    c                    | j                   ry	 | j                  j                  }y# t        $ r}t        d      |d}~ww xY w)zValidate that client context is still active.

        Raises:
            RuntimeError: If accessed outside client context (unless immediate)
        NzoCannot access task results outside client context. Task futures must be used within 'async with client:' block.)r9   r6   sessionRuntimeError)r   _es      r   _check_client_connectedzTask._check_client_connectedX   sL     	$$A 	O 	s   & 	A ;A c                    | j                   S )zGet the task ID.)r7   r   s    r   r>   zTask.task_idi   s     }}r    c                    | j                   S )zCheck if server executed the task immediately.

        Returns:
            True if server executed synchronously (graceful degradation or no task support)
            False if server accepted background execution
        )r9   rG   s    r   returned_immediatelyzTask.returned_immediatelyn   s     !!!r    c                F   || _         | j                  | j                  j                          | j                  D ]5  }	  ||      }t	        j
                  |      rt        j                  |       7 y# t        $ r$}t        j                  d| d       Y d}~ad}~ww xY w)a  Process incoming notifications/tasks/status (internal).

        Called by Client when a notification is received for this task.
        Updates cache, triggers events, and invokes user callbacks.

        Args:
            status: Task status from notification
        NzTask callback error: T)exc_info)r:   r;   setr<   inspectisawaitableasynciocreate_task	Exceptionloggerwarning)r   statuscallbackresultrD   s        r   _handle_status_notificationz Task._handle_status_notificationx   s     $ )""$ ..HK!&)&&v.''/ /  K!6qc:TJJKs   2A33	B <BB c                :    | j                   j                  |       y)a>  Register callback for status change notifications.

        The callback will be invoked when a notifications/tasks/status is received
        for this task (optional server feature per SEP-1686 lines 436-444).

        Supports both sync and async callbacks (auto-detected).

        Args:
            callback: Function to call with GetTaskResult when status changes.
                     Can return None (sync) or Awaitable[None] (async).

        Example:
            >>> task = await client.call_tool("slow_operation", {}, task=True)
            >>>
            >>> def on_update(status: GetTaskResult):
            ...     print(f"Task {status.taskId} is now {status.status}")
            >>>
            >>> task.on_status_change(on_update)
            >>> result = await task  # Callback fires when status changes
        N)r<   append)r   rU   s     r   on_status_changezTask.on_status_change   s    0 	%%h/r    c                r  K   | j                          | j                  r>t        j                  t        j
                        }t        | j                  d||dd      S | j                  | j                  }|S | j                  j                  | j                         d{   | _        | j                  S 7 w)zGet current task status.

        If server executed immediately, returns synthetic completed status.
        Otherwise queries the server for current status.
        	completedNi  )taskIdrT   	createdAtlastUpdatedAtttlpollInterval)rE   r9   r   nowr   utcr   r7   r:   r6   get_task_status)r   rb   cacheds      r   rT   zTask.status   s      	$$&,,x||,C }}"!!  )''FM $(<<#?#?#NN!!! Os   BB7B5 B7c                   K   yw)zWait for and return the task result.

        Must be implemented by subclasses to return the appropriate result type.
        N rG   s    r   rV   zTask.result   s      	s        r@)statetimeoutc          	     ^  K   | j                          | j                  r| j                          d{   S | j                  t	        j
                         | _        t        j                         }dh}d}	 | j                  r9| j                  j                  }|||vr| j                  S ||k(  r| j                  S t        j                         |z
  }||k\  r#t        d| j                   d|xs d d| d      ||z
  }	 t	        j                  | j                  j                         t        ||      	       d{    | j                  j                          7 *7 "# t        j                  $ r6 | j                  j                  | j                         d{  7  | _        Y Nw xY ww)
a  Wait for task to reach a specific state or complete.

        Uses event-based waiting when notifications are available (fast),
        with fallback to polling (reliable). Optimally wakes up immediately
        on status changes when server sends notifications/tasks/status.

        Args:
            state: Desired state ('working', 'input_required', 'completed', 'failed', 'cancelled').
                   If None, waits until the task exits the 'working' state (completed, failed, cancelled, input_required, etc.)
            timeout: Maximum time to wait in seconds

        Returns:
            GetTaskResult: Final task status

        Raises:
            TimeoutError: If desired state not reached within timeout
        Nworkingg      ?zTask z did not reach zterminal statez within srj   )rE   r9   rT   r;   rO   Eventtimer:   TimeoutErrorr7   wait_forwaitminclearr6   rd   )	r   ri   rj   startin_progress_statespoll_intervalcurrentelapsed	remainings	            r   rs   z	Task.wait   s    ( 	$$&&& %!(D		'[!!,,33=&88#111%--- iikE)G'!"DMM?/%:SCS9TT\]d\eefg   ')IW&&&&++-s=)7T   ""((*1  '> '' W+/<<+G+G+V%V%V"WsR   0F-ECF- <E! <E=E! F-E! !;F*F
F*'F-)F**F-c                   K   h d}| j                  |       d{   }|j                  |vr0d| _        | j                  |       d{   }|j                  |vr0|S 7 D7 w)u   Wait until task reaches a terminal state (completed, failed, cancelled).

        Unlike wait(), this will not return on input_required — it continues
        waiting until the task fully resolves. Used internally by result().
        >   failed	cancelledr\   rn   N)rs   rT   r:   )r   rj   terminal_statesrT   s       r   _wait_terminalzTask._wait_terminal  si      ?yyy11mm?2 "&D99W955F	 mm?2
  2
 6s'   A%A!.A%A#A%A%#A%c                   K   | j                   ry| j                          | j                  j                  | j                         d{    d| _        y7 w)aO  Cancel this task, transitioning it to cancelled state.

        Sends a tasks/cancel protocol request. The server will attempt to halt
        execution and move the task to cancelled state.

        Note: If server executed immediately (graceful degradation), this is a no-op
        as there's no server-side task to cancel.
        N)r9   rE   r6   cancel_taskr7   r:   rG   s    r   cancelzTask.cancel   sI      $$&ll&&t}}555! 	6s   AAA	Ac                >    | j                         j                         S )z!Allow 'await task' to get result.)rV   	__await__rG   s    r   r   zTask.__await__1  s    {{}&&((r    r   )r   r   r>   strr?   zTaskResultT | None)r*   r+   )r*   r   )r*   bool)rT   r   r*   r+   )rU   z1Callable[[GetTaskResult], None | Awaitable[None]]r*   r+   )r*   r   )r*   r2   )ri   z
str | Nonerj   floatr*   r   )rh   )rj   r   r*   r   )r,   r-   r.   r/   r   rE   propertyr>   rI   rW   rZ   rT   abcabstractmethodrV   rs   r   r   r   rg   r    r   r4   r4   0   s    
  04	77 7 -	76"   " "K40C0 
04"< 	  &*E=W"=W49=W	=W~"")r    r4   c                  D     e Zd ZdZ	 	 d	 	 	 	 	 	 	 	 	 d fdZddZ xZS )ToolTaskaM  
    Represents a tool call that may execute in background or immediately.

    Provides a uniform API whether the server accepts background execution
    or executes synchronously (graceful degradation per SEP-1686).

    Usage:
        task = await client.call_tool_as_task("analyze", args)

        # Check status
        status = await task.status()

        # Wait for completion
        await task.wait()

        # Get result (waits if needed)
        result = await task.result()  # Returns CallToolResult

        # Or just await the task directly
        result = await task
    c                D    t         |   |||       || _        || _        y)aj  
        Create a ToolTask wrapper.

        Args:
            client: The FastMCP client
            task_id: The task identifier
            tool_name: Name of the tool being executed
            immediate_result: If server executed synchronously, the immediate result
            raise_on_error: Whether task.result() should raise ToolError on errors
        N)r   r   
_tool_name_raise_on_error)r   r   r>   	tool_namer?   raise_on_errorr   s         r   r   zToolTask.__init__M  s&    $ 	*:;#-r    c                X  K   | j                   | j                   S | j                  r| j                  J | j                  }|j                  rF| j                  r9|j
                  rUt        |j
                  d   t        j                  j                        r$|j
                  d   j                  }t        |      d| j                   d}t        |      | j                          | j                          d{    | j                  j!                  | j"                         d{   }t        |t$              rdt        j                  j&                  j)                  |      }| j                  j+                  | j                  || j                         d{   }nt        |t        j                  j&                        r;| j                  j+                  | j                  || j                         d{   }nt-        |d      rt-        |d      r{t        j                  j'                  |j
                  |j.                  |j0                        }| j                  j+                  | j                  || j                         d{   }n|}|| _         |S 7 7 y7 	7 7 w)	a#  Wait for and return the tool result.

        If server executed immediately, returns the immediate result.
        Otherwise waits for background task to complete and retrieves result.

        Returns:
            CallToolResult: The parsed tool result (same as call_tool returns)
        Nr   zTool 'z' returned an error)r   contentstructured_content)r   structuredContent_meta)r=   r9   r8   is_errorr   r   r"   r#   r$   TextContenttextr   r   rE   r   r6   get_task_resultr7   dictr   model_validate_parse_call_tool_resulthasattrr   meta)r   rV   msg
raw_result
mcp_results        r   rV   zToolTask.resultc  sR     *&&&))555++F4#7#7>>jNN1%syy'<'<' !..+00C  n$ #4??"33FGCn$ ((* %%'''  $||;;DMMJJJ *d+ YY55DDZP
#||CCOO#'#7#7  D   
 J		(@(@A#||CCOO#'#7#7  D    :y1g 47 "%!9!9 * 2 2*4*G*G(oo ": "J
 $(<<#G#G"'+';'; $H $ F (F %U ( Ks]   C8J*:J;,J*'J (A2J*J#AJ*9J&:BJ*J(J* J*#J*&J*(J*)NT)
r   r   r>   r   r   r   r?   zCallToolResult | Noner   r   )r*   r   r,   r-   r.   r/   r   rV   r0   r1   s   @r   r   r   6  sK    6 37#.. . 	.
 0. .,Gr    r   r   c                  >     e Zd ZdZ	 d	 	 	 	 	 	 	 d fdZddZ xZS )
PromptTaskac  
    Represents a prompt call that may execute in background or immediately.

    Provides a uniform API whether the server accepts background execution
    or executes synchronously (graceful degradation per SEP-1686).

    Usage:
        task = await client.get_prompt_as_task("analyze", args)
        result = await task  # Returns GetPromptResult
    c                6    t         |   |||       || _        y)a  
        Create a PromptTask wrapper.

        Args:
            client: The FastMCP client
            task_id: The task identifier
            prompt_name: Name of the prompt being executed
            immediate_result: If server executed synchronously, the immediate result
        N)r   r   _prompt_name)r   r   r>   prompt_namer?   r   s        r   r   zPromptTask.__init__  s      	*:;'r    c                  K   | j                   | j                   S | j                  r| j                  J | j                  }n~| j                          | j	                          d{    | j
                  j                  | j                         d{   }t        j                  j                  j                  |      }|| _         |S 7 c7 8w)a#  Wait for and return the prompt result.

        If server executed immediately, returns the immediate result.
        Otherwise waits for background task to complete and retrieves result.

        Returns:
            GetPromptResult: The prompt result with messages and description
        N)r=   r9   r8   rE   r   r6   r   r7   r#   r$   GetPromptResultr   )r   rV   r   s      r   rV   zPromptTask.result  s      *&&&))555++F ((* %%'''  $||;;DMMJJJ YY..==jIF % ( Ks$   A#C%C	&,CC7CCr   )r   r   r>   r   r   r   r?   z mcp.types.GetPromptResult | None)r*   zmcp.types.GetPromptResultr   r1   s   @r   r   r     s>    	  >B(( ( 	(
 ;(&r    r   c                  B     e Zd ZdZ	 d	 	 	 	 	 	 	 d fdZ	 	 ddZ xZS )ResourceTaskaw  
    Represents a resource read that may execute in background or immediately.

    Provides a uniform API whether the server accepts background execution
    or executes synchronously (graceful degradation per SEP-1686).

    Usage:
        task = await client.read_resource_as_task("file://data.txt")
        contents = await task  # Returns list[ReadResourceContents]
    c                6    t         |   |||       || _        y)a  
        Create a ResourceTask wrapper.

        Args:
            client: The FastMCP client
            task_id: The task identifier
            uri: URI of the resource being read
            immediate_result: If server executed synchronously, the immediate result
        N)r   r   _uri)r   r   r>   urir?   r   s        r   r   zResourceTask.__init__  s    & 	*:;	r    c                d  K   | j                   | j                   S | j                  r| j                  J | j                  }n^| j                          | j	                          d{    | j
                  j                  | j                         d{   }t        |t        j                  j                        rt        |j                        }nt        |t              rd|v rg }|d   D ]  }t        |t              rvd|v r9|j                  t        j                  j                   j#                  |             P|j                  t        j                  j$                  j#                  |             |j                  |        |}nt        |t              r|n|g}|| _         |S 7 D7 w)a  Wait for and return the resource contents.

        If server executed immediately, returns the immediate result.
        Otherwise waits for background task to complete and retrieves result.

        Returns:
            list[ReadResourceContents]: The resource contents
        Ncontentsblob)r=   r9   r8   rE   r   r6   r   r7   r"   r#   r$   ReadResourceResultlistr   r   rY   BlobResourceContentsr   TextResourceContents)r   rV   r   parsed_contentsitems        r   rV   zResourceTask.result  su     *&&&))555++F ((* %%'''  $||;;DMMJJJ *cii&B&BCj112J-*
2J"$&z2D!$-!T>+22 #		 > > M Md S ,22 #		 > > M Md S (..t4 3 ) (2*d'C* %? ( Ks%   A$F0&F*',F0F-DF0-F0r   )r   r   r>   r   r   r   r?   zLlist[mcp.types.TextResourceContents | mcp.types.BlobResourceContents] | None)r*   zElist[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]r   r1   s   @r   r   r     sE    	&   	
,6	N6r    r   )-r/   
__future__r   r   rO   rM   rp   r   collections.abcr   r   r   r   typingr   r	   r
   	mcp.typesr#   r   r   fastmcp.client.messagesr   r   fastmcp.exceptionsr   fastmcp.utilities.loggingr   r,   rR   fastmcp.client.clientr   r   r   r2   ABCr4   r   r$   r   r   r   r   r   r   rg   r    r   <module>r      s    # " 
     / ' 2 2  ; ; ( 0	H	<(n ($ m$C)377GK( C)Ltt$% tn>cii//0 >BZcii,,syy/M/MM	NOZr    