
    
i8                        d Z ddlm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
 ddlmZmZmZmZmZ ddlmZmZmZ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ee         eef                   ZdS )z!Tool retry middleware for agents.    )annotationsN)TYPE_CHECKINGAny)ToolMessage)	OnFailureRetryOncalculate_delayshould_retry_exceptionvalidate_retry_params)AgentMiddleware
AgentStateContextT	ResponseT)	AwaitableCallable)Command)ToolCallRequest)BaseToolc            	      r     e Zd ZdZddefdddddd	d. fdZd/dZed0d!            Zd1d%Z	d2d+Z
d3d-Z xZS )4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jitterr   intr   list[BaseTool | str] | Noner   r   r   r   r   floatr   r   r    boolreturnNonec                  t                                                       t          ||||           |dk    r!d}	t          j        |	t
          d           d}n&|dk    r d}	t          j        |	t
          d           d}|| _        |  |d
 |D             | _        nd	| _        g | _        || _	        || _
        || _        || _        || _        || _        d	S )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   Nc                J    g | ] }t          |t                    s|j        n|!S  )
isinstancestrname).0tools     C:\Users\Dell Inspiron 16\Desktop\tws\AgrotaPowerBi\back-agrota-powerbi\mcp-client-agrota\venv\Lib\site-packages\langchain/agents/middleware/tool_retry.py
<listcomp>z0ToolRetryMiddleware.__init__.<locals>.<listcomp>   s.     d d dVZ*T32G2G!QT d d d    )super__init__r   warningswarnDeprecationWarningr   _tool_filterr   r   r   r   r   r   r    )selfr   r   r   r   r   r   r   r    msg	__class__s             r3   r7   zToolRetryMiddleware.__init__   s   r 	 	k=)^TTT   2  M#1a@@@@ JJ+++J  M#1a@@@@#J& 	+ d d^c d d dD $D
 $,*"r5   	tool_namer/   c                &    | j         dS || 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.
        NT)r;   )r<   r?   s     r3   _should_retry_toolz&ToolRetryMiddleware._should_retry_tool   s      $4D---r5   exc	Exceptionattempts_madec                    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?   rB   rD   exc_typeexc_msgattempt_words         r3   _format_failure_messagez+ToolRetryMiddleware._format_failure_message   s}     99%c(($1Q$6$6yyJ=Y = =} = =| = == = '= = =	
r5   tool_call_id
str | Noner   c                    | j         dk    r|t          | j                   r|                      |          }n|                     |||          }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*   )contentrP   r0   status)r   callablerO   r   )r<   r?   rP   rB   rD   rS   s         r3   _handle_failurez#ToolRetryMiddleware._handle_failure   sv    " ?g%%IDO$$ 	Rooc**GG229c=QQG%	
 
 
 	
r5   requestr   handler7Callable[[ToolCallRequest], ToolMessage | Command[Any]]ToolMessage | Command[Any]c           	        |j         r|j         j        n|j        d         }|                     |          s ||          S |j        d         }t	          | j        dz             D ]}	  ||          c S # t          $ r}|dz   }t          || j                  s| 	                    ||||          cY d}~c S || j        k     rCt          || j        | j        | j        | j                  }|dk    rt          j        |           n| 	                    ||||          cY d}~c S Y d}~d}~ww xY wd}	t#          |	          )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).

        Raises:
            RuntimeError: If the retry loop completes without returning. This should not happen.
        r0   idrF   Nr   r   r   r    r   2Unexpected: retry loop completed without returning)r2   r0   	tool_callrA   ranger   rC   r
   r   rV   r	   r   r   r   r    timesleepRuntimeError
r<   rW   rX   r?   rP   rG   rB   rD   delayr=   s
             r3   wrap_tool_callz"ToolRetryMiddleware.wrap_tool_call   s   " *1TGL%%7;LV;T	 &&y11 	$77###(. T-122 	] 	]G]ww''''' ] ] ] '! .c4=AA ]//	<m\\\\\\\\\\ T---+'+':&*&8"&.#{  E qyy
5)))  //	<m\\\\\\\\\\/]4 C3s+   (
A55
D/?1D*0D/8A%D*D/*D/BCallable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]c           	       K   |j         r|j         j        n|j        d         }|                     |          s ||           d{V S |j        d         }t	          | j        dz             D ]}	  ||           d{V c S # t          $ r}|dz   }t          || j                  s| 	                    ||||          cY d}~c S || j        k     rIt          || j        | j        | j        | j                  }|dk    rt          j        |           d{V  n| 	                    ||||          cY d}~c S Y d}~d}~ww xY wd}	t#          |	          )a  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).

        Raises:
            RuntimeError: If the retry loop completes without returning. This should not happen.
        r0   Nr\   rF   r]   r   r^   )r2   r0   r_   rA   r`   r   rC   r
   r   rV   r	   r   r   r   r    asynciorb   rc   rd   s
             r3   awrap_tool_callz#ToolRetryMiddleware.awrap_tool_callZ  s     $ *1TGL%%7;LV;T	 &&y11 	* )))))))))(. T-122 	] 	]G]$WW----------- ] ] ] '! .c4=AA ]//	<m\\\\\\\\\\ T---+'+':&*&8"&.#{  E qyy%mE222222222  //	<m\\\\\\\\\\/]4 C3s+   0B
E1D>>EA+D>1E>E)r   r!   r   r"   r   r   r   r   r   r#   r   r#   r   r#   r    r$   r%   r&   )r?   r/   r%   r$   )r?   r/   rB   rC   rD   r!   r%   r/   )
r?   r/   rP   rQ   rB   rC   rD   r!   r%   r   )rW   r   rX   rY   r%   rZ   )rW   r   rX   rg   r%   rZ   )rK   
__module____qualname____doc__rC   r7   rA   staticmethodrO   rV   rf   rj   __classcell__)r>   s   @r3   r   r      s        _ _H -1&L * #"] ] ] ] ] ] ] ]~. . . . 
 
 
 \
&
 
 
 
@8  8  8  8 t9  9  9  9  9  9  9  9 r5   r   )rm   
__future__r   ri   ra   r8   typingr   r   langchain_core.messagesr   "langchain.agents.middleware._retryr   r   r	   r
   r   !langchain.agents.middleware.typesr   r   r   r   collections.abcr   r   langgraph.typesr   r   langchain.toolsr   r   r-   r5   r3   <module>rx      st   ' ' " " " " " "    % % % % % % % % / / / / / /              _ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ )33333333''''''AAAAAA((((((u  u  u  u  u /*Y*?9*TU u  u  u  u  u r5   