feat: add RepairToolOrphans capability for fixing broken tool pairing
Closed
RI
7mo ago+593+593+012345678901234567890123456789-0-0-0123456789
Summary
Implements the RepairToolOrphans capability (as requested by @DouweM in #4728) to repair broken tool-call/return pairing in persisted message history.
Previous PR #5021 was closed because it was implemented as a history_processor instead of a capability. This PR follows the guidance correctly.
What it does
- Dangling tool calls:
ToolCallPartwith no matching return → injects syntheticToolReturnPartin the next request - Dangling builtin calls:
BuiltinToolCallPartwithout return → injectsBuiltinToolReturnPartin the sameModelResponse(required by Anthropic) - Orphaned returns:
ToolReturnPart/RetryPromptPartwith no matching call → removed - Empty requests: If cleanup empties a
ModelRequest, inserts placeholderUserPromptPart - Warnings: Emits
UserWarningwhen repairs are made (configurable viawarn=True/False)
Usage
from pydantic_ai import Agent
from pydantic_ai.capabilities import RepairToolOrphans
agent = Agent('anthropic:claude-sonnet', capabilities=[RepairToolOrphans()])
Files changed
pydantic_ai_slim/pydantic_ai/capabilities/repair_tool_orphans.py— capability implementation (250 lines)pydantic_ai_slim/pydantic_ai/capabilities/__init__.py— register in exports andCAPABILITY_TYPEStests/test_repair_tool_orphans.py— 16 tests (unit + integration)tests/test_capabilities.py— snapshot update for registry
Test plan
- 15 unit tests covering: empty/clean history, dangling regular/builtin calls, trailing responses, orphaned returns/retries, empty request placeholders, partial answers, metadata preservation, warning behavior, multi-turn poisoned conversations
- 1 integration test with
Agent+FunctionModelusing poisoned message history - All 424 existing capability tests pass (2 pre-existing failures unrelated to this change)
Closes #4728
🤖 Generated with Claude Code