fix: preserve transformed Responses stream events - #2365
Conversation
responsesSessionStream read each inner event in Next() and then called inner.Current() again from Current(). Consumptive transformed Responses streams advance on every Current() call, so events were skipped in pairs and response.completed never reached the client, surfacing an incomplete-stream error. Cache the event consumed by Next() and return it from Current(), clearing it when the stream is exhausted. Adds a regression test verifying every wrapped event reaches the downstream consumer.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughThe response session stream now caches its current event during iteration. Exhaustion clears the cache, and consumers read the cached event. A regression test verifies that wrapped streams preserve all upstream events. ChangesResponse stream handling
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to Responses stream consumers now receive each transformed event, including terminal completion events, rather than advancing the underlying stream a second time. The targeted regression coverage supports the intended behavior with no remaining merge-blocking risk. Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR fixes transformed Responses streams by caching the event consumed during
Confidence Score: 5/5The PR appears safe to merge, with the stream wrapper now preserving every event for downstream consumers. The changed wrapper follows existing consumer iteration patterns, preserves terminal recording through its independent chunk buffer, and avoids the prior second read from potentially consumptive inner streams.
|
| Filename | Overview |
|---|---|
| internal/server/orchestrator/responses_session.go | Caches each event during Next() so session recording and downstream delivery observe the same stream item without a second consumptive read. |
| internal/server/orchestrator/responses_session_test.go | Adds focused regression coverage demonstrating that all wrapped stream events are delivered in order. |
Reviews (1): Last reviewed commit: "fix: preserve transformed Responses stre..." | Re-trigger Greptile
* upstream/unstable: (91 commits) fix(openai): complete cross-format request conversion (looplj#2377) fix: window deploy scripts, close looplj#2376 (looplj#2386) feat: add ZenMux channels and quota-aware UI (looplj#2381) fix(api): report server stream deadlines to SSE clients (looplj#2362) fix(frontend): handle empty channel error messages (looplj#2375) fix(frontend): silence transient channel polling errors (looplj#2382) fix(trace): stop persisting traces for embedding requests (looplj#2378) fix(anthropic): preserve client cache_control breakpoints to keep prompt cache hits (looplj#2342) fix(images): accept application/json in passthrough mode for /images/edits (looplj#2336) (looplj#2370) fix(openai): prevent zero-status errors from returning HTTP 200 (looplj#2372) fix(openai): preserve request contracts across transports (looplj#2374) Pass the caller's context through the v0.3.0 and v0.4.0 migrations (looplj#2348) fix(openai): tolerate object-shaped usage cost (looplj#2373) feat: add opencode session header, close looplj#2361 (looplj#2369) fix(anthropic): reject invalid tool input on clean EOF (looplj#2363) fix: dockerfile compitable, close looplj#2367 (looplj#2368) fix: preserve transformed Responses stream events (looplj#2365) fix(codex): strip user field from outbound requests (looplj#2352) fix(frontend): repair height-constrained scroll layout in mobile dialogs (looplj#2354) feat: show codex reset details, close looplj#2301 (looplj#2360) ... # Conflicts: # .github/workflows/docker-unstable.yml # .gitignore # docker-compose.yml # frontend/src/features/models/data/providers.json # frontend/src/features/models/data/providers.ts # llm/transformer/anthropic/inbound_convert.go # llm/transformer/anthropic/thinking_test.go
responsesSessionStream read each inner event in Next() and then called inner.Current() again from Current(). Consumptive transformed Responses streams advance on every Current() call, so events were skipped in pairs and response.completed never reached the client, surfacing an incomplete-stream error. Cache the event consumed by Next() and return it from Current(), clearing it when the stream is exhausted. Adds a regression test verifying every wrapped event reaches the downstream consumer.
Summary
Cache the event consumed by
responsesSessionStream.Nextand return that cached event fromCurrentinstead of callinginner.Current()a second time. Add a regression test proving every wrapped event reaches the downstream consumer.Root cause
For transformed (non-pass-through) Responses streams,
responsesSessionStream.Nextreads one event from the inner stream to record session data, andCurrentthen callsinner.Current()again. Queue-backed streams such asresponsesInboundStreamadvance their cursor on everyCurrent()call, so events were consumed in pairs:Nextconsumed event 0 internallyNextconsumed event 2 internallyThe client never received
response.completed, so the request ended asstream ended without terminal event or completed response.Native Responses pass-through masked the bug because its
Current()returns a cached pointer and is idempotent.Impact
Affected when all of the following are true:
openai/responsesstream=trueCurrent()semanticsThis includes Responses requests transformed through Chat Completions, Anthropic, Gemini, or a non-pass-through Responses path. Both SSE and downstream Responses WebSocket use the same
Next/Currentcontract.Verification
TestResponsesSessionStreamPreservesEveryEventForConsumer.Related
Summary by CodeRabbit