
    g3fi+7                        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mZmZmZmZ ddlmZ erddlmZmZ dd	lmZ dd
lmZ ddlmZ  G d de      Zy)z!Tool retry middleware for agents.    )annotationsN)TYPE_CHECKING)ToolMessage)	OnFailureRetryOncalculate_delayshould_retry_exceptionvalidate_retry_params)AgentMiddleware)	AwaitableCallable)Command)ToolCallRequest)BaseToolc            	           e Zd ZdZddefdddddd		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d fd
ZddZddZ	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ		 	 	 	 	 	 ddZ
 xZS )ToolRetryMiddlewarea  Middleware that automatically retries failed tool calls with configurable backoff.

    Supports retrying on specific exceptions and exponential backoff.

    Examples:
        !!! example "Basic usage with default settings (2 retries, exponential backoff)"

            ```python
            from langchain.agents import create_agent
            from langchain.agents.middleware import ToolRetryMiddleware

            agent = create_agent(model, tools=[search_tool], middleware=[ToolRetryMiddleware()])
            ```

        !!! example "Retry specific exceptions only"

            ```python
            from requests.exceptions import RequestException, Timeout

            retry = ToolRetryMiddleware(
                max_retries=4,
                retry_on=(RequestException, Timeout),
                backoff_factor=1.5,
            )
            ```

        !!! example "Custom exception filtering"

            ```python
            from requests.exceptions import HTTPError


            def should_retry(exc: Exception) -> bool:
                # Only retry on 5xx errors
                if isinstance(exc, HTTPError):
                    return 500 <= exc.status_code < 600
                return False


            retry = ToolRetryMiddleware(
                max_retries=3,
                retry_on=should_retry,
            )
            ```

        !!! example "Apply to specific tools with custom error handling"

            ```python
            def format_error(exc: Exception) -> str:
                return "Database temporarily unavailable. Please try again later."


            retry = ToolRetryMiddleware(
                max_retries=4,
                tools=["search_database"],
                on_failure=format_error,
            )
            ```

        !!! example "Apply to specific tools using `BaseTool` instances"

            ```python
            from langchain_core.tools import tool


            @tool
            def search_database(query: str) -> str:
                '''Search the database.'''
                return results


            retry = ToolRetryMiddleware(
                max_retries=4,
                tools=[search_database],  # Pass BaseTool instance
            )
            ```

        !!! example "Constant backoff (no exponential growth)"

            ```python
            retry = ToolRetryMiddleware(
                max_retries=5,
                backoff_factor=0.0,  # No exponential growth
                initial_delay=2.0,  # Always wait 2 seconds
            )
            ```

        !!! example "Raise exception on failure"

            ```python
            retry = ToolRetryMiddleware(
                max_retries=2,
                on_failure="error",  # Re-raise exception instead of returning message
            )
            ```
       Ncontinueg       @g      ?g      N@T)max_retriestoolsretry_on
on_failurebackoff_factorinitial_delay	max_delayjitterc                  t         |           t        ||||       |dk(  r!d}	t        j                  |	t
        d       d}n%|dk(  r d}	t        j                  |	t
        d       d}|| _        |  |2|D 
cg c]   }
t        |
t              s|
j                  n|
" c}
| _
        nd	| _
        g | _        || _        || _        || _        || _        || _        || _        y	c c}
w )
uv  Initialize `ToolRetryMiddleware`.

        Args:
            max_retries: Maximum number of retry attempts after the initial call.

                Must be `>= 0`.
            tools: Optional list of tools or tool names to apply retry logic to.

                Can be a list of `BaseTool` instances or tool name strings.

                If `None`, applies to all tools.
            retry_on: Either a tuple of exception types to retry on, or a callable
                that takes an exception and returns `True` if it should be retried.

                Default is to retry on all exceptions.
            on_failure: Behavior when all retries are exhausted.

                Options:

                - `'continue'`: Return a `ToolMessage` with error details,
                    allowing the LLM to handle the failure and potentially recover.
                - `'error'`: Re-raise the exception, stopping agent execution.
                - **Custom callable:** Function that takes the exception and returns a
                    string for the `ToolMessage` content, allowing custom error
                    formatting.

                **Deprecated values** (for backwards compatibility):

                - `'return_message'`: Use `'continue'` instead.
                - `'raise'`: Use `'error'` instead.
            backoff_factor: Multiplier for exponential backoff.

                Each retry waits `initial_delay * (backoff_factor ** retry_number)`
                seconds.

                Set to `0.0` for constant delay.
            initial_delay: Initial delay in seconds before first retry.
            max_delay: Maximum delay in seconds between retries.

                Caps exponential backoff growth.
            jitter: Whether to add random jitter (`±25%`) to delay to avoid thundering herd.

        Raises:
            ValueError: If `max_retries < 0` or delays are negative.
        raisezion_failure='raise' is deprecated and will be removed in a future version. Use on_failure='error' instead.r   )
stacklevelerrorreturn_messagezuon_failure='return_message' is deprecated and will be removed in a future version. Use on_failure='continue' instead.r   N)super__init__r
   warningswarnDeprecationWarningr   
isinstancestrname_tool_filterr   r   r   r   r   r   r   )selfr   r   r   r   r   r   r   r   msgtool	__class__s              d/var/www/auto_recruiter/arenv/lib/python3.12/site-packages/langchain/agents/middleware/tool_retry.pyr#   zToolRetryMiddleware.__init__   s    r 	 	k=)^T  2  MM#1a@ J++J  MM#1a@#J& 	^c dVZ*T32GT!Q dD $D
 $,*" !es   8%Cc                8    | j                   y|| j                   v S )zCheck if retry logic should apply to this tool.

        Args:
            tool_name: Name of the tool being called.

        Returns:
            `True` if retry logic should apply, `False` otherwise.
        T)r*   )r+   	tool_names     r/   _should_retry_toolz&ToolRetryMiddleware._should_retry_tool   s$     $D----    c                x    t        |      j                  }t        |      }|dk(  rdnd}d| d| d| d| d| d	S )
