4.5 KiB
Pydantic AI Streaming Resilience Sweep
The streaming/agent-loop code is in pydantic_ai_slim/pydantic_ai/ (_agent_graph, result, messages,
run) and the AG-UI / Vercel adapters.
Objective
Find one concrete bug in the streaming state machine. Streaming is the largest topical cluster and harbors hard-to-spot ordering and lifecycle bugs. Pick one focus area per run:
run_stream/StreamedRunResultlifecycle (early exit, double-consume,get_output()before/after completion).agent.iter/_next_nodegraph node ordering and assertions.event_stream_handlerevent sequence (start → deltas → end), including for tool calls and thinking parts.- Partial / aborted / cancelled streams (network drop,
break, timeout) and cleanup. - AG-UI / Vercel adapter event ordering and terminal events.
- Usage / final message assembly when the stream ends or errors mid-way.
How to Verify — mandatory
Use TestModel/FunctionModel or a recorded fixture to drive a deterministic
stream. Write a new minimal test asserting the event/None sequence and
final message — e.g. no deltas after the final part, tool-call/return
pairing intact, usage populated on completion, no StopAsyncIteration/
assertion leakage on early exit. Do not run and report the existing suite.
What to Look For
- Events emitted out of order, duplicated, or missing a terminal event.
- State leaking between consumption attempts;
get_output()returning stale or partial data. - Exceptions/asserts surfacing to the user on normal early termination.
- Final
ModelResponse/usage missing parts that were streamed. - Cancellation not cleaning up the underlying provider stream.
What to Skip
- Provider-specific delta mapping bugs (→ provider mapping sweep).
- Speculation without a deterministic failing reproduction.
- Behavior already tracked by an open issue — search issues first.
Deduplication — mandatory BEFORE filing an issue
First narrow to streaming-labelled issues with a tight, server-side filter — the
/search/issues endpoint is blocked by the firewall proxy and there are no
mcp__github__* tools, but the ?labels= filter on the issue-list endpoint is
allowed. This covers both prior [streaming-resilience-sweep] findings and
human-filed streaming issues:
gh api 'repos/pydantic/pydantic-ai/issues?state=open&labels=streaming&per_page=100' \
--jq '.[] | select(.pull_request == null) | {number, title}'
Only if that is inconclusive, widen to a full open-issue scan and grep locally for "stream_output" / "stream_text":
gh api --paginate 'repos/pydantic/pydantic-ai/issues?state=open&per_page=100' \
--jq '.[] | select(.pull_request == null) | {number, title, labels: [.labels[].name]}'
If a matching issue exists, call mcp__safeoutputs__noop immediately.
Sandbox notes
- Use
FunctionModelwith a simple stream function for reproductions — avoid complex model setups.
Quality Gate — When to Noop
mcp__safeoutputs__noop is the expected outcome most runs. Only file with a deterministic,
minimal, failing streaming reproduction and captured event trace.
Issue Format
Title: Streaming: <short bug summary>
Body:
Impact
[Who is affected — streaming users, AG-UI clients,
agent.iterusers]Focus Area & Code Path
[Which streaming surface;
file:line]Reproduction
[The new streaming test — full code — and the command]
Expected vs Actual
Expected event/result sequence: … Actual: … [captured trace]
Evidence
- [Captured event trace / output;
path:linereferences]Adversarial review
- Reproduced on
main: [exact command + real captured trace — confirm the asymmetry/failure actually exists, not a false premise]- Existing tests checked: [streaming tests read; none assert the current behavior, and the fix doesn't break them]
- Ruled out by-design: [sibling streaming methods behave the same / nearby comment / maintainer decision checked]
- Not a duplicate: [label-filtered dedup returned nothing]