Skip to content

fix DeepSeek Responses UUID item ids for Codex - #940

Draft
mouzhi wants to merge 6 commits into
lidge-jun:devfrom
mouzhi:agent/deepseek-responses-item-id-normalize
Draft

fix DeepSeek Responses UUID item ids for Codex#940
mouzhi wants to merge 6 commits into
lidge-jun:devfrom
mouzhi:agent/deepseek-responses-item-id-normalize

Conversation

@mouzhi

@mouzhi mouzhi commented Aug 3, 2026

Copy link
Copy Markdown

Summary

  • Add opt-in responsesItemIdRepair.rewriteNonCanonicalIds for openai-responses passthrough providers.
  • Rewrite DeepSeek-style UUID response/item ids to Codex-friendly resp_ / msg_ / rs_ ids, fold reasoning_text into encrypted_content (while keeping plaintext content for DeepSeek replay), and ensure a terminal [DONE] trailer.
  • Keep SSE rewrite consumers alive when many events are dropped, including the Windows eager-relay path.
  • Make raw-id aliases type-scoped (message vs reasoning) and charge every newly retained alias through TranslatorBudget.
  • Enable the repair by default on the built-in DeepSeek registry preset so the native deepseek/deepseek-v4-flash Responses route no longer requires a hand-edited provider config for the [Bug]: Responses passthrough UUID item IDs leave Codex turn stuck on Thinking #938 UUID stall.

Fixes #938

Scope note

This PR is primarily the provider-local / built-in SSE UUID repair for #938.

It also turns the same repair on for the built-in DeepSeek preset, which covers the UUID-stall half of the #875/#946 residual on SSE. It does not claim a complete fix for the separate “no function_call_output continuation request is sent after tools” residual tracked in #875: that path still needs end-to-end tool-loop evidence on Codex App WebSocket after this lands.

Why

DeepSeek V4 Flash/Pro Responses API can return UUID item ids and raw reasoning_text streams. Codex App/CLI may stay on Thinking/Working even when OpenCodex logs HTTP 200 and reported usage. This keeps OpenCodex passthrough intact while normalizing the client-facing SSE for Codex.

Usage

{
  "providers": {
    "deepseeknew": {
      "adapter": "openai-responses",
      "baseUrl": "https://api.deepseek.com",
      "authMode": "key",
      "models": ["deepseek-v4-flash"],
      "responsesItemIdRepair": {
        "rewriteNonCanonicalIds": true,
        "repairMissingTerminalIds": true
      }
    }
  }
}

Built-in DeepSeek now seeds the same repair automatically via registry fill-only defaults.

Test plan

  • bun test tests/responses-item-id-repair.test.ts
  • bun test tests/responses-item-id-repair-deepseek.test.ts
  • bun test tests/sse-payload-rewrite.test.ts
  • Regression: shared placeholder across message/reasoning stays type-scoped
  • Regression: newly retained raw aliases after an already-mapped output_index charge TranslatorBudget
  • Local Codex CLI/Desktop validation with DeepSeek Responses before/after the initial fix

Summary by CodeRabbit

  • New Features

    • Added an optional setting to rewrite non-canonical response, message, and reasoning IDs into consistent canonical IDs.
    • Normalized reasoning streams, filtered intermediate events, and ensured completed responses receive a consistent [DONE] termination marker.
    • Added configurable end-of-stream trailers for streamed responses.
    • Enabled ID rewriting and terminal repair by default for DeepSeek.
  • Bug Fixes

    • Preserved unchanged SSE event formatting.
    • Correctly removes dropped events without disrupting subsequent streamed output.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

PR quality gates passed

This pull request now targets dev with acceptable ancestry and description.

The [WRONG BRANCH] title prefix has been removed. The pull request has been marked ready for review again.

@github-actions github-actions Bot changed the title fix DeepSeek Responses UUID item ids for Codex [WRONG BRANCH] fix DeepSeek Responses UUID item ids for Codex Aug 3, 2026
@github-actions
github-actions Bot marked this pull request as draft August 3, 2026 08:23
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds opt-in rewriting for non-canonical Responses IDs and reasoning streams. It also allows SSE rewrites to drop events, clears dropped buffered data, and emits a [DONE] trailer after completed responses.

