V3 Issue #230 — Structured Provider Streaming Plan
Issue-specific implementation plan for #230, covering R006. This branch is based on the #229 PR head; keep its dependency explicit until #229 merges.
Goal: Expose typed provider stream events and complete ModelResponse terminal state while preserving cancellation, deadlines, retries, usage accounting, and legacy text streams.
Architecture: Add immutable SDK-independent event records in providers/models.py; implement streaming at the provider adapter layer; route both stream and non-stream requests through the existing shared runtime boundary in providers/calls.py. Preserve provider-native tool/reasoning continuation from #229. Keep runtime event redesign (#255) out of scope.
Constraints: No new dependency, provider calls, or tool execution. No retry/fallback after content is delivered. Cancellation, malformed arguments, duplicate call IDs, and protocol termination errors must be terminal. Legacy chat_stream() remains a text-only projection.
Scope and compatibility
- Update public provider exports, runtime callers, adapters, retry/worker cleanup, tests, and provider documentation.
- Keep final
ModelResponseauthoritative for finish reason, calls, usage, and continuation; usage updates are cumulative observations. - Unsupported adapters must fail explicitly rather than fabricate final-answer status or silently flatten structured output.
- Ollama must use its real newline-delimited streaming endpoint.
Task 1: Event contract and shared runtime boundary
Files: openkyrozen/providers/models.py, openkyrozen/providers/__init__.py, openkyrozen/providers/calls.py, tests.
- [x] Add immutable
TextDelta,ToolCallDelta,UsageUpdate,ResponseComplete, andResponseErrorevents. - [x] Add
chat_response_stream(..., *, tools=None)andget_model_events()with explicit legacy bridging and text projection. - [x] Ensure only one terminal event, no partial executable calls, and no lossy conversion of tool/error/cancelled responses.
- [x] Write and run failing contract/runtime regressions before implementation.
Task 2: Native adapter streams
Files: all adapter families in openkyrozen/providers/, adapter fixtures.
- [x] Implement structured native streams for OpenAI-compatible/Azure, Responses, Anthropic, Google/Vertex, Bedrock, Ollama, and Perplexity.
- [x] Preserve fragmented calls, response-local indexes, continuation/signature data, cumulative usage, finish reason, and provider response identity.
- [x] Validate assembled JSON once at completion; reject malformed/incomplete calls, duplicate IDs, missing terminators, and SDK failures.
- [x] Close underlying streams on failure, cancellation, or consumer closure; never retry/fallback after delivered deltas.
Task 3: Retry, usage, docs, and delivery
- [x] Integrate stream retries with the existing bounded worker/deadline/cancellation policy before the first response delta only.
- [x] Charge each successful provider response once from its final cumulative usage; retain successful-response estimation when usage is absent.
- [x] Keep
chat()andchat_stream()as explicit text-only compatibility projections. - [x] Document event semantics and compatibility boundary; add native-stream regression and offline acceptance.
- [x] Independently review, resolve material findings, sign and verify the commit, publish PR #230 against #229's branch, and wait for checks. Do not merge.
Validation
- [x] Test interleaving, fragmented IDs/names/arguments, empty chunks, signatures after text, cumulative usage, terminal errors, truncation, duplicate IDs, malformed arguments, cancellation, cleanup, and no fallback after content.
- [x] Run focused tests on Python 3.12 and 3.13, full tests,
make check,make lint,make docs-check, Go TUI tests, and offline agent/subagent/TUI acceptance. - [x] Report PASS, FAIL, and BLOCKED separately; no paid provider calls.
Validation record
- PASS: 93 focused provider/runtime tests on Python 3.12; 16 local SDK HTTP transport tests;
make check,make lint,make docs-check; Go TUI tests; offline agent, subagent, and TUI acceptance. Agent acceptance required loopback access and passed when run with the isolated database under/tmp. - PASS: final
workflow_dispatchCI completed successfully on the Python 3.12 and 3.13 full-suite jobs, plus wheel/TUI, installer and installed-update jobs. - BLOCKED locally: Python 3.13 dependencies are not installed in this workspace. Local full-suite execution hit unrelated sandbox restrictions in user-home update-lock paths, localhost binding, optional build/benchmark dependencies, and startup-state locations; affected provider suites passed separately and the full CI suites passed remotely.
- PASS: independent review found and verified fixes for compatible-provider reasoning replay and fallback capability overstatement; no remaining material finding.
- PASS: PR #278 targets the #229 branch, is attached to this task, and the signed commit is verified locally and by GitHub.
- BLOCKED: CodeRabbit review is disabled on this stacked PR base; independent review was performed and the full CI workflow passed. No live provider calls were made.