From 06c503f672250679d54b69e286d43822c0abb0d8 Mon Sep 17 00:00:00 2001 From: Eugene Yurtsev Date: Wed, 8 Nov 2023 13:20:18 -0500 Subject: [PATCH] Add RunnableRetry Documentation (#13074) --- .../langchain/schema/runnable/base.py | 11 +++ .../langchain/schema/runnable/retry.py | 73 ++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/libs/langchain/langchain/schema/runnable/base.py b/libs/langchain/langchain/schema/runnable/base.py index d40e27f14d078..b99fce2c836f4 100644 --- a/libs/langchain/langchain/schema/runnable/base.py +++ b/libs/langchain/langchain/schema/runnable/base.py @@ -644,6 +644,17 @@ def with_retry( wait_exponential_jitter: bool = True, stop_after_attempt: int = 3, ) -> Runnable[Input, Output]: + """Create a new Runnable that retries the original runnable on exceptions. + + Args: + retry_if_exception_type: A tuple of exception types to retry on + wait_exponential_jitter: Whether to add jitter to the wait time + between retries + stop_after_attempt: The maximum number of attempts to make before giving up + + Returns: + A new Runnable that retries the original runnable on exceptions. + """ from langchain.schema.runnable.retry import RunnableRetry return RunnableRetry( diff --git a/libs/langchain/langchain/schema/runnable/retry.py b/libs/langchain/langchain/schema/runnable/retry.py index c0c39a54d9a94..504857a4c663e 100644 --- a/libs/langchain/langchain/schema/runnable/retry.py +++ b/libs/langchain/langchain/schema/runnable/retry.py @@ -35,13 +35,84 @@ class RunnableRetry(RunnableBinding[Input, Output]): - """Retry a Runnable if it fails.""" + """Retry a Runnable if it fails. + + A RunnableRetry helps can be used to add retry logic to any object + that subclasses the base Runnable. + + Such retries are especially useful for network calls that may fail + due to transient errors. + + The RunnableRetry is implemented as a RunnableBinding. The easiest + way to use it is through the `.with_retry()` method on all Runnables. + + Example: + + Here's an example that uses a RunnableLambda to raise an exception + + .. code-block:: python + + import time + + def foo(input) -> None: + '''Fake function that raises an exception.''' + raise ValueError("Invoking foo failed. At time {time.time()}") + + runnable = RunnableLambda(foo) + + runnable_with_retries = runnable.with_retry( + retry_exception_types=(ValueError,), # Retry only on ValueError + wait_exponential_jitter=True, # Add jitter to the exponential backoff + max_attempt_number=2, # Try twice + ) + + # The method invocation above is equivalent to the longer form below: + + runnable_with_retries = RunnableRetry( + bound=runnable, + retry_exception_types=(ValueError,), + max_attempt_number=2, + wait_exponential_jitter=True + ) + + This logic can be used to retry any Runnable, including a chain of Runnables, + but in general it's best practice to keep the scope of the retry as small as + possible. For example, if you have a chain of Runnables, you should only retry + the Runnable that is likely to fail, not the entire chain. + + Example: + + .. code-block:: python + + from langchain.chat_models import ChatOpenAI + from langchain.prompts import PromptTemplate + + template = PromptTemplate.from_template("tell me a joke about {topic}.") + model = ChatOpenAI(temperature=0.5) + + # Good + chain = template | model.with_retry() + + # Bad + chain = template | model + retryable_chain = chain.with_retry() + """ retry_exception_types: Tuple[Type[BaseException], ...] = (Exception,) + """The exception types to retry on. By default all exceptions are retried. + + In general you should only retry on exceptions that are likely to be + transient, such as network errors. + + Good exceptions to retry are all server errors (5xx) and selected client + errors (4xx) such as 429 Too Many Requests. + """ wait_exponential_jitter: bool = True + """Whether to add jitter to the exponential backoff.""" max_attempt_number: int = 3 + """The maximum number of attempts to retry the runnable.""" @property def _kwargs_retrying(self) -> Dict[str, Any]: