Skip to content

fix(deepseek): apply bounded JSON policy on HTTP SSE for Flash - #1006

Open
Michael-Han0608 wants to merge 2 commits into
lidge-jun:devfrom
Michael-Han0608:fix/deepseek-flash-http-sse-bounded-json
Open

fix(deepseek): apply bounded JSON policy on HTTP SSE for Flash#1006
Michael-Han0608 wants to merge 2 commits into
lidge-jun:devfrom
Michael-Han0608:fix/deepseek-flash-http-sse-bounded-json

Conversation

@Michael-Han0608

@Michael-Han0608 Michael-Han0608 commented Aug 4, 2026

Copy link
Copy Markdown

Summary

5dd965a13 introduced a DeepSeek Flash compatibility policy that forces a bounded upstream Responses body and then reframes it for the client. That policy is currently applied only when the inbound transport is WebSocket.

Codex Desktop still uses HTTP Responses/SSE by default (websockets is opt-in). On that default path, DeepSeek Flash can return useful output without a usable terminal event, leaving the client pending until idle timeout.

This PR applies the same bounded-upstream policy to HTTP Responses turns as well:

  1. When registry policy says upstream streaming is unreliable for the selected model, force stream: false upstream for both HTTP and WebSocket Responses turns.
  2. If the client requested stream: true on HTTP, reframe the completed JSON body into a minimal SSE sequence that always ends on a terminal event:
    • response.created
    • response.output_item.done for each output item
    • response.completed / response.failed / response.incomplete
  3. Preserve the existing WebSocket path by keeping bounded JSON for sendResponsesJsonAsEvents().

This keeps the historical registry field name (modelWebsocketUpstreamStreaming) to minimize churn; the caller now applies it beyond WebSocket-only turns.

Why

Default path today:

Codex Desktop
  -> HTTP POST /v1/responses (stream=true)
  -> OpenCodex
  -> DeepSeek POST /responses (stream=true)
  -> text/event-stream

After this change for DeepSeek Flash:

Codex Desktop
  -> HTTP POST /v1/responses (stream=true)
  -> OpenCodex
  -> DeepSeek POST /responses (stream=false)
  -> application/json
  -> OpenCodex synthesizes terminal SSE for the client

Test plan

  • bun test tests/deepseek-inbound-wire.test.ts
  • bun test tests/deepseek-reasoning-replay.test.ts
  • Local retest on Codex Desktop with websockets: false
    • tool continuation reaches final summary
    • plain-text turn completes
  • Local retest with websockets: true
    • no regression observed

Notes

Summary by CodeRabbit

  • New Features

    • Added support for streaming compatible bounded responses over both HTTP SSE and WebSocket connections.
    • Converts bounded JSON responses into complete client event sequences, including creation, completion, and terminal status events.
    • Preserves error and incomplete statuses during conversion.
  • Bug Fixes

    • Improved handling for models that do not support upstream streaming across supported transports.

Extend the DeepSeek Flash terminal-safe Responses path beyond WebSocket so
default Codex Desktop HTTP/SSE turns also force bounded upstream JSON and
reframe a complete client event sequence.
Copilot AI lite review requested due to automatic review settings August 4, 2026 13:43
@github-actions github-actions Bot added the bug Something isn't working label Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The proxy applies bounded upstream JSON compatibility to HTTP SSE and WebSocket Responses requests. It preserves the client’s stream preference and converts valid bounded JSON into client-facing SSE events when required.

Changes

Responses streaming compatibility

Layer / File(s) Summary
Transport-independent streaming policy
src/types.ts, src/server/responses/core.ts, src/providers/registry.ts
OcxParsedRequest preserves the client’s original stream preference. Responses upstream streaming policy now applies to HTTP and WebSocket transports. Registry comments document the expanded scope.
Bounded JSON to client events
src/server/responses/core.ts
Completed bounded JSON produces response.created, output-item completion, and terminal SSE events for streaming HTTP clients. WebSocket and non-streaming clients retain JSON responses. Invalid, oversized, or incomplete bodies return upstream errors.
Transport coverage and documentation
tests/deepseek-inbound-wire.test.ts, structure/04_transports-and-sidecars.md
Tests cover bounded JSON handling across HTTP SSE and WebSocket transports, including terminal statuses. Documentation describes compatibility for both transports.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant handleResponses
  participant DeepSeek
  Client->>handleResponses: Send Responses request
  handleResponses->>DeepSeek: Request bounded JSON
  DeepSeek-->>handleResponses: Return Responses JSON
  handleResponses-->>Client: Return SSE events or JSON
Loading

Possibly related PRs

Suggested reviewers: ingwannu, wibias

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: extending DeepSeek Flash's bounded JSON policy to HTTP SSE transport. It matches the PR's core objective of applying this compatibility policy beyond WebSocket.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the existing DeepSeek Flash “bounded upstream JSON” compatibility policy from WebSocket-only Responses turns to the default HTTP/SSE Responses path, synthesizing a terminal-safe SSE event sequence when the client requested streaming.

Changes:

  • Apply the registry’s modelWebsocketUpstreamStreaming: false policy to HTTP Responses turns (force stream: false upstream when streaming is unreliable).
  • When the client requested stream: true over HTTP and upstream is bounded JSON, synthesize a minimal SSE sequence ending in a terminal event (response.completed / response.failed / response.incomplete).
  • Update docs and expand tests to cover HTTP/SSE bounded-JSON reframing and terminal-status preservation.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/deepseek-inbound-wire.test.ts Adds coverage for bounded-JSON forcing on HTTP and SSE synthesis from completed JSON.
