
    g3fi-#                        d Z ddlmZ ddlmZmZmZmZ ddlm	Z	 ddl
mZ ddlmZmZ ddlmZmZmZmZ erddlmZ  G d	 d
e      Z	 	 	 	 	 	 	 	 	 	 ddZ G d de      Z G d deeef         Zy)z$Call tracking middleware for agents.    )annotations)TYPE_CHECKING	AnnotatedAnyLiteral)	AIMessage)UntrackedValue)NotRequiredoverride)AgentMiddleware
AgentStatePrivateStateAttrhook_config)Runtimec                  &    e Zd ZU dZded<   ded<   y)ModelCallLimitStatezlState 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_countN)__name__
__module____qualname____doc____annotations__     j/var/www/auto_recruiter/arenv/lib/python3.12/site-packages/langchain/agents/middleware/model_call_limit.pyr   r      s    
 KJWWr   r   c                    g }|| |k\  r|j                  d|  d| d       |||k\  r|j                  d| d| d       ddj                  |       S )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'       st    " OLL$@~Q|nANOi!7YKq1EF)$))O*D)EFFr   c                  8     e Zd ZdZ	 	 	 	 	 	 	 	 	 	 d fdZ xZS )ModelCallLimitExceededErrorzException 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                x    || _         || _        || _        || _        t	        ||||      }t
        |   |       y)a/  Initialize 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,   z$ModelCallLimitExceededError.__init__A   sA     )"("+L)\S\]r   )
r"   intr#   r0   r$   
int | Noner%   r1   returnNone)r   r   r   r   r,   __classcell__r/   s   @r   r)   r)   :   sB      !	
  
 r   r)   c                       e Zd ZdZeZdddd	 	 	 	 	 	 	 d fdZ edg      edd              Z	 edg      	 	 	 	 	 	 dd       Z
edd	       Z	 	 	 	 	 	 dd
Z xZS )ModelCallLimitMiddlewarea^  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         |           ||d}t        |      |dvrd| d}t        |      || _        || _        || _        y)a  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)>   r8   errorzInvalid exit_behavior: z. Must be 'end' or 'error')r+   r,   
ValueErrorr$   r%   r9   )r-   r$   r%   r9   r.   r/   s        r   r,   z!ModelCallLimitMiddleware.__init__w   sc    4 	I$5TCS/! 00+M?:TUCS/!("*r   )can_jump_toc                   |j                  dd      }|j                  dd      }| j                  duxr || j                  k\  }| j                  duxr || j                  k\  }|s|ru| j                  dk(  r#t	        ||| j                  | j                        | j                  dk(  r4t        ||| j                  | j                        }t        |      }d|gd	S y)
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%   r8   )content)jump_tomessages)getr$   r%   r9   r)   r'   r   )	r-   stateruntimer"   r#   thread_limit_exceededrun_limit_exceededlimit_messagelimit_ai_messages	            r   before_modelz%ModelCallLimitMiddleware.before_model   s    $ yy!:A>II4a8	 !% 1 1 = c,RVRcRcBc!^^47WI<W $6!!W,1!-'!%!2!2"nn	  !!U* = )T->->! $-]#C #(7G6HIIr   c                .   K   | j                  ||      S w)a  Async 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'`.
        )rI   r-   rC   rD   s      r   abefore_modelz&ModelCallLimitMiddleware.abefore_model   s     *   00   c                X    |j                  dd      dz   |j                  dd      dz   d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   )rB   rK   s      r   after_modelz$ModelCallLimitMiddleware.after_model   s7     (-yy1JA'NQR'R$)II.Da$H1$L
 	
r   c                .   K   | j                  ||      S w)zAsync increment model call counts after a model call.

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

        Returns:
            State updates with incremented call counts.
        )rP   rK   s      r   aafter_modelz%ModelCallLimitMiddleware.aafter_model   s      w//rM   )r$   r1   r%   r1   r9   zLiteral['end', 'error']r2   r3   )rC   r   rD   r   r2   zdict[str, Any] | None)r   r   r   r   r   state_schemar,   r   r   rI   rL   rP   rR   r4   r5   s   @r   r7   r7   Y   s    6 'L
 $( $16&+ !&+ 	&+
 /&+ 
&+P eW%(  &(T eW%1"1 1 
	1 &1, 
 
0"0 0 
	0r   r7   N)
r"   r0   r#   r0   r$   r1   r%   r1   r2   str)r   
__future__r   typingr   r   r   r   langchain_core.messagesr   "langgraph.channels.untracked_valuer	   typing_extensionsr
   r   !langchain.agents.middleware.typesr   r   r   r   langgraph.runtimer   r   r'   	Exceptionr)   r7   r   r   r   <module>r]      s    * " 9 9 - = 3  )X* XGGG G 	G
 	G4) >g0/BC/GH g0r   