Skip to content

fix(console): emit complete Responses-API SSE lifecycle for converted streams - #40210

Open
Yuxin-Qiao wants to merge 2 commits into
anomalyco:devfrom
Yuxin-Qiao:fix/console-responses-stream-events
Open

fix(console): emit complete Responses-API SSE lifecycle for converted streams#40210
Yuxin-Qiao wants to merge 2 commits into
anomalyco:devfrom
Yuxin-Qiao:fix/console-responses-stream-events

Conversation

@Yuxin-Qiao

@Yuxin-Qiao Yuxin-Qiao commented Aug 3, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #40171

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Two gateway fixes for the Zen/Go /v1/responses endpoint when the upstream speaks chat completions (oa-compat):

  1. Complete Responses-API SSE lifecycle. The stream converter only emitted response.output_text.delta and response.completed, so Responses-API clients (e.g. the Codex CLI) never saw response.created, response.in_progress, response.output_item.added, response.content_part.added or response.output_item.done and could not complete a turn. The converter is now stateful (createToOpenaiChunk) and emits the full sequence in order: created -> in_progress -> output_item.added (message) -> content_part.added -> output_text.delta -> output_item.done (with accumulated text) -> completed (with output, stop_reason, usage). Tool-call streams additionally get function_call_arguments.done and output_item.done.

  2. Drop unsupported tool types when converting to chat completions. Codex always sends a web_search tool; toOaCompatibleRequest mapped every tool into {"type":"function","function":{...}}, producing an undefined name that upstream serde rejects with tools[N].function: missing field "name", 400ing the whole request. Non-function tools are now filtered out (chat-completions upstreams have no equivalent), and function tools keep their nested function.name.

Verified live before the fix: deepseek-v4-flash on zen/go/v1 streamed only output_text.delta -> completed -> [DONE] -> ping; deepseek-v4-flash-free on zen/v1 rejects a web_search tool with the serde error above. Note that the paid tier has since been switched server-side to DeepSeek's native /v1/responses (full lifecycle observed live on 2026-08-05), but the free tier and any remaining oa-compat-backed models still hit both issues.

How did you verify your code works?

  • Added packages/console/app/test/openaiResponsesStream.test.ts: text-stream lifecycle ordering with a single response.created, accumulated text in output_item.done/completed, tool-call lifecycle (function_call_arguments.done, output_item.done), stream with no visible text, [DONE] passthrough.
  • Added packages/console/app/test/openaiCompatibleRequest.test.ts: web_search dropped while function tools are kept with their nested name, chat-completions tools pass through, no-tools stays undefined.
  • Ran the same assertions through a Node type-stripping harness locally (bun is not installed in this environment); all passed.

Screenshots / recordings

N/A (server-side gateway change).

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@JINGMINGRUO

Copy link
Copy Markdown

Thanks for working on this! I am a Codex CLI user with an OpenCode Go subscription, and I am hitting exactly the failure described in #40171: with deepseek-v4-flash on https://opencode.ai/zen/go/v1 and wire_api = responses, streaming turns start but never complete because the lifecycle events (response.created, response.output_item.added, response.content_part.added, response.output_item.done) are missing. Long coding tasks are effectively unusable through Codex right now.

This fix looks like exactly what the gateway needs: completing the SSE lifecycle for the chat-to-responses conversion that is already happening. It would unblock a lot of Codex users on OpenCode Go, and I would be happy to test it end-to-end once it is merged.

One heads-up: the issue-compliance bot flagged that the PR description is missing the required template sections and said it would auto-close within 2 hours unless updated. @Yuxin-Qiao, could you fill in the PR description template so this stays open while it waits for review? Maintainers, this one would be great to prioritize if you have a moment.

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@Yuxin-Qiao

Copy link
Copy Markdown
Author

Thanks for the heads-up and the offer to test! I updated the PR description to follow the template, and the bot confirmed it now meets the contributing guidelines. Appreciate the context on how the missing lifecycle events break Codex CLI turns end to end.

@Yuxin-Qiao

Copy link
Copy Markdown
Author

Just pushed a second fix to this branch (3189c93): the gateway was mapping every tool in the request - including Codex's web_search - into {"type":"function","function":{...}}, which produced an undefined name and the exact tools[N].function: missing field "name" 400 you hit. Non-function tools are now dropped when converting to chat completions, and function tools keep their nested function.name. Also verified today that the paid tier (go/v1) has been switched server-side to DeepSeek native /v1/responses (full lifecycle + reasoning_text.delta observed), but zen/v1 free with deepseek-v4-flash-free still shows both the incomplete SSE and the web_search 400, so this PR should cover your remaining case. Happy to re-check dumps if you post them.

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.

[BUG] Go service /v1/responses returns HTTP 200 but emits an incomplete SSE event stream, breaking Codex-style clients

2 participants