Changes

Responses SSE repair

Layer / File(s) Summary
Repair configuration and provider activation
src/types.ts, src/config.ts, src/providers/*, docs-site/src/content/docs/*/reference/configuration/providers.md, tests/responses-item-id-repair.test.ts
The rewriteNonCanonicalIds option is added to provider configuration, registry metadata, DeepSeek defaults, enrichment, documentation, and opt-in detection.
Nullable SSE relay and trailer handling
src/server/sse-payload-rewrite.ts, src/server/relay-eager.ts
Rewrite callbacks can return null to drop events. Relay paths clear dropped buffered data, continue reading when no output is emitted, preserve unchanged formatting, and emit configured trailers.
Responses ID and reasoning normalization
src/server/responses-item-id-repair.ts
The repair pipeline maps raw IDs to canonical IDs, folds reasoning text into encrypted content, removes unsupported logprobs, suppresses intermediate events, rewrites snapshots, and generates missing [DONE] trailers.
Repair integration and validation
src/server/responses/core.ts, tests/responses-item-id-repair-deepseek.test.ts, tests/sse-payload-rewrite.test.ts
Responses core wires shared handlers, client-visible response ID mappings, and trailers into both relay paths. Tests cover ID rewriting, reasoning normalization, aliases, translator budgets, suppression, and stream termination.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Provider
  participant responsesItemIdRepair
  participant relaySseWithPayloadRewrite
  participant Client
  Provider->>responsesItemIdRepair: Responses SSE events
  responsesItemIdRepair->>responsesItemIdRepair: Map IDs and normalize reasoning
  responsesItemIdRepair-->>relaySseWithPayloadRewrite: Event or null
  relaySseWithPayloadRewrite->>Client: Canonical event
  responsesItemIdRepair-->>relaySseWithPayloadRewrite: Completion trailer
  relaySseWithPayloadRewrite->>Client: [DONE]
Loading

Possibly related PRs

