Skip to content

feat: add run_with_events() for a direct event stream - #4

Merged
sjwan01 merged 5 commits into
mainfrom
feature/run-with-events
Aug 7, 2026
Merged

feat: add run_with_events() for a direct event stream#4
sjwan01 merged 5 commits into
mainfrom
feature/run-with-events

Conversation

@sjwan01

@sjwan01 sjwan01 commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Summary

Adds AgentRunner.run_with_events() — a streaming entry point that yields normalized runtime events directly to a bare consumer, with no extension required.

run_stream() forwards only chunks yielded by extensions implementing on_agent_runner_event_stream; a bare consumer receives only run_end. run_with_events() solves this by internally installing a private bridge extension (_EventBridge) that captures chain-only tool events and forwards them alongside streamed tokens.

Event shapes

type payload
token {"type": "token", "chunk": str} — one streamed text chunk
tool_call {"type": "tool_call", "tool_name": str, "tool_call_id": str, "args": dict}
tool_result {"type": "tool_result", "tool_name": str, "tool_call_id": str, "content": Any, "is_error": bool}
run_end {"type": "run_end", "session_id": str, "output": str, "new_messages": list, "usage": ...}

Ordering: tool events precede the token chunk of the text that follows them; the stream always ends with run_end. User extensions keep observing events under run_with_events().

Implementation

  • run_stream() body extracted into a private _run_stream_impl(streamers) shared by both entry points (no code duplication; the thinking warning is inherited).
  • _EventBridge (_internals.py): chain hook queues normalized tool_call/tool_result events; stream hook drains the queue (preserving order) then yields token for TOKEN_STREAM. Never mutates chain data.
  • run_stream() behavior unchanged — existing tests untouched.

Known limitations (documented)

  • Inherits the pydantic-ai thinking + streaming + tools defect (post-tool-call text may be lost when thinking_enabled=True; run_end.output not a reliable fallback). Warning emitted once on first iteration; prefer run() when thinking is enabled. See README Known issues.
  • Events are interception-point snapshots: tool_call.args / tool_result.content show pre-rewrite values; tool_result.is_error is always False (hooks never set it).

Verification

  • make check: ruff ✅ mypy strict ✅ 157 passed, 7 skipped (7 = Postgres, run in CI).
  • New tests: bare-consumer event stream (order + shapes), warning inheritance, run_stream() unchanged, extension coexistence, early-abort restore, snapshot semantics.

@sjwan01
sjwan01 merged commit 9f46cb2 into main Aug 7, 2026
8 checks passed
@sjwan01
sjwan01 deleted the feature/run-with-events branch August 7, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant