Metadata-Version: 2.4
Name: langchain-tool-recover
Version: 0.1.0
Summary: Tiny LangChain middleware for better tool-call recovery, loop detection, and structured failure messages.
Project-URL: Homepage, https://github.com/Trihedron1240/langchain-tool-recover
Project-URL: Repository, https://github.com/Trihedron1240/langchain-tool-recover
Project-URL: Issues, https://github.com/Trihedron1240/langchain-tool-recover/issues
Author: Trihedron1240
License-Expression: MIT
License-File: LICENSE
Keywords: agents,langchain,middleware,recovery,tools
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: <4.0,>=3.10
Requires-Dist: langchain<2.0,>=1.0
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Description-Content-Type: text/markdown

# langchain-tool-recover

A tiny LangChain middleware that stops agents from looping, failing silently, or retrying tools stupidly.

`langchain-tool-recover` is not another retry library. LangChain already has `ToolRetryMiddleware`, `ToolCallLimitMiddleware`, and `create_agent` middleware. This package fills the smaller wedge around tool-call recovery UX: duplicate-call loop detection, structured failure messages, empty-result recovery, and sane LangChain tool defaults.

## Install

```bash
pip install langchain-tool-recover
```

For local development:

```bash
pip install -e ".[dev]"
```

## Use With `create_agent`

```python
from langchain.agents import create_agent
from langchain_tool_recover import ToolRecoverMiddleware

agent = create_agent(
    model=model,
    tools=tools,
    middleware=[
        ToolRecoverMiddleware(),
    ],
)
```

When a tool fails or returns an empty result, the agent receives compact JSON it can reason over:

```json
{"status":"needs_replan","tool":"search_docs","failure_class":"empty_result","message":"The tool returned no results.","suggestion":"Try broadening the query or removing exact-match terms.","action":"return_to_agent","attempt":1}
```

## Use As A Tool Wrapper

```python
from langchain_tool_recover import recover_tool

safe_search = recover_tool(search_tool)
```

The wrapper preserves the tool name, description, argument schema, and async support where LangChain exposes it.

## Before And After

Without this library:

```text
Agent calls search_docs("foo")
No results.
Agent calls search_docs("foo")
No results.
Agent calls search_docs("foo")
No results.
```

With this library:

```text
Agent calls search_docs("foo")
No results.
[tool-recover] search_docs attempt=1 status=empty_result
[tool-recover] suggestion=Try broadening the query or removing exact-match terms.
[tool-recover] action=returned_recovery_message
Suggestion sent to agent: broaden the query or try another tool.
```

Duplicate calls are tracked per run. The first duplicate logs a warning and executes. The second duplicate returns a `needs_replan` message. The third and later duplicates are blocked for that run.

## Default Policy

| Failure class | Default action |
| --- | --- |
| `timeout` | `retry_with_backoff` |
| `rate_limit` | `retry_with_backoff` |
| `validation_error` | `return_to_agent` |
| `auth_error` | `fail_fast` |
| `empty_result` | `return_to_agent` |
| `duplicate_call` | `block` after duplicate limit |
| `unsafe_command` | `block` |
| `unknown_error` | `fail_fast` |

Override policies by passing a mapping:

```python
from langchain_tool_recover import FailureClass, RecoveryAction, ToolRecoverMiddleware

middleware = ToolRecoverMiddleware(
    policies={
        FailureClass.UNKNOWN_ERROR: RecoveryAction.RETURN_TO_AGENT,
    }
)
```

## What It Classifies

The classifier is deterministic and conservative:

- Exceptions: timeout, rate limit, validation error, auth error, unsafe command, unknown error.
- Empty results: `None`, blank strings, empty collections, and explicit empty sentinels such as `{"empty": true}` or `{"results": []}`.
- LangChain `ToolMessage(status="error")` content is reclassified when the tool node converts invocation errors into messages.

It intentionally does not try to infer complex semantic failure from arbitrary prose in v1.

## Examples

- `examples/basic_agent.py`
- `examples/duplicate_tool_loop.py`
- `examples/empty_search_recovery.py`

## Development

```bash
pip install -e ".[dev]"
pytest
python -m build
ruff check .
```

## License

MIT
