
    RiL                       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  ee      Zerdd
lmZmZ  G d de      Z ed      Z  G d dejB                  ee          Z" G d de"d         Z# G d de"ejH                  jJ                           Z& G d de"e'ejH                  jP                  ejH                  jR                  z              Z*y)zSEP-1686 client Task classes.    )annotationsN)	AwaitableCallable)datetimetimezone)TYPE_CHECKINGGenericTypeVar)GetTaskResultTaskStatusNotification)MessageMessageHandler)
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     z/home/jay/workspace/.worktrees/task-2117-dev1/scripts/.codegraph-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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.)r8   r5   sessionRuntimeError)r   _es      r   _check_client_connectedzTask._check_client_connectedW   sL     	$$A 	O 	s   & 	A ;A c                    | j                   S )zGet the task ID.)r6   r   s    r   r=   zTask.task_idh   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
        )r8   rF   s    r   returned_immediatelyzTask.returned_immediatelym   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)r9   r:   setr;   inspectisawaitableasynciocreate_task	Exceptionloggerwarning)r   statuscallbackresultrC   s        r   _handle_status_notificationz Task._handle_status_notificationw   s     $ )""$ .. 	KHK!&)&&v.''/	K  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   rT   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  )taskIdrS   	createdAtlastUpdatedAtttlpollInterval)rD   r8   r   nowr   utcr   r6   r9   r5   get_task_status)r   ra   cacheds      r   rS   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 rF   s    r   rU   zTask.result   s      	s   g     r@)statetimeoutc          	     `  K   | j                          | j                  r| j                          d{   S | j                  t	        j
                         | _        t        j                         }h d}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 ('submitted', 'working', 'completed', 'failed').
                   If None, waits for any terminal state (completed/failed)
            timeout: Maximum time to wait in seconds

        Returns:
            GetTaskResult: Final task status

        Raises:
            TimeoutError: If desired state not reached within timeout
        N>   failed	cancelledr[   g      ?zTask z did not reach zterminal statez within s)rh   )rD   r8   rS   r:   rN   Eventtimer9   TimeoutErrorr6   wait_forwaitminclearr5   rc   )	r   rg   rh   startterminal_statespoll_intervalcurrentelapsed	remainings	            r   rq   z	Task.wait   s    ( 	$$&&& %!(D		>!!,,33=/1#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   | 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)r8   rD   r5   cancel_taskr6   r9   rF   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.)rU   	__await__rF   s    r   r~   zTask.__await__!  s    {{}&&((r   r   )r   r   r=   strr>   zTaskResultT | None)r)   r*   )r)   r   )r)   bool)rS   r   r)   r*   )rT   z1Callable[[GetTaskResult], None | Awaitable[None]]r)   r*   )r)   r   )r)   r1   )rg   z
str | Nonerh   floatr)   r   )r+   r,   r-   r.   r   rD   propertyr=   rH   rV   rY   rS   abcabstractmethodrU   rq   r|   r~   rf   r   r   r3   r3   /   s    
  04	77 7 -	76"   " "K40C0 
04"< 	  &*E=W"=W49=W	=W~"")r   r3   c                  >     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                6    t         |   |||       || _        y)a  
        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
        N)r   r   
_tool_name)r   r   r=   	tool_namer>   r   s        r   r   zToolTask.__init__=  s      	*:;#r   c                  K   | j                   | j                   S | j                  r| j                  J | j                  }n| j                          | j	                          d{    | j
                  j                  | j                         d{   }t        |t              rZt        j                  j                  j                  |      }| j
                  j                  | j                  |d       d{   }nt        |t        j                  j                        r1| j
                  j                  | j                  |d       d{   }nt!        |d      r}t!        |d      rqt        j                  j                  |j"                  |j$                  |j&                        }| j
                  j                  | j                  |d       d{   }n|}|| _         |S 7 7 [7 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)
        NT)raise_on_errorcontentstructured_content)r   structuredContent_meta)r<   r8   r7   rD   rq   r5   get_task_resultr6   r!   dictr"   r#   r   model_validate_parse_call_tool_resultr   hasattrr   r   meta)r   rU   
raw_result
mcp_results       r   rU   zToolTask.resultP  s     *&&&))555++F ((* ))+  $||;;DMMJJJ *d+ YY55DDZP
#||CCOOZ  D    J		(@(@A#||CCOOZ  D   
 :y1g 47 "%!9!9 * 2 2*4*G*G(oo ": "J
 $(<<#G#GD $H $ F
 (F %I  K
s]   A$G7&G+',G7G.A(G7<G1=AG7G3BG7G5G7.G71G73G75G7r   )r   r   r=   r   r   r   r>   zCallToolResult | None)r)   r   r+   r,   r-   r.   r   rU   r/   r0   s   @r   r   r   &  s=    6 37$$ $ 	$
 0$&9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<   r8   r7   rD   rq   r5   r   r6   r"   r#   GetPromptResultr   )r   rU   r   s      r   rU   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   r0   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<   r8   r7   rD   rq   r5   r   r6   r!   r"   r#   ReadResourceResultlistr   r   rX   BlobResourceContentsr   TextResourceContents)r   rU   r   parsed_contentsitems        r   rU   zResourceTask.result  sw     *&&&))555++F ((* ))+  $||;;DMMJJJ *cii&B&BCj112J-*
2J"$&z2 5D!$-!T>+22 #		 > > M Md S ,22 #		 > > M Md S (..t45 ) (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   r0   s   @r   r   r     sE    	&   	
,6	N6r   r   )+r.   
__future__r   r   rN   rL   rn   r   collections.abcr   r   r   r   typingr   r	   r
   	mcp.typesr"   r   r   fastmcp.client.messagesr   r   fastmcp.utilities.loggingr   r+   rQ   fastmcp.client.clientr   r   r   r1   ABCr3   r   r#   r   r   r   r   r   r   rf   r   r   <module>r      s    # " 
     / ' 2 2  ; ; 0	H	<(n ($ m$t)377GK( t)nct$% cL>cii//0 >BZcii,,syy/M/MM	NOZr   