
    i'                        S r SSKJr  SSKJrJrJrJr  SSKJ	r	  SSK
Jr  SSKJr  SSKJrJrJrJr  \(       a  SSKJr   " S	 S
\5      r          SS jr " S S\5      r " S S\\\4   5      rg)z$Call tracking middleware for agents.    )annotations)TYPE_CHECKING	AnnotatedAnyLiteral)	AIMessage)UntrackedValue)NotRequired)AgentMiddleware
AgentStatePrivateStateAttrhook_config)Runtimec                  .    \ rS rSr% SrS\S'   S\S'   Srg)	ModelCallLimitState   z`State schema for ModelCallLimitMiddleware.

Extends AgentState with model call tracking fields.
z-NotRequired[Annotated[int, PrivateStateAttr]]thread_model_call_countz=NotRequired[Annotated[int, UntrackedValue, PrivateStateAttr]]run_model_call_count N)__name__
__module____qualname____firstlineno____doc____annotations____static_attributes__r       f/home/james-whalen/.local/lib/python3.13/site-packages/langchain/agents/middleware/model_call_limit.pyr   r      s    
 KJWWr   r   c                    / nUb  X:  a  UR                  SU  SU S35        Ub  X:  a  UR                  SU SU S35        SSR                  U5       3$ )aI  Build a message indicating which limits were exceeded.

Args:
    thread_count: Current thread model call count.
    run_count: Current run model call count.
    thread_limit: Thread model call limit (if set).
    run_limit: Run model call limit (if set).

Returns:
    A formatted message describing which limits were exceeded.
zthread limit (/)zrun limit (zModel call limits exceeded: z, )appendjoin)thread_count	run_countthread_limit	run_limitexceeded_limitss        r   _build_limit_exceeded_messager)       sp    " OL$@~Q|nANO!7YKq1EF)$))O*D)EFFr   c                  D   ^  \ rS rSrSr          SU 4S jjrSrU =r$ )ModelCallLimitExceededError:   zException raised when model call limits are exceeded.

This exception is raised when the configured exit behavior is 'error'
and either the thread or run model call limit has been exceeded.
c                l   > Xl         X l        X0l        X@l        [	        XX45      n[
        TU ]  U5        g)zInitialize the exception with call count information.

Args:
    thread_count: Current thread model call count.
    run_count: Current run model call count.
    thread_limit: Thread model call limit (if set).
    run_limit: Run model call limit (if set).
N)r$   r%   r&   r'   r)   super__init__)selfr$   r%   r&   r'   msg	__class__s         r   r/   $ModelCallLimitExceededError.__init__A   s4     )"("+L\]r   )r%   r'   r$   r&   )
r$   intr%   r4   r&   
int | Noner'   r5   returnNone)r   r   r   r   r   r/   r   __classcell__r2   s   @r   r+   r+   :   sB      !	
  
 r   r+   c                  v   ^  \ rS rSrSr\rSSSS.       SU 4S jjjr\" S/S9SS j5       r	SS	 jr
S
rU =r$ )ModelCallLimitMiddlewareY   a  Tracks model call counts and enforces limits.

This middleware monitors the number of model calls made during agent execution
and can terminate the agent when specified limits are reached. It supports
both thread-level and run-level call counting with configurable exit behaviors.

Thread-level: The middleware tracks the number of model calls and persists
call count across multiple runs (invocations) of the agent.

Run-level: The middleware tracks the number of model calls made during a single
run (invocation) of the agent.

Example:
    ```python
    from langchain.agents.middleware.call_tracking import ModelCallLimitMiddleware
    from langchain.agents import create_agent

    # Create middleware with limits
    call_tracker = ModelCallLimitMiddleware(thread_limit=10, run_limit=5, exit_behavior="end")

    agent = create_agent("openai:gpt-4o", middleware=[call_tracker])

    # Agent will automatically jump to end when limits are exceeded
    result = await agent.invoke({"messages": [HumanMessage("Help me with a task")]})
    ```
Nend)r&   r'   exit_behaviorc                  > [         TU ]  5         Uc  Uc  Sn[        U5      eUS;  a  SU S3n[        U5      eXl        X l        X0l        g)aB  Initialize the call tracking middleware.

Args:
    thread_limit: Maximum number of model calls allowed per thread.
        None means no limit.
    run_limit: Maximum number of model calls allowed per run.
        None means no limit.
    exit_behavior: What to do when limits are exceeded.
        - "end": Jump to the end of the agent execution and
            inject an artificial AI message indicating that the limit was exceeded.
        - "error": Raise a `ModelCallLimitExceededError`

Raises:
    ValueError: If both limits are `None` or if `exit_behavior` is invalid.
Nz@At least one limit must be specified (thread_limit or run_limit))r=   errorzInvalid exit_behavior: z. Must be 'end' or 'error')r.   r/   
ValueErrorr&   r'   r>   )r0   r&   r'   r>   r1   r2   s        r   r/   !ModelCallLimitMiddleware.__init__w   s]    , 	I$5TCS/! 00+M?:TUCS/!("*r   )can_jump_toc                   UR                  SS5      nUR                  SS5      nU R                  SL=(       a    X0R                  :  nU R                  SL=(       a    X@R                  :  nU(       d  U(       ap  U R                  S:X  a   [	        UUU R                  U R                  S9eU R                  S:X  a0  [        X4U R                  U R                  5      n[        US9nSU/S	.$ g)
a  Check model call limits before making a model call.

Args:
    state: The current agent state containing call counts.
    runtime: The langgraph runtime.

Returns:
    If limits are exceeded and exit_behavior is "end", returns
    a Command to jump to the end with a limit exceeded message. Otherwise returns None.

Raises:
    ModelCallLimitExceededError: If limits are exceeded and exit_behavior
        is "error".
r   r   r   Nr@   )r$   r%   r&   r'   r=   )content)jump_tomessages)getr&   r'   r>   r+   r)   r   )	r0   stateruntimer$   r%   thread_limit_exceededrun_limit_exceededlimit_messagelimit_ai_messages	            r   before_model%ModelCallLimitMiddleware.before_model   s      yy!:A>II4a8	 !% 1 1 = c,RcRcBc!^^47WI<W $6!!W,1!-'!%!2!2"nn	  !!U* = T->->! $-]#C #(7G6HIIr   c                X    UR                  SS5      S-   UR                  SS5      S-   S.$ )zIncrement model call counts after a model call.

Args:
    state: The current agent state.
    runtime: The langgraph runtime.

Returns:
    State updates with incremented call counts.
r   r      r   )r   r   )rH   )r0   rI   rJ   s      r   after_model$ModelCallLimitMiddleware.after_model   s7     (-yy1JA'NQR'R$)II.Da$H1$L
 	
r   )r>   r'   r&   )r&   r5   r'   r5   r>   zLiteral['end', 'error']r6   r7   )rI   r   rJ   r   r6   zdict[str, Any] | None)r   r   r   r   r   r   state_schemar/   r   rO   rS   r   r8   r9   s   @r   r;   r;   Y   ss    6 'L
 $( $16"+ !"+ 	"+
 /"+ 
"+ "+H eW%' &'R
 
r   r;   N)
r$   r4   r%   r4   r&   r5   r'   r5   r6   str)r   
__future__r   typingr   r   r   r   langchain_core.messagesr   "langgraph.channels.untracked_valuer	   typing_extensionsr
   !langchain.agents.middleware.typesr   r   r   r   langgraph.runtimer   r   r)   	Exceptionr+   r;   r   r   r   <module>r_      s    * " 9 9 - = )  )X* XGGG G 	G
 	G4) >y
/BC/GH y
r   