Skip to main content

pydantic-ai-escalation

Escalate a Pydantic AI agent to a stronger model when output validation keeps failing.

Most requests in an extraction or classification pipeline are handled well by a cheap model. Output validators catch the rest: the totals don't add up, the entity isn't in the database, the date is in the future. EscalateOnOutputRetry gives the cheap model a chance to fix its answer from the validation error, and only when that isn't enough moves the run to a stronger model, which sees what went wrong before it.

mini ──✗──▶ mini + error ──✗──▶ terra + errors ──✓

Install

pip install pydantic-ai-escalation

Requires pydantic-ai-slim 2.51 or later and Python 3.10+.

Usage

from pydantic import BaseModel

from pydantic_ai import Agent, ModelRetry, RunContext
from pydantic_ai_escalation import EscalateOnOutputRetry, Level


class Invoice(BaseModel):
    lines: list[float]
    total: float


escalation = EscalateOnOutputRetry(
    levels=[
        Level('openai:gpt-4o-mini', output_retries=1),
        Level('openai:gpt-5.6-terra', output_retries=1),
        Level('openai:gpt-5.6-sol'),
    ]
)

agent = Agent(
    output_type=Invoice,
    capabilities=[escalation],
    retries={'output': escalation.required_output_retries},
)


@agent.output_validator
def totals_match(ctx: RunContext[None], invoice: Invoice) -> Invoice:
    if abs(sum(invoice.lines) - invoice.total) > 0.01:
        raise ModelRetry(f'The lines add up to {sum(invoice.lines)}, but the total is {invoice.total}.')
    return invoice

With these levels a run makes at most five requests:

Request Model Sees
1 gpt-4o-mini the prompt
2 gpt-4o-mini the prompt, its answer, the validation error
3 gpt-5.6-terra everything above
4 gpt-5.6-terra everything above, plus its own attempt and error
5 gpt-5.6-sol everything above

The run stops at the first answer that passes validation.

In an agent spec

The capability can be declared in a YAML or JSON agent spec:

# agent.yaml
retries:
  output: 4
capabilities:
  - EscalateOnOutputRetry:
      levels:
        - {model: openai:gpt-4o-mini, output_retries: 1}
        - {model: openai:gpt-5.6-terra, output_retries: 1}
        - {model: openai:gpt-5.6-sol}
from pydantic_ai import Agent
from pydantic_ai_escalation import EscalateOnOutputRetry

agent = Agent.from_file('agent.yaml', custom_capability_types=[EscalateOnOutputRetry])

How it works

  • A level is a model plus output_retries, the number of feedback retries it gets before the run moves on. output_retries=0 (the default) escalates after the first failed attempt.
  • What counts as a failure: every failed output validation in the current run: a ModelRetry from an output validator, an output that doesn't match the schema, or a response with no output at all. Retries of function tools don't count, and neither do failures from earlier runs in the message history.
  • The next level sees everything. Escalation doesn't reset the conversation: the stronger model receives the earlier attempts and their errors, so it knows what didn't work.
  • The last level keeps going until the agent's output retry budget runs out, then the run fails the way it would without the capability.

The output retry budget

Pydantic AI stops a run once it has used up its output retry budget (retries={'output': ...}, which defaults to 1). A capability can't raise that budget, so set it yourself: required_output_retries is the budget that reaches every attempt on every level. If the budget is too small, the upper levels are never reached. On the tool output path (a structured output_type, the default) the capability detects this and emits an EscalationBudgetWarning; with plain text output it can't see the budget, so check it yourself.

Telemetry

When instrumentation is on, each escalation emits one zero-duration escalate_on_output_retry span with escalation.from_level, escalation.to_level and escalation.model. Requests that stay on the same level emit nothing extra: core's own spans already cover them. The span carries no prompt or output content.

Limitations

  • run_stream() can't escalate. Pydantic AI doesn't retry output validation in run_stream(): it fails on the first invalid output, so there is never a second request to send to another model. Use run_stream_events() or run() with an event_stream_handler instead; both stream every response and escalate as usual.
  • Durable execution uses the same model selection path but hasn't been tested yet.
  • The upcoming change to how Pydantic AI records retries (pydantic/pydantic-ai#8094) is handled, but only tested against a simulation of the new format.

Why it's built this way

See DESIGN.md for the decisions behind the API: why this is a capability rather than a FallbackModel, what exactly counts as a failure, and why the budget is yours to set.

License

MIT

Metadata

Release files for pydantic-ai-escalation 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pydantic-ai-escalation 0.1.0
File Size Uploaded Provenance
pydantic_ai_escalation-0.1.0.tar.gz 14.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pydantic-ai-escalation 0.1.0
File Interpreter ABI Platform
pydantic_ai_escalation-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.9 kB

Release files / pydantic_ai_escalation-0.1.0.tar.gz

Download URL pydantic_ai_escalation-0.1.0.tar.gz
Size 14.9 kB
Tags Source
SHA-256 checksum
How to use checksums
799975f43103fb1a243652106d48bec5500e860aa66853d2817ef65f982919c3
BLAKE2b-256 checksum
How to use checksums
ff576173215e0ed3fdac4ea54fee7ccefb22ea01b316c80f1d88136956e77533
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / pydantic_ai_escalation-0.1.0-py3-none-any.whl

Download URL pydantic_ai_escalation-0.1.0-py3-none-any.whl
Size 8.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0a1fe5b6ea139886ace4c5c6715ad927318cb7f151e1116d5705c9715d958fc7
BLAKE2b-256 checksum
How to use checksums
5ba8edfa71947b019f673b8a14f29ead9cd938fb2a08686410b1cd30eed3e7ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for pydantic-ai-escalation 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page