fix(openai): handle done-only/content-part responses - #11621
Conversation
PR Review: fix(openai-codex): handle done-only/content-part responsesOverall AssessmentIs this PR sound? Mostly yes. The core problem is well-defined: some Codex/Spark API variants omit expected delta events and only provide assistant text in FindingsP1 (High) --
|
| Severity | Count | Key Issue |
|---|---|---|
| P1 | 1 | content_part handlers missing double-emission guard |
| P2 | 3 | Scope creep to native handler; raw-stream path inconsistency; code duplication |
| P3 | 2 | Missing changeset; no regression test for happy path |
The fix addresses a real user-facing bug and the approach is sound. The P1 regarding content_part double-emission should be addressed before merge. The P2 items are worth discussion but are not blockers if the team is comfortable with the current scope.
|
Addressed review feedback in ae6589e:\n\n- Added duplicate guards for content_part fallback text in both providers by treating content_part as fallback-only when text deltas were already observed (sawTextDeltaInCurrentResponse).\n- Added regression tests in both provider suites for non-fallback streaming paths to ensure no double-emission when deltas and done/content_part events are both present.\n- Clarified raw SSE fallback flow in openai-native and removed stale no-op status classification for output_item.done / content_part.* (those events are now delegated through coreHandledEventTypes to processEvent).\n- Added a changeset for the user-facing bugfix.\n- Updated PR title to reflect scope (ix(openai)).\n\nI left the shared-event-parser extraction as follow-up work to keep this bugfix PR surgical. |
ghost
left a comment
There was a problem hiding this comment.
Re-review: All P0/P1 issues resolved
The latest commit addresses the findings from the initial review:
P1 (content_part double-emission) -- Fixed. A new sawTextDeltaInCurrentResponse flag precisely tracks whether text arrived through streaming deltas. The content_part.added/done handlers now correctly guard with !this.sawTextDeltaInCurrentResponse before emitting, preventing double-emission when both deltas and content_part events are sent by a model. This is a clean separation of concerns -- sawTextDeltaInCurrentResponse guards content_part fallbacks, while sawTextOutputInCurrentResponse guards done-event and completed-event fallbacks.
P2 (raw-stream no-op) -- Fixed. response.output_item.done, response.content_part.added, and response.content_part.done have been removed from the "Status events - no action needed" block in the SSE/raw-stream fallback path. These event types are now in coreHandledEventTypes and routed to processEvent(), so the fallback logic applies consistently across both SDK and raw-stream paths.
P3 (changeset) -- Fixed. A changeset entry (.changeset/sly-candles-hide.md) is now included.
P3 (regression tests) -- Fixed. New tests verify that text is NOT duplicated when both delta and content_part events are emitted (in both codex and native handlers).
The remaining P2 items (scope naming in PR title, code duplication between handlers) are noted but not blocking. Approving.
hannesrudolph
left a comment
There was a problem hiding this comment.
Reviewed by Rooviewer (Task #72955). The P1 double-emission bug is successfully fixed with the sawTextDeltaInCurrentResponse guard. Tests cover the regression path. Approving.
Summary
Why
Some Codex/Spark responses can omit expected delta events, which caused:
Unexpected API Response: The language model did not provide any assistant messages.Testing
pnpm vitest run api/providers/__tests__/openai-codex-native-tool-calls.spec.ts(insrc)pnpm vitest run api/providers/__tests__/openai-codex.spec.ts api/providers/__tests__/openai-codex-native-tool-calls.spec.ts(insrc)Start a new Roo Code Cloud session on this branch