Suggested reviewers: wibias, lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 DeepSeek Responses UUID item-ID repair for Codex.
Linked Issues check ✅ Passed The repair rewrites UUID response, reasoning, and message IDs consistently across SSE events, preserves request-local mappings, and keeps function-call IDs unchanged [#938].
Out of Scope Changes check ✅ Passed All changes support #938 through repair logic, relay integration, registry configuration, tests, and documentation; no unrelated code changes are evident.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/relay-eager.ts (1)

147-170: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add the repair trailer to the eager relay.

On Windows, win32EagerRewrite routes repaired openai-responses streams through relaySseEagerBounded and supplies rewritePayload (src/server/responses/core.ts:1760-1817). The eager EOF branch only flushes the rewrite tail (src/server/relay-eager.ts:219-227). If the upstream sends response.completed without data: [DONE], sawTerminal() is already true, so no synthetic terminal is emitted and the client stream closes without [DONE].

Add a trailer option to EagerRelayOptions, pass the same createResponsesItemIdDoneTrailer result used by relaySseWithPayloadRewrite (src/server/responses-item-id-repair.ts:423-435), and enqueue it after flushRewriteTail() before closing the eager stream.

🤖 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 `@src/server/relay-eager.ts` around lines 147 - 170, Add a trailer field to
EagerRelayOptions and update the eager EOF path to enqueue it after
flushRewriteTail() and before closing the stream. In the win32EagerRewrite call
to relaySseEagerBounded, pass the same createResponsesItemIdDoneTrailer result
already used by relaySseWithPayloadRewrite, ensuring repaired streams emit
[DONE] even when response.completed arrives without a data: [DONE] event.
🤖 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-item-id-repair.ts`:
- Around line 148-157: Update mapResponseId so newly minted canonical IDs
incorporate the current responseIdMap size, while retaining the existing
scope-based prefix. Ensure each distinct non-canonical rawId receives a unique
ID and preserve reuse of previously mapped IDs.
- Around line 159-183: The normalizeReasoningItem path can emit a reasoning item
without an id when rewriteNonCanonicalIds is enabled without
repairMissingTerminalIds. Update the id resolution around
rememberMappedId/mapRawId to mint a canonical reasoning id for missing ids
whenever id rewriting is active, preserving existing ids and mappings otherwise.
Add a focused regression test beside the existing DeepSeek repair tests covering
a missing-id reasoning item with only rewriteNonCanonicalIds enabled and
asserting the canonical id format.
- Around line 407-426: Refactor createResponsesItemIdPayloadRewrite into a
single factory that owns the repair state in a closure and returns both the
payload rewrite and its trailer callback, so composition cannot lose __state.
Thread the returned trailer separately through relaySseWithPayloadRewrite and
the eager relay path, updating callers and tests accordingly; then remove the
existing createResponsesItemIdPayloadRewrite and
createResponsesItemIdDoneTrailer exports.

In `@src/server/sse-payload-rewrite.ts`:
- Around line 143-171: Update both payload checks in the pull relay’s normal
block and flush-final handling to test `payload !== null` instead of truthiness,
ensuring empty string payloads still pass through `rewrite` and can be
suppressed consistently with the eager relay.

In `@tests/responses-item-id-repair-deepseek.test.ts`:
- Around line 54-90: Add a focused test beside the existing response item ID
repair test that feeds response.completed followed by an upstream data: [DONE]
trailer through relaySseWithResponsesItemIdRepair, then asserts the repaired
stream contains exactly one data: [DONE] occurrence. This should specifically
exercise the sawDoneTrailer guard rather than only the synthetic trailer path.

---

Outside diff comments:
In `@src/server/relay-eager.ts`:
- Around line 147-170: Add a trailer field to EagerRelayOptions and update the
eager EOF path to enqueue it after flushRewriteTail() and before closing the
stream. In the win32EagerRewrite call to relaySseEagerBounded, pass the same
createResponsesItemIdDoneTrailer result already used by
relaySseWithPayloadRewrite, ensuring repaired streams emit [DONE] even when
response.completed arrives without a data: [DONE] event.
🪄 Autofix (Beta)

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: 0c8caefe-4183-4f20-a286-b59910fdacc3

📥 Commits

Reviewing files that changed from the base of the PR and between f9b9440 and c214ffc.

📒 Files selected for processing (7)
  • src/config.ts
  • src/server/relay-eager.ts
  • src/server/responses-item-id-repair.ts
  • src/server/sse-payload-rewrite.ts
  • src/types.ts
  • tests/responses-item-id-repair-deepseek.test.ts
  • tests/responses-item-id-repair.test.ts

Comment thread src/server/responses-item-id-repair.ts
Comment thread src/server/responses-item-id-repair.ts Outdated
Comment thread src/server/sse-payload-rewrite.ts
Comment thread tests/responses-item-id-repair-deepseek.test.ts
@mouzhi
mouzhi force-pushed the agent/deepseek-responses-item-id-normalize branch from c214ffc to c86af23 Compare August 3, 2026 08:32
@mouzhi mouzhi changed the title [WRONG BRANCH] fix DeepSeek Responses UUID item ids for Codex fix DeepSeek Responses UUID item ids for Codex Aug 3, 2026
@mouzhi
mouzhi changed the base branch from main to dev August 3, 2026 08:33
@mouzhi
mouzhi marked this pull request as ready for review August 3, 2026 08:33
@github-actions github-actions Bot added the bug Something isn't working label Aug 3, 2026

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/relay-eager.ts (1)

229-239: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add the responses item-id repair trailer to the eager relay.

On the Windows rewrite path, src/server/responses/core.ts:1874-1876 passes only the payload rewrite. src/server/relay-eager.ts:229-239 flushes the rewrite tail but never invokes createResponsesItemIdDoneTrailer from src/server/responses-item-id-repair.ts:417-435. When the upstream sends response.completed without [DONE], the eager client stream ends without data: [DONE]\n\n, unlike the pull relay. Pass a trailer callback through EagerRelayHooks and enqueue it at upstream EOF. hooks.sawTerminal() may already be true for response.completed, so this is a missing client terminator, not necessarily a synthetic incomplete.

🤖 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 `@src/server/relay-eager.ts` around lines 229 - 239, The eager relay must emit
the responses item-id repair trailer at upstream EOF, including when
hooks.sawTerminal() is already true. Extend EagerRelayHooks with a trailer
callback, pass createResponsesItemIdDoneTrailer through the Windows rewrite path
in responses/core.ts, and invoke/enqueue the trailer from relay-eager.ts
alongside the rewrite tail before ending the stream; preserve synthetic
incomplete handling separately.
🤖 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-item-id-repair.ts`:
- Around line 286-295: Update accumulateReasoningText and the direct
reasoningTextByOutputIndex replacements in repairEventPayload to use one helper
that releases any existing entry before replacement, charges each appended delta
through TranslatorBudget, and retains the updated value. When the reasoning
stream ends, release the entire stored entry and remove it from
reasoningTextByOutputIndex.
- Around line 371-390: Remove the inner logprobs-removal block from the
output_text normalization branch, including its destructuring and reassignment,
while preserving the unconditional logprobs stripping block guarded by
state.rewriteNonCanonicalIds.
- Around line 297-341: Update the terminal-event tracking in repairEventPayload
so response.completed, response.failed, and response.incomplete all mark the
stream as terminal for trailer generation. Rename sawCompleted to
sawTerminalResponse throughout ResponsesItemIdRepairState and
createResponsesItemIdDoneTrailer, preserving the existing behavior that emits
[DONE] only when a terminal response event was observed.

In `@src/server/sse-payload-rewrite.ts`:
- Around line 177-196: Preserve the item-ID rewrite reference in the composed
relay setup and pass createResponsesItemIdDoneTrailer(itemIdRewrite) as the
trailer to relaySseWithPayloadRewrite, matching the existing
relaySseWithResponsesItemIdRepair behavior. Add a composed-path test covering a
stream that ends without an upstream [DONE] event and verifies the synthetic
trailer is emitted.

---

Outside diff comments:
In `@src/server/relay-eager.ts`:
- Around line 229-239: The eager relay must emit the responses item-id repair
trailer at upstream EOF, including when hooks.sawTerminal() is already true.
Extend EagerRelayHooks with a trailer callback, pass
createResponsesItemIdDoneTrailer through the Windows rewrite path in
responses/core.ts, and invoke/enqueue the trailer from relay-eager.ts alongside
the rewrite tail before ending the stream; preserve synthetic incomplete
handling separately.
🪄 Autofix (Beta)

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: b3e43054-f882-419e-acba-3e3638476f23

📥 Commits

Reviewing files that changed from the base of the PR and between c214ffc and c86af23.

📒 Files selected for processing (5)
  • src/config.ts
  • src/server/relay-eager.ts
  • src/server/responses-item-id-repair.ts
  • src/server/sse-payload-rewrite.ts
  • src/types.ts

Comment thread src/server/responses-item-id-repair.ts Outdated
Comment thread src/server/responses-item-id-repair.ts
Comment thread src/server/responses-item-id-repair.ts
Comment thread src/server/sse-payload-rewrite.ts

@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: c86af23544

ℹ️ 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 on lines +179 to +180
if (terminal || text) {
next.encrypted_content = encodeReasoningEnvelope({ txt: text || " " });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve plaintext reasoning for DeepSeek replay

When Codex replays this repaired reasoning item during a stateless DeepSeek tool-call continuation, it sends back only the new ocxr1 envelope because this normalization removes the original content. sanitizeReasoningInputContent then strips that proxy-only envelope before native forwarding; even with preserveResponsesReasoningContent, there is no plaintext content left to preserve, so DeepSeek loses the prior reasoning required for the continuation. Decode the envelope back into reasoning_text for providers that preserve raw reasoning, or otherwise retain a replayable plaintext representation.

Useful? React with 👍 / 👎.

Comment thread src/server/responses-item-id-repair.ts Outdated
Comment on lines +417 to +423
export function createResponsesItemIdDoneTrailer(
rewrite: SsePayloadRewrite,
): () => string | undefined {
return () => {
const state = (rewrite as SsePayloadRewrite & { __state?: ResponsesItemIdRepairState }).__state;
if (!state) return undefined;
if (state.sawCompleted && !state.sawDoneTrailer) return "data: [DONE]\n\n";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Attach the DONE trailer to the main Responses relay

For the normal /v1/responses passthrough flow, core.ts constructs this repair through createResponsesItemIdPayloadRewrite directly and calls relaySseWithPayloadRewrite without the trailer option; the Windows eager path likewise receives only the payload rewrite. Therefore, when DeepSeek cleanly ends after response.completed without [DONE], this callback is never consulted and Codex can remain stuck on Working—the exact failure this change intends to repair. Thread the repair trailer through both the composed pull relay and eager relay paths.

Useful? React with 👍 / 👎.

Comment thread src/config.ts
message: z.array(z.string().min(1)).optional(),
reasoning: z.array(z.string().min(1)).optional(),
repairMissingTerminalIds: z.boolean().optional(),
rewriteNonCanonicalIds: z.boolean().optional(),

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 Document the new repair option

Custom Responses providers must explicitly set responsesItemIdRepair.rewriteNonCanonicalIds, but this new user-facing configuration field is not documented anywhere under docs-site/; users installing the released package therefore have no shipped reference for enabling the DeepSeek compatibility behavior or understanding its stream normalization effects. Add it to the relevant provider/configuration documentation.

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

Useful? React with 👍 / 👎.

Comment thread src/server/responses-item-id-repair.ts Outdated
Comment on lines +291 to +294
state.reasoningTextByOutputIndex.set(
outputIndex,
(state.reasoningTextByOutputIndex.get(outputIndex) ?? "") + delta,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bound accumulated reasoning text

When rewriteNonCanonicalIds is enabled, every response.reasoning_text.delta is retained here by concatenating it onto the complete prior text, without charging the request's TranslatorBudget or enforcing a cumulative limit. A provider that emits a very long reasoning stream can therefore grow the proxy heap without bound and incur quadratic string-copying work before the terminal item is converted to an envelope. Track and release these retained bytes through the translator budget and fail at the existing turn limit, or use an equivalently bounded accumulator.

Useful? React with 👍 / 👎.

Comment on lines +152 to +155
const existing = state.responseIdMap.get(rawId);
if (existing) return existing;
const minted = `resp_ocx_${state.scope}`;
state.responseIdMap.set(rawId, minted);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Alias rewritten response IDs in continuation state

When Codex chains a turn with previous_response_id, it sends the synthetic resp_ocx_* value minted here, but the passthrough inspection branch records the untouched upstream response under its raw UUID. The local continuation cache therefore cannot find the synthetic ID; a stateless DeepSeek route loses the earlier input expansion, while a non-stateless custom route may forward an ID the upstream has never issued. Preserve the client-visible prefix while registering an alias from the synthetic response ID to the raw completed snapshot, or otherwise keep continuation lookup synchronized with this rewrite.

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-item-id-repair.ts`:
- Around line 156-159: Update the response-ID minting path around
responseIdMap.set to charge TranslatorBudget for each new rawId-to-minted
mapping before storing it, using the existing item_ids budget scope and matching
the accounting used by the equivalent item-ID mappings. Add a regression test
with a small budget that verifies many distinct provider response IDs cannot
grow responseIdMap beyond the request limit.

In `@tests/responses-item-id-repair-deepseek.test.ts`:
- Around line 122-131: Add a test alongside the existing trailer test that
invokes relaySseEagerBounded with a dropping rewritePayload and the repair
trailer, using a terminal response stream without an upstream [DONE]. Assert the
repaired output contains exactly one data: [DONE], covering the eager trailer
path and its wiring through relaySseEagerBounded.
🪄 Autofix (Beta)

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: 1c958d9d-ff10-4393-8f66-01a9c03fe8f2

📥 Commits

Reviewing files that changed from the base of the PR and between c86af23 and 3c32ac8.

📒 Files selected for processing (5)
  • src/server/relay-eager.ts
  • src/server/responses-item-id-repair.ts
  • src/server/responses/core.ts
  • src/server/sse-payload-rewrite.ts
  • tests/responses-item-id-repair-deepseek.test.ts

Comment thread src/server/responses-item-id-repair.ts
Comment thread tests/responses-item-id-repair-deepseek.test.ts

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks — the opt-in SSE normalization is valuable and is the right direction for #938, but I found two correctness blockers on the current head.

  1. rawToCanonical is global across message/reasoning item types and is consulted before the event type. If a provider reuses the same configured placeholder ID for both types, a reasoning lifecycle event can be rewritten to the msg_... ID. Please make the alias mapping type-scoped and add a regression where message and reasoning share one raw placeholder while their lifecycle IDs remain distinct.

  2. When an output index already has a mapped ID, each new raw alias is retained in rawToCanonical without charging TranslatorBudget. A malformed stream can therefore grow the alias map outside the request budget. Charge every newly retained alias and add a small-budget regression proving the map is bounded.

There is also an important scope distinction. This PR can fix #938 as an explicit provider-local SSE compatibility option, but it does not yet fix the built-in DeepSeek/Codex App residual tracked in #875/#946: the built-in DeepSeek preset does not enable the option, and the WebSocket path forces bounded upstream JSON and bypasses this SSE rewrite. Either keep the PR and its closing claim explicitly scoped to #938, or add the built-in preset plus JSON/WebSocket normalization and an end-to-end function-call continuation test.

Please also sync with current dev and rerun the required full CI after the corrections. The current head is 21 commits behind and only target/label checks have run; I am not approving external code execution for this head while the blockers remain.

mouzhi added 4 commits August 3, 2026 22:24
Make openai-responses passthrough optionally rewrite non-canonical message/reasoning ids, fold reasoning_text into encrypted_content, and keep SSE rewrite consumers alive when events are dropped so DeepSeek Responses works with Codex CLI/TUI.
Make response ids unique, mint missing item ids under rewriteNonCanonicalIds, share rewrite/trailer state through one factory, pass trailers through the Windows eager path, and add focused regression tests. Also link the PR to lidge-jun#938.
Budget responseIdMap and reasoning text, emit [DONE] for failed/incomplete,
preserve plaintext reasoning for DeepSeek replay, alias rewritten response
ids into local previous_response_id state, document rewriteNonCanonicalIds,
and cover eager trailer plus budget regressions.
Scope rawToCanonical by message/reasoning, charge newly retained aliases,
and turn on rewriteNonCanonicalIds for the built-in DeepSeek preset so the
native Responses route gets the lidge-jun#938 UUID repair by default.
@mouzhi
mouzhi force-pushed the agent/deepseek-responses-item-id-normalize branch from a9db780 to 9f25360 Compare August 3, 2026 14:26

@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: 6

🤖 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 `@docs-site/src/content/docs/ja/reference/configuration/providers.md`:
- Around line 200-201: Document the enabled repair behavior after the JSON
example in all three translated pages:
docs-site/src/content/docs/ja/reference/configuration/providers.md lines
200-201, docs-site/src/content/docs/ko/reference/configuration/providers.md
lines 203-204, and
docs-site/src/content/docs/ru/reference/configuration/providers.md lines
248-249. Match the English source by explaining that rewriteNonCanonicalIds
rewrites UUID-style response/item IDs, normalizes reasoning_text to
encrypted_content, preserves plaintext for replay, and appends a terminal
[DONE], while disabled mode guarantees passthrough behavior.

In `@src/providers/derive.ts`:
- Line 272: Update the responsesItemIdRepair assignment in the derive flow to
clone its nested message and reasoning arrays instead of only shallow-copying
the config object. Preserve undefined fields and match the existing
collection-copying convention used for seed.models and seed.modelContextWindows,
ensuring runtime provider state cannot alias PROVIDER_REGISTRY.

In `@src/server/responses-item-id-repair.ts`:
- Around line 220-253: Update normalizeReasoningItem to initialize next from the
original item so status and future upstream metadata are preserved, while
continuing to normalize type, id, and summary. Explicitly rebuild or remove
proxy-owned content and encrypted_content before applying the existing reasoning
text/envelope logic, and add an assertion verifying status is retained.
- Around line 497-503: Update the composition test to call
createResponsesItemIdRepairHandlers instead of
createResponsesItemIdPayloadRewrite, while preserving the backward-compatible
helper export. Compose itemId.rewrite for payload rewriting and pass
itemId.trailer to relaySseWithPayloadRewrite so the repair trailer is emitted.
- Around line 383-407: Update the rewriteNonCanonicalIds flow in the response
event handling and trailer() so every retained entry in
reasoningTextByOutputIndex is synthesized into a reasoning item before the
terminal event is forwarded. Ensure this flush applies whenever the option is
enabled, including general provider configuration rather than relying on the
DeepSeek registry default, while preserving existing handling for
reasoning_text.* and reasoning content-part events.

In `@src/server/responses/core.ts`:
- Around line 1646-1658: Add a concise comment beside mapClientResponseId
explaining that it is assigned only for SSE responses, while JSON responses do
not use Responses item-ID repair; state that any future JSON repair support must
initialize the alias before the JSON path. Do not change the existing aliasing
logic.
🪄 Autofix (Beta)

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: 5e7adbba-a122-48b8-bf23-5631a2f825d0

📥 Commits

Reviewing files that changed from the base of the PR and between 3c32ac8 and 9f25360.

📒 Files selected for processing (15)
  • docs-site/src/content/docs/ja/reference/configuration/providers.md
  • docs-site/src/content/docs/ko/reference/configuration/providers.md
  • docs-site/src/content/docs/reference/configuration/providers.md
  • docs-site/src/content/docs/ru/reference/configuration/providers.md
  • docs-site/src/content/docs/zh-cn/reference/configuration/providers.md
  • src/config.ts
  • src/providers/derive.ts
  • src/providers/registry.ts
  • src/server/relay-eager.ts
  • src/server/responses-item-id-repair.ts
  • src/server/responses/core.ts
  • src/server/sse-payload-rewrite.ts
  • src/types.ts
  • tests/responses-item-id-repair-deepseek.test.ts
  • tests/responses-item-id-repair.test.ts

Comment thread docs-site/src/content/docs/ja/reference/configuration/providers.md
Comment thread src/providers/derive.ts Outdated
Comment thread src/server/responses-item-id-repair.ts
Comment thread src/server/responses-item-id-repair.ts
Comment thread src/server/responses-item-id-repair.ts
Comment thread src/server/responses/core.ts
Deep-clone registry repair arrays, preserve reasoning status metadata,
flush retained reasoning into terminal snapshots, document rewrite behavior
in ja/ko/ru, and cover composition trailer plus residual reasoning flush.

@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 `@docs-site/src/content/docs/ru/reference/configuration/providers.md`:
- Around line 256-257: Correct the Russian number agreement in the provider
configuration description around rewriteNonCanonicalIds: either replace
“gateway” with plural “шлюзов” and retain the plural relative clause, or make
the existing singular “gateway” take singular agreement (“который возвращает”).

In `@src/server/responses-item-id-repair.ts`:
- Around line 341-378: The synthesis path in flushRetainedReasoningIntoResponse
must preserve an existing non-reasoning output item at outputIndex instead of
overwriting it. Insert the synthetic reasoning item without deleting the
assistant message or its content, and add a focused regression test in the
DeepSeek repair tests verifying the existing assistant message remains
unchanged.
🪄 Autofix (Beta)

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: 681bdfd9-fca7-408c-b986-fe6fae0d34ab

📥 Commits

Reviewing files that changed from the base of the PR and between 9f25360 and eea5e3b.

📒 Files selected for processing (8)
  • docs-site/src/content/docs/ja/reference/configuration/providers.md
  • docs-site/src/content/docs/ko/reference/configuration/providers.md
  • docs-site/src/content/docs/ru/reference/configuration/providers.md
  • src/providers/derive.ts
  • src/server/responses-item-id-repair.ts
  • src/server/responses/core.ts
  • tests/responses-item-id-repair-deepseek.test.ts
  • tests/sse-payload-rewrite.test.ts

Comment thread docs-site/src/content/docs/ru/reference/configuration/providers.md Outdated
Comment thread src/server/responses-item-id-repair.ts
Do not overwrite existing assistant messages when synthesizing retained
reasoning, dual-write rewritten response ids for previous_response_id
continuity, fix Russian docs agreement, and add regressions.
@Wibias
Wibias marked this pull request as draft August 3, 2026 20:50
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