structure/04_transports-and-sidecars.md Updates transport documentation to reflect bounded-JSON reframing for both WS and HTTP/SSE clients.
src/types.ts Adds _clientRequestedStream to preserve client streaming intent after compatibility rewrites.
src/server/responses/core.ts Implements JSON→SSE reframing and applies bounded-JSON upstream policy beyond WebSocket turns.
src/providers/registry.ts Clarifies registry hint semantics and documents broader (HTTP+WS) applicability while preserving the field name.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2209 to +2212
const sseHeaders = new Headers(headers);
sseHeaders.set("content-type", "text/event-stream; charset=utf-8");
sseHeaders.set("cache-control", "no-cache");
return new Response(responsesJsonToClientSse(responseJson), {

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f4204eb60

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/responses/core.ts Outdated
Comment on lines +893 to +897
const responsesUpstreamStreaming = providerModelWebsocketUpstreamStreaming(
route.providerName,
route.provider,
route.modelId,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict bounded JSON policy to the Responses wire

For DeepSeek Flash requests originating from /v1/chat/completions or Anthropic Messages, inboundWire causes resolveWireProtocolOverride() to retain the openai-chat adapter, but this compatibility hint is resolved before that selection and then unconditionally sets parsed.stream = false. Consequently, the OpenAI Chat adapter sends stream: false upstream and streaming Chat/Claude clients receive no incremental output even though the terminal-event defect is specific to DeepSeek's Responses stream. Resolve the wire first and apply this policy only when the final adapter is openai-responses (or otherwise gate it on the Responses inbound).

AGENTS.md reference: src/AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Comment on lines +2203 to +2206
let responseJson: Record<string, unknown>;
try {
responseJson = JSON.parse(restoredText) as Record<string, unknown>;
} catch {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate bounded JSON before synthesizing SSE

When a streaming HTTP upstream returns syntactically valid but non-object JSON, this cast accepts it without validation. In particular, a successful application/json body of null reaches responsesJsonToClientSse(), which dereferences response.output and throws out of the request handler instead of returning the intended typed 502; arrays and primitives are similarly converted into a fabricated response.completed event. Validate that the parsed value is a non-array object with a Responses-compatible shape before reframing it, and return formatErrorResponse() otherwise.

AGENTS.md reference: src/AGENTS.md:L17-L17

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/responses/core.ts`:
- Around line 2202-2212: Validate the result assigned in the
parsed._clientRequestedStream SSE branch before passing it to
responsesJsonToClientSse: require a non-null, non-array object and return the
existing 502 upstream_error response for invalid JSON shapes, including null.
Add a regression test covering a null upstream JSON body.
- Around line 2209-2216: Update the synthesized SSE response in the
responsesJsonToClientSse flow to set Cache-Control to no-store instead of
no-cache, preventing authenticated completions from being stored. Extend the
existing HTTP SSE regression test to assert the response includes the no-store
directive.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 167de94c-6d18-447b-a2e9-0dda973df8c6

📥 Commits

Reviewing files that changed from the base of the PR and between e44d234 and 2f4204e.

📒 Files selected for processing (5)
  • src/providers/registry.ts
  • src/server/responses/core.ts
  • src/types.ts
  • structure/04_transports-and-sidecars.md
  • tests/deepseek-inbound-wire.test.ts

Comment thread src/server/responses/core.ts
Comment thread src/server/responses/core.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/deepseek-inbound-wire.test.ts`:
- Line 243: The test callback contains three `const payload` declarations which
violates TypeScript's lexical scoping rules for const declarations. Identify all
three payload const declarations within the same test callback and remove or
rename the duplicate declarations so only one const payload exists in that
scope. Preserve the payload declaration shown at line 243 and update any other
payload declarations with distinct variable names to avoid the duplicate const
error.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d798c9a7-4e3e-44c1-bdb8-132ad1bbc04f

📥 Commits

Reviewing files that changed from the base of the PR and between 2f4204e and 5c2b7bd.

📒 Files selected for processing (2)
  • src/server/responses/core.ts
  • tests/deepseek-inbound-wire.test.ts

test("HTTP stream clients reject null bounded JSON with a typed upstream error", async () => {
const response = await respondWithUpstreamJson(null);
expect(response.status).toBe(502);
const payload = (await response.json()) as { error?: { code?: string; message?: string } };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the duplicate payload declarations.

Line 243 declares const payload three times in the same test() callback. TypeScript rejects duplicate const declarations in one lexical scope. Bun cannot load this test module, so this regression coverage does not run.

Proposed fix
     const payload = (await response.json()) as { error?: { code?: string; message?: string } };
-    const payload = (await response.json()) as { error?: { code?: string; message?: string } };
-    const payload = (await response.json()) as { error?: { code?: string; message?: string } };

Based on learnings: repeated const declarations are valid only in separate test() callback scopes.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const payload = (await response.json()) as { error?: { code?: string; message?: string } };
const payload = (await response.json()) as { error?: { code?: string; message?: string } };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/deepseek-inbound-wire.test.ts` at line 243, The test callback contains
three `const payload` declarations which violates TypeScript's lexical scoping
rules for const declarations. Identify all three payload const declarations
within the same test callback and remove or rename the duplicate declarations so
only one const payload exists in that scope. Preserve the payload declaration
shown at line 243 and update any other payload declarations with distinct
variable names to avoid the duplicate const error.

Source: Learnings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants