
    kKj,                       U d Z ddlmZ ddlZddl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 	 dd	lmZ erddlmZ ddlmZ ddlmZ  ej2                  e      Zd(dZ edd       G d d             Zd)dZ edd       G d d             Z e	dd      Z de!d<   d*dZ"d+dZ#d(dZ$ e       fd,dZ%i Z&de!d<   d-d Z'd.d!Z( e       Z)d"e!d#<   d$Z*d/d%Z+d0d&Z,y# e$ r d'd
ZY w xY w)1a%  Task context and scoping for background task execution.

Determines authorization scope (``get_task_scope``), manages the context
snapshot that is captured at task submission and restored in workers
(``TaskContextSnapshot``), and maintains in-process registries for live
sessions and servers.
    )annotationsN)OrderedDict)
ContextVar)	dataclass)TYPE_CHECKING)parse_task_keytask_redis_prefix)TaskKeyc                      y)N  r       m/Users/ahmed/devFolder/Ultron/claude-voice/.venv/lib/python3.12/site-packages/fastmcp/server/tasks/context.pyr
   r
      s    
 r   )Docket)ServerSession)FastMCPc                     ddl m}   |        }|y|j                  r|j                  j                  d      nd}|r|j                   d| S |j                  S )uT  Get the authorization scope for task isolation.

    Returns the raw scope identifier for the current access token, or
    ``None`` when no auth context is present (anonymous tasks).

    The scope is composed as ``client_id|sub`` when the token carries a
    ``sub`` claim — necessary for fixed-OAuth servers where ``client_id`` is
    shared across all users — and falls back to ``client_id`` alone for
    DCR/CIMD flows where the client identity is already per-user.

    Encoding for Redis/Docket keys happens at the boundary in ``keys.py``;
    this function returns the raw value.
    r   )get_access_tokenNsub|)fastmcp.server.dependenciesr   claimsget	client_id)r   tokenr   s      r   get_task_scoper   *   sV     =E}%*\\%,,

5
!tC
//"!C5))??r   T)frozenslotsc                  (    e Zd ZU dZded<   	 ded<   y)TaskContextInfozInformation about the current background task context.

    Returned by ``get_task_context()`` when running inside a Docket worker.
    Contains identifiers needed to communicate with the MCP session.
    strtask_id
str | None
task_scopeN)__name__
__module____qualname____doc____annotations__r   r   r   r    r    C   s     L2Pr   r    c                     ddl m}   |        syddlm} 	 |j	                         }t        |j                        }t        |d   |d         S # t        $ r Y yt        t        f$ r Y yw xY w)aJ  Get the current task context if running inside a background task worker.

    This function extracts task information from the Docket execution context.
    Returns None if not running in a task context (e.g., foreground execution).

    Returns:
        TaskContextInfo with task_id and task_scope, or None if not in a task.
    r   )is_docket_availableN)current_executionclient_task_idr$   )r"   r$   )r   r+   docket.dependenciesr,   r   r   keyr    LookupError
ValueErrorKeyError)r+   r,   	execution	key_partss       r   get_task_contextr5   R   sw     @ 5
%))+	"9==1	./ .
 	
  ! s   7A 	A*A*)A*c                      e Zd ZU dZdZded<   dZded<   dZded<   dZded<   e	dd	       Z
e	dd
       ZddZ	 	 	 	 	 	 	 	 	 	 ddZy)TaskContextSnapshotzAll context data snapshotted at task-submission time.

    Stored as a single Redis key per task, restored once in the worker.
    Nr#   access_token_jsonzdict[str, str] | Nonehttp_headersorigin_request_id
session_idc                   ddl m}m}m}  |       } |       }|j                  }	 |j
                  } | |r|j                         nd |d      xs d|t        |j                        |      S d|      S # t        $ r d}Y Sw xY w)z6Capture current context for background task execution.r   )r   get_contextget_http_headersNT)include_allr8   r9   r:   r;   )
