Adds special private helper to allow direct injection of `ToolRuntime`
in tools, plus adding guards for generic annotations w/ `get_origin`.
Went w/ the private helper so that we didn't change behavior for other
injected types.
To make migration easier, things are more backwards compat
Very minimal footprint here
Will need to upgrade migration guide and other docs w/ this change
Adds `ToolRetryMiddleware` to automatically retry failed tool calls with
configurable exponential backoff, exception filtering, and error
handling.
## Example
```python
from langchain.agents import create_agent
from langchain.agents.middleware import ToolRetryMiddleware
from langchain_openai import ChatOpenAI
# Retry up to 3 times with exponential backoff
retry = ToolRetryMiddleware(
max_retries=3,
initial_delay=1.0,
backoff_factor=2.0,
)
agent = create_agent(
model=ChatOpenAI(model="gpt-4"),
tools=[search_tool, database_tool],
middleware=[retry],
)
# Tool failures are automatically retried
result = agent.invoke({"messages": [{"role": "user", "content": "Search for AI news"}]})
```
For advanced usage with specific exception handling:
```python
from requests.exceptions import Timeout, HTTPError
def should_retry(exc: Exception) -> bool:
# Only retry on 5xx errors or timeouts
if isinstance(exc, HTTPError):
return 500 <= exc.response.status_code < 600
return isinstance(exc, Timeout)
retry = ToolRetryMiddleware(
max_retries=4,
retry_on=should_retry,
tools=["search_database"], # Only apply to specific tools
)
```
Largely:
- Remove explicit `"Default is x"` since new refs show default inferred
from sig
- Inline code (useful for eventual parsing)
- Fix code block rendering (indentations)
The one risk point that I can see here is that model + tool call
counting now occurs in the `after_model` hook which introduces order
dependency (what if you have HITL execute before this hook and we jump
early to `model`, for example).
This is something users can work around at the moment and we can
document. We could also introduce a priority concept to middleware.
## Description
Fixes#33453
`ModelResponse` was defined in `types.py` and included in its `__all__`
list, but was not exported from the middleware package's `__init__.py`.
This caused `ImportError` when attempting to import it directly
from `langchain.agents.middleware`, despite being documented as a public
export.
## Changes
- Added `ModelResponse` to the import statement in
`langchain/agents/middleware/__init__.py`
- Added `ModelResponse` to the `__all__` list in
`langchain/agents/middleware/__init__.py`
- Added comprehensive unit tests in `test_imports.py` to verify the
import works correctly
## Issue
The original issue reported that the following import failed:
```python
from langchain.agents.middleware import ModelResponse
# ImportError: cannot import name 'ModelResponse' from
'langchain.agents.middleware'
The workaround was to import from the submodule:
from langchain.agents.middleware.types import ModelResponse # Workaround
Solution
After this fix, ModelResponse can be imported directly as documented:
from langchain.agents.middleware import ModelResponse # Now works!
Testing
- ✅ Added 3 unit tests in
tests/unit_tests/agents/middleware/test_imports.py
- ✅ All tests pass locally: make format, make lint, make test
- ✅ Verified ModelResponse is properly exported and importable
- ✅ Verified ModelResponse appears in __all__ list
Dependencies
None. This is a simple export fix with no new dependencies.
---------
Co-authored-by: Eugene Yurtsev <eugene@langchain.dev>
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>