Skip to content

fix(apicompat): surface reasoning-only chat streams#3016

Open
Fool0ntheHill wants to merge 1 commit into
Wei-Shaw:mainfrom
Fool0ntheHill:codex/deepseek-cc-responses-bridge
Open

fix(apicompat): surface reasoning-only chat streams#3016
Fool0ntheHill wants to merge 1 commit into
Wei-Shaw:mainfrom
Fool0ntheHill:codex/deepseek-cc-responses-bridge

Conversation

@Fool0ntheHill
Copy link
Copy Markdown

Summary

Fix Chat Completions -> Responses streaming compatibility for DeepSeek-backed models when the stream has reasoning output but no visible content.

In this case the bridge used to close a Responses reasoning item but never create a visible message / response.output_text.* item, so Codex clients could receive a successful stream that still looked blank.

This patch synthesizes a visible fallback message from collected reasoning text only when all of these are true at stream finalization:

  • no message item exists
  • no visible text has been emitted
  • reasoning text is non-blank
  • no tool calls are present

Normal content streams and tool-call streams are unchanged.

Real sub2api SSE sample

Captured from a real sub2api /v1/responses streaming request routed to a DeepSeek-backed model, redacted and minimized.

Request shape:

{
  "model": "deepseek-v4-flash",
  "input": "只回复 pong",
  "stream": true,
  "max_output_tokens": 8,
  "reasoning": { "effort": "medium" }
}

Observed summary after this patch:
responses_output_text_delta=2
responses_reasoning_items=2
responses_message_items=2
responses_completed=1

Relevant SSE excerpt:
event: response.output_item.added
data: {"item":{"id":"<redacted>","status":"in_progress","summary":[],"type":"reasoning"},"output_index":0,"type":"response.output_item.added"}

event: response.reasoning_summary_text.delta
data: {"delta":"<reasoning delta>","item_id":"<redacted>","output_index":0,"summary_index":0,"type":"response.reasoning_summary_text.delta"}

event: response.output_item.done
data: {"item":{"id":"<redacted>","status":"completed","summary":[{"text":"<reasoning text>","type":"summary_text"}],"type":"reasoning"},"output_index":0,"type":"response.output_item.done"}

event: response.output_item.added
data: {"item":{"content":[{"text":"<fallback visible text>","type":"output_text"}],"id":"<redacted>","role":"assistant","status":"in_progress","type":"message"},"output_index":1,"type":"response.output_item.added"}

event: response.output_text.delta
data: {"content_index":0,"delta":"<fallback visible text delta>","item_id":"<redacted>","output_index":1,"type":"response.output_text.delta"}

event: response.output_text.done
data: {"content_index":0,"item_id":"<redacted>","output_index":1,"text":"<fallback visible text>","type":"response.output_text.done"}

event: response.completed
data: {"type":"response.completed","response":{"id":"<redacted>","object":"response","model":"deepseek-v4-flash","status":"completed","output":[{"type":"reasoning","id":"<redacted>","summary":[{"type":"summary_text","text":"<reasoning text>"}]},{"type":"message","id":"<redacted>","role":"assistant","content":[{"type":"output_text","text":"<fallback visible text>"}],"status":"completed"}]}}

Tests
Added lifecycle coverage for:
reasoning-only stream synthesizes visible text
blank reasoning does not synthesize visible text
normal content after reasoning does not duplicate fallback text
tool-call streams do not synthesize fallback visible text

Verified:
go test ./internal/pkg/apicompat -count=1
Note: I kept this PR scoped to the Chat Completions -> Responses bridge. On my local Windows environment, full go test ./... still exposes an unrelated existing internal/service WebSocket large-frame test issue, so I did not mix that fix into this PR.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

All contributors have signed the CLA. ✅
Posted by the CLA Assistant Lite bot.

@Fool0ntheHill
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@iuhl
Copy link
Copy Markdown

iuhl commented Jun 3, 2026

The failing backend-security job is caused by govulncheck running on Go 1.26.3. The reported stdlib vulnerabilities GO-2026-5039 and GO-2026-5037 are both fixed in Go 1.26.4. PR #3001 already applies the required Go 1.26.4 bump and its Security Scan passed. Once #3001 is merged or this branch rebases onto it, the backend-security failure should clear.

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.

2 participants