fix: preserve validation error `input` on tool-call retries

Merged
DO
6mo ago
+45-7

Summary

#4947 stripped input from top-level validation errors in RetryPromptPart.model_response() to reduce token bloat for NativeOutput retries (where input duplicates the entire generated JSON across every error, per #4919).

That strip also applied to tool-call retries. For tool calls, the validation error is the model's most direct signal of what arguments it sent — without input, models like Claude Sonnet 4 can't reliably self-correct and repeat the same malformed call until max_retries is exhausted (~0.5% permanent failure rate reported in #5178).

Fix

Scope the strip to tool_name is None (the NativeOutput path). All tool-call retry construction sites (tool_manager.py:181, _output.py:140,202) set tool_name, so input is preserved there.

Credit to @truffle-dev for the analysis and minimal patch in https://github.com/pydantic/pydantic-ai/issues/5178#issuecomment-4306739061.

Tests

  • New regression tests in test_messages.py for the tool-call paths (top-level and nested).
  • Existing NativeOutput-path tests continue to assert the strip behavior.
  • Two snapshots in test_agent.py updated to reflect input now surfacing in output-tool retries (expected behavior change).

Follow-up (not in this PR)

The NativeOutput strip itself may be over-aggressive — it assumes the model reliably cross-references errors against its own prior text output. Worth revisiting separately (dedupe-per-unique-payload likely cleaner than unconditional strip).

Checklist

  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • No breaking changes in accordance with the version policy.
  • PR title is fit for the release changelog.