Files
pydantic-ai-harness/docs
David SFandGitHub 6c60f7cf0d feat(step_persistence): rescue last provider-valid resume point on error (#384)
* feat(step_persistence): rescue last provider-valid resume point on error

`StepPersistence` previously left an errored run with no `ContinuableSnapshot`
for a provider-valid state that no node boundary had captured, so `continue_run`
could only resume from an earlier point or raise `LookupError`.

Add an error-path capture: when a run fails against a provider-valid history,
save one snapshot so the run still exposes its last safe resume point. Two
sites cover the two ways such a state is reached without a completed-node
snapshot:

- `on_model_request_error` saves the request payload directly -- e.g. a
  resolved tool cycle, which is only provider-valid as the next request is
  built, never at a `CallToolsNode` boundary.
- `on_run_error` reads a `latest_node_history` contextvar refreshed at every
  `after_node_run` boundary, rescuing a text response whose following
  `CallToolsNode` raises inside output validation. It cannot read `ctx.messages`
  directly: the `RunContext` passed to `on_run_error` holds the start-of-run
  history, which the graph rebinds away during the run.

Both saves are gated on `_is_resumable_history` (provider-valid plus at least
one `ModelResponse`), so a crash mid-tool-call leaves a dangling `ToolCallPart`
that is skipped and `latest_snapshot` never regresses to an unsendable point.
`on_run_error` compares by message count and skips when the store already holds
a newer snapshot, so a stale completed-node stash never supersedes a fresher
`on_model_request_error` save.

This is the gated (provider-validity) approach; the ungated simplification that
rides on pydantic-ai#6319 is deferred to a follow-up.

Closes #253

* test(step_persistence): drop dead validator branch to restore 100% coverage

The `gate` output-validator in `test_does_not_regress_to_stale_stash_after_model_request_save`
had a `return value` branch that never executed: the run fails on request 3
before any output passes validation a second time. Simplify it to always raise
`ModelRetry` (one retry is all the stash scenario needs), matching the sibling
`test_rescues_newest_text_history_over_earlier_saved_snapshot`.
2026-07-20 19:40:25 -05:00
..
2026-07-13 16:15:40 -05:00
2026-07-13 16:15:40 -05:00
2026-07-13 16:15:40 -05:00