a6  Format the failure message when retries are exhausted.

        Args:
            tool_name: Name of the tool that failed.
            exc: The exception that caused the failure.
            attempts_made: Number of attempts actually made.

        Returns:
            Formatted error message string.
           attemptattemptszTool 'z' failed after  z with z: z. Please try again.)type__name__r(   )r+   r1   excattempts_madeexc_typeexc_msgattempt_words          r/   _format_failure_messagez+ToolRetryMiddleware._format_failure_message   s[     9%%c($1Q$6yJYK}oQ|n M:Ry(;=	
r3   c                    | j                   dk(  r|t        | j                         r| j                  |      }n| j                  |||      }t        |||d      S )a  Handle failure when all retries are exhausted.

        Args:
            tool_name: Name of the tool that failed.
            tool_call_id: ID of the tool call (may be `None`).
            exc: The exception that caused the failure.
            attempts_made: Number of attempts actually made.

        Returns:
            `ToolMessage` with error details.

        Raises:
            Exception: If `on_failure` is `'error'`, re-raises the exception.
        r    )contenttool_call_idr)   status)r   callabler@   r   )r+   r1   rC   r;   r<   rB   s         r/   _handle_failurez#ToolRetryMiddleware._handle_failure   s\    " ??g%IDOO$ooc*G229c=QG%	
 	
r3   c           	        |j                   r|j                   j                  n|j                  d   }| j                  |      s ||      S |j                  d   }t	        | j
                  dz         D ]  }	  ||      c S  d}	t#        |	      # t        $ r}|dz   }t        || j                        s| j                  ||||      cY d}~c S || j
                  k  rSt        || j                  | j                  | j                  | j                        }|dkD  r1t        j                   |       n| j                  ||||      cY d}~c S Y d}~d}~ww xY w)a<  Intercept tool execution and retry on failure.

        Args:
            request: Tool call request with call dict, `BaseTool`, state, and runtime.
            handler: Callable to execute the tool (can be called multiple times).

        Returns:
            `ToolMessage` or `Command` (the final result).
        r)   idr5   Nr   r   r   r   r   2Unexpected: retry loop completed without returning)r-   r)   	tool_callr2   ranger   	Exceptionr	   r   rF   r   r   r   r   r   timesleepRuntimeError
r+   requesthandlerr1   rC   r6   r;   r<   delayr,   s
             r/   wrap_tool_callz"ToolRetryMiddleware.wrap_tool_call  sH    *1GLL%%7;L;LV;T	 &&y17##((. T--12 	]G]w''	]: C37  ] '! .c4==A//	<m\\ T---+'+':':&*&8&8"&..#{{E qy

5)  //	<m\\/]s+   6B	E.EEA5EEEc           	       K   |j                   r|j                   j                  n|j                  d   }| j                  |      s ||       d{   S |j                  d   }t	        | j
                  dz         D ]  }	  ||       d{   c S  d}	t#        |	      7 O7 # t        $ r}|dz   }t        || j                        s| j                  ||||      cY d}~c S || j
                  k  r\t        || j                  | j                  | j                  | j                        }|dkD  r:t        j                   |       d{  7   n| j                  ||||      cY d}~c S Y d}~d}~ww xY ww)aj  Intercept and control async tool execution with retry logic.

        Args:
            request: Tool call request with call `dict`, `BaseTool`, state, and runtime.
            handler: Async callable to execute the tool and returns `ToolMessage` or
                `Command`.

        Returns:
            `ToolMessage` or `Command` (the final result).
        r)   NrH   r5   rI   r   rJ   )r-   r)   rK   r2   rL   r   rM   r	   r   rF   r   r   r   r   r   asynciorO   rP   rQ   s
             r/   awrap_tool_callz#ToolRetryMiddleware.awrap_tool_callV  sa     *1GLL%%7;L;LV;T	 &&y1 )))((. T--12 	]G]$W---	]: C3G * . ] '! .c4==A//	<m\\ T---+'+':':&*&8&8"&..#{{E qy%mmE222  //	<m\\/]ss   AE6B .E6 B$B"B$E6"B$$	E3-.E.E3E6#A$E.E
E.!E3"E6.E33E6)r   intr   zlist[BaseTool | str] | Noner   r   r   r   r   floatr   rZ   r   rZ   r   boolreturnNone)r1   r(   r\   r[   )r1   r(   r;   rM   r<   rY   r\   r(   )
r1   r(   rC   z
str | Noner;   rM   r<   rY   r\   r   )rR   r   rS   z2Callable[[ToolCallRequest], ToolMessage | Command]r\   ToolMessage | Command)rR   r   rS   z=Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]r\   r^   )r:   
__module____qualname____doc__rM   r#   r2   r@   rF   rU   rX   __classcell__)r.   s   @r/   r   r      s   _H -1&L * #"] ] +	]
 ] ] ] ] ] ] 
]~.
&

,6
=F
WZ
	
@5  5  D5  
	5 n6  6  O6  
	6 r3   r   )ra   
__future__r   rW   rN   r$   typingr   langchain_core.messagesr   "langchain.agents.middleware._retryr   r   r   r	   r
   !langchain.agents.middleware.typesr   collections.abcr   r   langgraph.typesr   r   langchain.toolsr   r    r3   r/   <module>rl      sF    ' "      /  >3'A(n / n r3   