r   r   r=   r>   request_contextr;   RuntimeErrormodel_dump_jsonr!   
request_id)clsr   r=   r>   access_tokenctxrA   r;   s           r   capturezTaskContextSnapshot.capture{   s    	
 	
 ()m--	J 2>,,.D)d;Ct3B3NO../!	
 		
 UY!	
 		
  	J	s   A8 8BBc                   t        |t              r|j                         }t        j                  |      }|j                  d      }t        |t              rC|j                         D ci c](  \  }}t        |      j                         t        |      * }}} | |j                  d      ||j                  d      |j                  d            S c c}}w )z&Deserialize from JSON stored in Redis.r9   r8   r:   r;   r@   )

isinstancebytesdecodejsonloadsr   dictitemsr!   lower)rE   rawparsedheaderskvs         r   	from_jsonzTaskContextSnapshot.from_json   s     c5!**,CC**^,gt$:A--/J/$!Qs1v||~s1v-/GJ$jj)<= $jj)<=zz,/	
 	
 Ks   *-Cc                    t        j                  | j                  | j                  | j                  | j
                  d      S )z$Serialize to JSON for Redis storage.r@   )rM   dumpsr8   r9   r:   r;   )selfs    r   to_jsonzTaskContextSnapshot.to_json   s:    zz%)%;%; $ 1 1%)%;%;"oo	
 	
r   c                (  K   |j                  t        |       d| d      }|j                         4 d{   }|j                  || j	                         |       d{    ddd      d{    y7 ?7 7 	# 1 d{  7  sw Y   yxY ww)z*Store this snapshot as a single Redis key.:	:snapshotN)ex)r/   r	   redissetr[   )rZ   docketr$   r"   ttl_secondsr/   r`   s          r   savezTaskContextSnapshot.save   so      jj-j9:!G9INO<<>>U))CK)@@@ ">>@ ">>>sV   5BA7B&A=!A9"A=&B1A;2B9A=;B=BBBB)returnr7   )rR   zstr | bytesre   r7   re   r!   )
rb   r   r$   r#   r"   r!   rc   intre   None)r%   r&   r'   r(   r8   r)   r9   r:   r;   classmethodrH   rW   r[   rd   r   r   r   r7   r7   o   s    
 %)z(*.L'.$(z(!J
!
 
4 
 
	

A
A 
A 	
A
 
A 

Ar   r7   task_snapshot)defaultz2ContextVar[tuple[str, TaskContextSnapshot] | None]_task_snapshotc                2    t         j                  | |f       y)zBind a snapshot to the current asyncio context under ``task_id``.

    Nothing outside this task's context sees it; stale entries left in a
    reused context are ignored on recall.
    N)rl   ra   )r"   snapshots     r   _remember_snapshotro      s     *+r   c                H    t         j                         }||\  }}|| k(  r|S y)zReturn the snapshot bound for ``task_id`` in the current context.

    Returns ``None`` if nothing is bound, or if the bound entry belongs to
    a different task (a stale leftover from a reused asyncio context).
    N)rl   r   )r"   cachedcached_task_idrn   s       r   _recall_snapshotrs      s2     !F#) W$Or   c                 f    t               } | yt        | j                        }|r|j                  S dS )zGet the session_id for the current background task, if available.

    Reads the cached snapshot set by the worker-level restore dependency.
    Returns None if not in a task context or the snapshot wasn't restored.
    N)r5   rs   r"   r;   )	task_inforn   s     r   get_task_session_idrv      s8     !"I	 1 12H"*844r   c           	     `  K   	 t        |       }ddlm}m} 	  |       j
                  }||j                         }|y|d   }|d   }	 |j                         4 d{   }|j                  |j                  t        |       d| d             d{   }ddd      d{    yt        |t        j                  |             y# t        $ r Y yw xY w# t        $ r d}Y w xY w7 7 Y7 K# 1 d{  7  sw Y   [xY w# t        $ r t        j!                  d| d	
       Y yw xY ww)u0  Worker-level Docket dependency that restores the task-context snapshot.

    Runs before each fastmcp-owned task, populating the snapshot ContextVar
    so user code — and any task-scoped dependency like ``_CurrentContext`` —
    sees a ready snapshot without touching Redis itself.  All Redis I/O
    goes through Docket's async client, so cluster URLs and the memory://
    backend work transparently (#3897).  Failures are non-fatal: the task
    still runs, and sync helpers return ``None`` as they would have before
    the snapshot was captured.
    Nr   )_current_docket
