diff --git a/libs/langchain/langchain/schema/runnable/base.py b/libs/langchain/langchain/schema/runnable/base.py index d40e27f14d0..b99fce2c836 100644 --- a/libs/langchain/langchain/schema/runnable/base.py +++ b/libs/langchain/langchain/schema/runnable/base.py @@ -644,6 +644,17 @@ class Runnable(Generic[Input, Output], ABC): 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 c0c39a54d9a..504857a4c66 100644 --- a/libs/langchain/langchain/schema/runnable/retry.py +++ b/libs/langchain/langchain/schema/runnable/retry.py @@ -35,13 +35,84 @@ U = TypeVar("U") 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]: