Skip to content

fix(translator): preserve reasoning_content across Codex Responses round-trip#3561

Open
shellus wants to merge 1 commit into
router-for-me:devfrom
shellus:fix/codex-reasoning-content-roundtrip
Open

fix(translator): preserve reasoning_content across Codex Responses round-trip#3561
shellus wants to merge 1 commit into
router-for-me:devfrom
shellus:fix/codex-reasoning-content-roundtrip

Conversation

@shellus

@shellus shellus commented May 26, 2026

Copy link
Copy Markdown
Contributor

Problem

Codex clients using the Responses API (/v1/responses) with DeepSeek V4 thinking models fail on multi-turn conversations with:

Error from provider (DeepSeek): The `reasoning_content` in the thinking mode must be passed back to the API.

Root cause: Codex clients discard reasoning item summary.text but retain encrypted_content between turns. The response translator only populated summary.text and left encrypted_content empty. The request translator also had no reasoning item handler at all.

Changes

Response translator (both streaming and non-streaming)

  • Populate encrypted_content with the same text as summary.text in reasoning output items

Request translator

  • Add case "reasoning": handler for input items
  • Extract reasoning content from summary.text first, falling back to encrypted_content (Codex behavior)
  • Attach reasoning_content to the next assistant message (both regular text and tool_calls)

Verification

  • Existing tests pass
  • Tested end-to-end with Codex CLI v0.125.0 + DeepSeek V4 Pro via OpenAI-compatible provider: multi-turn conversations with tool calls no longer fail

Related Issues

@github-actions github-actions Bot changed the base branch from main to dev May 26, 2026 06:35
@github-actions

Copy link
Copy Markdown

This pull request targeted main.

The base branch has been automatically changed to dev.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for handling reasoning content when converting between OpenAI Chat Completions and OpenAI Responses. It collects reasoning content from either the summary text or the encrypted_content fallback, appending it as reasoning_content to assistant messages and pending tool calls. Additionally, it ensures encrypted_content is populated in the response so that Codex clients preserve it across turns. The reviewer suggested explicitly clearing pendingReasoningContent when encountering non-assistant messages to prevent potential leakage of reasoning content into subsequent turns.

Comment on lines +196 to +199
if role == "assistant" && pendingReasoningContent != "" {
message, _ = sjson.SetBytes(message, "reasoning_content", pendingReasoningContent)
pendingReasoningContent = ""
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If the message role is not assistant (e.g., user or system), any accumulated pendingReasoningContent from a previous turn should be discarded to prevent it from leaking into subsequent assistant messages in case of malformed or incomplete history. We should explicitly clear pendingReasoningContent when encountering non-assistant messages.

\t\t\t\tif role == \"assistant\" {\n\t\t\t\t\tif pendingReasoningContent != \"\" {\n\t\t\t\t\t\tmessage, _ = sjson.SetBytes(message, \"reasoning_content\", pendingReasoningContent)\n\t\t\t\t\t\tpendingReasoningContent = \"\"\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tpendingReasoningContent = \"\"\n\t\t\t\t}

…und-trip

Codex clients discard reasoning item summary.text but retain
encrypted_content between turns. Previously the response translator
only populated summary.text and left encrypted_content empty, so
Codex would send back an empty reasoning item. The request translator
also had no reasoning item handler, losing any reasoning content that
did survive.

Response side (both streaming and non-streaming):
- Populate encrypted_content with the same reasoning text.

Request side:
- Add case for 'reasoning' input items.
- Extract reasoning content from summary.text first, falling back to
  encrypted_content when summary is empty (Codex behavior).
- Attach collected reasoning_content to all assistant messages in the
  same turn (text message and tool_calls message).
- Clear pending reasoning content on non-assistant messages to
  prevent cross-turn leakage.

Fixes DeepSeek V4 400 error: 'The reasoning_content in the thinking
mode must be passed back to the API.'
@shellus shellus force-pushed the fix/codex-reasoning-content-roundtrip branch from 0747299 to 60910d3 Compare May 26, 2026 06:43
@shellus

shellus commented May 26, 2026

Copy link
Copy Markdown
Contributor Author

Updated based on review feedback and additional testing:

  1. Don't clear pendingReasoningContent in the message case — when an assistant response contains both text and tool_calls, the Responses format represents them as separate items (message + function_call). Both need reasoning_content. The previous version consumed it on the text message, leaving the tool_calls message without it. Now flushPendingToolCalls (which creates the tool_calls assistant message) is the canonical consumer.

  2. Clear pendingReasoningContent on non-assistant messages — added per review suggestion. When a user/system/developer message appears after reasoning, clear the pending content to prevent leakage into unrelated turns. Tool messages are excluded since they're part of the same turn's tool-call chain.

Tested e2e with Codex CLI v0.125.0 + DeepSeek V4 Pro in both codex exec (single-turn) and interactive mode (multi-turn with text+tool_calls).

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.

Codex encountered an error using the deepseek model. DeepSeek v4 系列模型在调用工具轮次中未正确回传reasoning_content 导致报错

1 participant