Skip to content

Commit 4fd4b5c

Browse files
fix(langchain): update toolCallRetry and add modelRetry middleware (#1528)
Updates middleware documentation to reflect changes from: - langchain-ai/langchainjs#9454 - langchain-ai/langchain#34027 which adds `modelRetryMiddleware` and updates `toolRetryMiddleware` API. ## Changes ### New: Model Retry Middleware Documentation - Added comprehensive documentation for `modelRetryMiddleware` (JavaScript) and `ModelRetryMiddleware` (Python) - Includes configuration options, examples, and use cases - Covers retry strategies, exponential backoff, custom exception filtering, and failure handling - Added to middleware tables for both Python and JavaScript ### Updated: Tool Retry Middleware Documentation - Updated `onFailure` parameter documentation to reflect API changes: - `'raise'` → `'error'` (deprecated, still supported with warning) - `'return_message'` → `'continue'` (deprecated, still supported with warning) - Updated all examples to use new parameter values - Added deprecation notices for old values ⚠️ this should be merged as part of v1.1 release
1 parent 8cb7ae9 commit 4fd4b5c

File tree

2 files changed

+286
-6
lines changed

2 files changed

+286
-6
lines changed

pipeline/preprocessors/link_map.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class LinkMap(TypedDict):
5555
"TodoListMiddleware": "langchain/middleware/#langchain.agents.middleware.TodoListMiddleware",
5656
"LLMToolSelectorMiddleware": "langchain/middleware/#langchain.agents.middleware.LLMToolSelectorMiddleware",
5757
"ToolRetryMiddleware": "langchain/middleware/#langchain.agents.middleware.ToolRetryMiddleware",
58+
"ModelRetryMiddleware": "langchain/middleware/#langchain.agents.middleware.ModelRetryMiddleware",
5859
"LLMToolEmulator": "langchain/middleware/#langchain.agents.middleware.LLMToolEmulator",
5960
"ContextEditingMiddleware": "langchain/middleware/#langchain.agents.middleware.ContextEditingMiddleware",
6061
"ClearToolUsesEdit": "langchain/middleware/#langchain.agents.middleware.ClearToolUsesEdit",
@@ -378,6 +379,7 @@ class LinkMap(TypedDict):
378379
"ClearToolUsesEdit": "classes/langchain.index.ClearToolUsesEdit.html",
379380
"ContextEdit": "interfaces/langchain.index.ContextEdit.html",
380381
"toolRetryMiddleware": "functions/langchain.index.toolRetryMiddleware.html",
382+
"modelRetryMiddleware": "functions/langchain.index.modelRetryMiddleware.html",
381383
},
382384
},
383385
]

src/oss/langchain/middleware/built-in.mdx