get_serverr$   r-   r]   r^   z&Failed to restore task snapshot for %sT)exc_info)r   r1   r   rx   ry   _docketrB   r   r`   r/   r	   ro   r7   rW   	Exception_loggerwarning)	r/   partsrx   ry   rb   r$   r"   r`   rR   s	            r   restore_task_snapshotr      s3    s#
 H%% ~ $$&~|$J$%G	V<<>>U		

/
;<AgYiPQ C "> ;7$7$A$A#$FG1     " ">>>  V@#PTUVs   D.C D.C D.	D C,D !3C2C.C2D $C0%D +D.,D D.	CD.CD.C)&D.(C))D.,D .C20D 2D8C;9D D !D+(D.*D++D.z%dict[str, weakref.ref[ServerSession]]_task_sessionsc                <    t        j                  |      t        | <   y)zRegister a session for in-process background task access.

    Called automatically when a task is submitted to Docket. The session is
    stored as a weakref so it doesn't prevent garbage collection when the
    client disconnects.
    N)weakrefrefr   )r;   sessions     r   register_task_sessionr     s     ")W!5N:r   c                t    t         j                  |       }|y |       }|t         j                  | d       |S )u   Get a registered session by ID if still alive.

    Returns None in distributed workers where the session lives in another
    process — callers must handle this gracefully.
    N)r   r   pop)r;   r   r   s      r   get_task_sessionr   (  s=     

Z
(C
{eG:t,Nr   z&OrderedDict[str, weakref.ref[FastMCP]]_task_server_mapi'  c                    t        j                  |      t        | <   t        t              t        kD  r.t        j                  d       t        t              t        kD  r-yy)zRegister the server for a background task.

    Called at task-submission time so that background workers can resolve
    the correct (child) server for mounted tasks.
    F)lastN)r   r   r   len_TASK_SERVER_MAP_MAX_SIZEpopitem)r"   servers     r   register_task_serverr   ;  sI     !(F 3W

";
;  e , 
";
;r   c                t    t         j                  |       }|y |       }|t         j                  | d       |S )z@Get the registered server for a background task, if still alive.N)r   r   r   )r"   r   r   s      r   get_task_serverr   F  s;    


w
'C
{UF~Wd+Mr   rf   )re   r#   )re   zTaskContextInfo | None)r"   r!   rn   r7   re   rh   )r"   r!   re   zTaskContextSnapshot | None)r/   r!   re   rh   )r;   r!   r   r   re   rh   )r;   r!   re   zServerSession | None)r"   r!   r   r   re   rh   )r"   r!   re   zFastMCP | None)-r(   
__future__r   rM   loggingr   collectionsr   contextvarsr   dataclassesr   typingr   fastmcp.server.tasks.keysr   r	   rb   r
   ImportErrorr   mcp.server.sessionr   fastmcp.server.serverr   	getLoggerr%   r}   r   r    r5   r7   rl   r)   ro   rs   rv   r   r   r   r   r   r   r   r   r   r   r   <module>r      s+   #    # " !   G	 0-
'

H
%2 $d#Q Q $Q: $d#KA KA $KA` FPTFB 
,
5 ,39 'Vd 9;5 :6 <G= 8 H" -_	  s   C% %	C10C1