Lines changed: 284 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ The following middleware work with any LLM provider:
2222
| [To-do list](#to-do-list) | Equip agents with task planning and tracking capabilities. |
2323
| [LLM tool selector](#llm-tool-selector) | Use an LLM to select relevant tools before calling main model. |
2424
| [Tool retry](#tool-retry) | Automatically retry failed tool calls with exponential backoff. |
25+
| [Model retry](#model-retry) | Automatically retry failed model calls with exponential backoff. |
2526
| [LLM tool emulator](#llm-tool-emulator) | Emulate tool execution using an LLM for testing purposes. |
2627
| [Context editing](#context-editing) | Manage conversation context by trimming or clearing tool uses. |
2728
| [Shell tool](#shell-tool) | Expose a persistent shell session to agents for command execution. |
@@ -42,6 +43,7 @@ The following middleware work with any LLM provider:
4243
| [To-do list](#to-do-list) | Equip agents with task planning and tracking capabilities. |
4344
| [LLM tool selector](#llm-tool-selector) | Use an LLM to select relevant tools before calling main model. |
4445
| [Tool retry](#tool-retry) | Automatically retry failed tool calls with exponential backoff. |
46+
| [Model retry](#model-retry) | Automatically retry failed model calls with exponential backoff. |
4547
| [LLM tool emulator](#llm-tool-emulator) | Emulate tool execution using an LLM for testing purposes. |
4648
| [Context editing](#context-editing) | Manage conversation context by trimming or clearing tool uses. |
4749

@@ -1261,11 +1263,13 @@ const agent = createAgent({
12611263
Either an array of error constructors to retry on, or a function that takes an error and returns `true` if it should be retried. Default is to retry on all errors.
12621264
</ParamField>
12631265

1264-
<ParamField body="onFailure" type="'raise' | 'return_message' | ((error: Error) => string)" default="return_message">
1266+
<ParamField body="onFailure" type="'error' | 'continue' | ((error: Error) => string)" default="continue">
12651267
Behavior when all retries are exhausted. Options:
1266-
- `'return_message'` (default) - Return a `ToolMessage` with error details, allowing the LLM to handle the failure and potentially recover
1267-
- `'raise'` - Re-raise the exception, stopping agent execution
1268+
- `'continue'` (default) - Return a `ToolMessage` with error details, allowing the LLM to handle the failure and potentially recover
1269+
- `'error'` - Re-raise the exception, stopping agent execution
12681270
- Custom function - Function that takes the exception and returns a string for the `ToolMessage` content, allowing custom error formatting
1271+
1272+
**Deprecated values:** `'raise'` (use `'error'` instead) and `'return_message'` (use `'continue'` instead). These deprecated values still work but will show a warning.
12691273
</ParamField>
12701274

12711275
<ParamField body="backoffFactor" type="number" default="2.0">
@@ -1313,8 +1317,8 @@ The middleware automatically retries failed tool calls with exponential backoff.
13131317
- `jitter` - Add random variation (default: true)
13141318

13151319
**Failure handling:**
1316-
- `onFailure: "return_message"` - Return error message
1317-
- `onFailure: "raise"` - Re-raise exception
1320+
- `onFailure: "continue"` (default) - Return error message
1321+
- `onFailure: "error"` - Re-raise exception
13181322
- Custom function - Function returning error message
13191323
:::
13201324

@@ -1416,7 +1420,281 @@ const constantBackoff = toolRetryMiddleware({
14161420
// Raise exception on failure
14171421
const strictRetry = toolRetryMiddleware({
14181422
maxRetries: 2,
1419-
onFailure: "raise", // Re-raise exception instead of returning message
1423+
onFailure: "error", // Re-raise exception instead of returning message
1424+
});
1425+
```
1426+
:::
1427+
1428+
</Accordion>
1429+
1430+
1431+
### Model retry
1432+
1433+
Automatically retry failed model calls with configurable exponential backoff. Model retry is useful for the following:
1434+
1435+
- Handling transient failures in model API calls.
1436+
- Improving reliability of network-dependent model requests.
1437+
- Building resilient agents that gracefully handle temporary model errors.
1438+
1439+
:::python
1440+
**API reference:** @[`ModelRetryMiddleware`]
1441+
1442+
```python
1443+
from langchain.agents import create_agent
1444+
from langchain.agents.middleware import ModelRetryMiddleware
1445+
1446+
agent = create_agent(
1447+
model="gpt-4o",
1448+
tools=[search_tool, database_tool],
1449+
middleware=[
1450+
ModelRetryMiddleware(
1451+
max_retries=3,
1452+
backoff_factor=2.0,
1453+
initial_delay=1.0,
1454+
),
1455+
],
1456+
)
1457+
```
1458+
:::
1459+
1460+
:::js
1461+
**API reference:** @[`modelRetryMiddleware`]
1462+
1463+
```typescript
1464+
import { createAgent, modelRetryMiddleware } from "langchain";
1465+
1466+
const agent = createAgent({
1467+
model: "gpt-4o",
1468+
tools: [searchTool, databaseTool],
1469+
middleware: [
1470+
modelRetryMiddleware({
1471+
maxRetries: 3,
1472+
backoffFactor: 2.0,
1473+
initialDelayMs: 1000,
1474+
}),
1475+
],
1476+
});
1477+
```
1478+
:::
1479+
1480+
<Accordion title="Configuration options">
1481+
1482+
:::python
1483+
<ParamField body="max_retries" type="number" default="2">
1484+
Maximum number of retry attempts after the initial call (3 total attempts with default)
1485+
</ParamField>
1486+
1487+
<ParamField body="retry_on" type="tuple[type[Exception], ...] | callable" default="(Exception,)">
1488+
Either a tuple of exception types to retry on, or a callable that takes an exception and returns `True` if it should be retried.
1489+
</ParamField>
1490+
1491+
<ParamField body="on_failure" type="string | callable" default="continue">
1492+
Behavior when all retries are exhausted. Options:
1493+
- `'continue'` (default) - Return an `AIMessage` with error details, allowing the agent to potentially handle the failure gracefully
1494+
- `'error'` - Re-raise the exception (stops agent execution)
1495+
- Custom callable - Function that takes the exception and returns a string for the `AIMessage` content
1496+
</ParamField>
1497+
1498+
<ParamField body="backoff_factor" type="number" default="2.0">
1499+
Multiplier for exponential backoff. Each retry waits `initial_delay * (backoff_factor ** retry_number)` seconds. Set to `0.0` for constant delay.
1500+
</ParamField>
1501+
1502+
<ParamField body="initial_delay" type="number" default="1.0">
1503+
Initial delay in seconds before first retry
1504+
</ParamField>
1505+
1506+
<ParamField body="max_delay" type="number" default="60.0">
1507+
Maximum delay in seconds between retries (caps exponential backoff growth)
1508+
</ParamField>
1509+
1510+
<ParamField body="jitter" type="boolean" default="true">
1511+
Whether to add random jitter (`±25%`) to delay to avoid thundering herd
1512+
</ParamField>
1513+
:::
1514+
1515+
:::js
1516+
<ParamField body="maxRetries" type="number" default="2">
1517+
Maximum number of retry attempts after the initial call (3 total attempts with default). Must be >= 0.
1518+
</ParamField>
1519+
1520+
<ParamField body="retryOn" type="((error: Error) => boolean) | (new (...args: any[]) => Error)[]" default="() => true">
1521+
Either an array of error constructors to retry on, or a function that takes an error and returns `true` if it should be retried. Default is to retry on all errors.
1522+
</ParamField>
1523+
1524+
<ParamField body="onFailure" type="'error' | 'continue' | ((error: Error) => string)" default="continue">
1525+
Behavior when all retries are exhausted. Options:
1526+
- `'continue'` (default) - Return an `AIMessage` with error details, allowing the agent to potentially handle the failure gracefully
1527+
- `'error'` - Re-raise the exception, stopping agent execution
1528+
- Custom function - Function that takes the exception and returns a string for the `AIMessage` content, allowing custom error formatting
1529+
</ParamField>
1530+
1531+
<ParamField body="backoffFactor" type="number" default="2.0">
1532+
Multiplier for exponential backoff. Each retry waits `initialDelayMs * (backoffFactor ** retryNumber)` milliseconds. Set to `0.0` for constant delay. Must be >= 0.
1533+
</ParamField>
1534+
1535+
<ParamField body="initialDelayMs" type="number" default="1000">
1536+
Initial delay in milliseconds before first retry. Must be >= 0.
1537+
</ParamField>
1538+
1539+
<ParamField body="maxDelayMs" type="number" default="60000">
1540+
Maximum delay in milliseconds between retries (caps exponential backoff growth). Must be >= 0.
1541+
</ParamField>
1542+
1543+
<ParamField body="jitter" type="boolean" default="true">
1544+
Whether to add random jitter (`±25%`) to delay to avoid thundering herd
1545+
</ParamField>
1546+
:::
1547+
1548+
</Accordion>
1549+
1550+
<Accordion title="Full example">
1551+
1552+
The middleware automatically retries failed model calls with exponential backoff.
1553+
1554+
:::python
1555+
```python
1556+
from langchain.agents import create_agent
1557+
from langchain.agents.middleware import ModelRetryMiddleware
1558+
1559+
1560+
# Basic usage with default settings (2 retries, exponential backoff)
1561+
agent = create_agent(
1562+
model="gpt-4o",
1563+
tools=[search_tool],
1564+
middleware=[ModelRetryMiddleware()],
1565+
)
1566+
1567+
# Custom exception filtering
1568+
class TimeoutError(Exception):
1569+
"""Custom exception for timeout errors."""
1570+
pass
1571+
1572+
class ConnectionError(Exception):
1573+
"""Custom exception for connection errors."""
1574+
pass
1575+
1576+
# Retry specific exceptions only
1577+
retry = ModelRetryMiddleware(
1578+
max_retries=4,
1579+
retry_on=(TimeoutError, ConnectionError),
1580+
backoff_factor=1.5,
1581+
)
1582+
1583+
1584+
def should_retry(error: Exception) -> bool:
1585+
# Only retry on rate limit errors
1586+
if isinstance(error, TimeoutError):
1587+
return True
1588+
# Or check for specific HTTP status codes
1589+
if hasattr(error, "status_code"):
1590+
return error.status_code in (429, 503)
1591+
return False
1592+
1593+
retry_with_filter = ModelRetryMiddleware(
1594+
max_retries=3,
1595+
retry_on=should_retry,
1596+
)
1597+
1598+
# Return error message instead of raising
1599+
retry_continue = ModelRetryMiddleware(
1600+
max_retries=4,
1601+
on_failure="continue", # Return AIMessage with error instead of raising
1602+
)
1603+
1604+
# Custom error message formatting
1605+
def format_error(error: Exception) -> str:
1606+
return f"Model call failed: {error}. Please try again later."
1607+
1608+
retry_with_formatter = ModelRetryMiddleware(
1609+
max_retries=4,
1610+
on_failure=format_error,
1611+
)
1612+
1613+
# Constant backoff (no exponential growth)
1614+
constant_backoff = ModelRetryMiddleware(
1615+
max_retries=5,
1616+
backoff_factor=0.0, # No exponential growth
1617+
initial_delay=2.0, # Always wait 2 seconds
1618+
)
1619+
1620+
# Raise exception on failure
1621+
strict_retry = ModelRetryMiddleware(
1622+
max_retries=2,
1623+
on_failure="error", # Re-raise exception instead of returning message
1624+
)
1625+
```
1626+
:::
1627+
1628+
:::js
1629+
```typescript
1630+
import { createAgent, modelRetryMiddleware } from "langchain";
1631+
1632+
// Basic usage with default settings (2 retries, exponential backoff)
1633+
const agent = createAgent({
1634+
model: "gpt-4o",
1635+
tools: [searchTool],
1636+
middleware: [modelRetryMiddleware()],
1637+
});
1638+
1639+
class TimeoutError extends Error {
1640+
// ...
1641+
}
1642+
class NetworkError extends Error {
1643+
// ...
1644+
}
1645+
1646+
// Retry specific exceptions only
1647+
const retry = modelRetryMiddleware({
1648+
maxRetries: 4,
1649+
retryOn: [TimeoutError, NetworkError],
1650+
backoffFactor: 1.5,
1651+
});
1652+
1653+
// Custom exception filtering
1654+
function shouldRetry(error: Error): boolean {
1655+
// Only retry on rate limit errors
1656+
if (error.name === "RateLimitError") {
1657+
return true;
1658+
}
1659+
// Or check for specific HTTP status codes
1660+
if (error.name === "HTTPError" && "statusCode" in error) {
1661+
const statusCode = (error as any).statusCode;
1662+
return statusCode === 429 || statusCode === 503;
1663+
}
1664+
return false;
1665+
}
1666+
1667+
const retryWithFilter = modelRetryMiddleware({
1668+
maxRetries: 3,
1669+
retryOn: shouldRetry,
1670+
});
1671+
1672+
// Return error message instead of raising
1673+
const retryContinue = modelRetryMiddleware({
1674+
maxRetries: 4,
1675+
onFailure: "continue", // Return AIMessage with error instead of throwing
1676+
});
1677+
1678+
// Custom error message formatting
1679+
const formatError = (error: Error) =>
1680+
`Model call failed: ${error.message}. Please try again later.`;
1681+
1682+
const retryWithFormatter = modelRetryMiddleware({
1683+
maxRetries: 4,
1684+
onFailure: formatError,
1685+
});
1686+
1687+
// Constant backoff (no exponential growth)
1688+
const constantBackoff = modelRetryMiddleware({
1689+
maxRetries: 5,
1690+
backoffFactor: 0.0, // No exponential growth
1691+
initialDelayMs: 2000, // Always wait 2 seconds
1692+
});
1693+
1694+
// Raise exception on failure
1695+
const strictRetry = modelRetryMiddleware({
1696+
maxRetries: 2,
1697+
onFailure: "error", // Re-raise exception instead of returning message
14201698
});
14211699
```
14221700
:::

0 commit comments

Comments
 (0)