Skip to content

fix(proxy/transform): preserve thinking as reasoning_content on assistant tool calls#2370

Open
97wow wants to merge 1 commit intofarion1231:mainfrom
97wow:fix/transform-preserve-reasoning-content
Open

fix(proxy/transform): preserve thinking as reasoning_content on assistant tool calls#2370
97wow wants to merge 1 commit intofarion1231:mainfrom
97wow:fix/transform-preserve-reasoning-content

Conversation

@97wow
Copy link
Copy Markdown

@97wow 97wow commented Apr 26, 2026

Closes #2365.

When convert_message_to_openai flattens an Anthropic assistant message that contains both thinking and tool_use blocks, the thinking content is currently dropped. Some upstream providers ??Moonshot AI (kimi-k2.6) is the case in #2365 ??refuse the next request:

400 thinking is enabled but reasoning_content is missing in assistant tool call message at index 2

This PR preserves the thinking text and re-emits it as reasoning_content on the converted assistant tool-call message.

Changes

src-tauri/src/proxy/providers/transform.rs ??convert_message_to_openai:

  1. Collect thinking block text into a reasoning_parts vec instead of skipping.
  2. When the produced message has role == "assistant" AND non-empty tool_calls, attach reasoning_content:
    • multiple thinking blocks ??joined with \n
    • no thinking captured ??fallback "tool call" placeholder (Moonshot accepts non-empty; it only refuses missing)
  3. Plain assistant messages (no tool_calls) are untouched ??no field added.

Design notes

Tests

Three new tests in the existing #[cfg(test)] block:

  • test_anthropic_to_openai_tool_use_preserves_reasoning_content ??thinking + tool_use ??field carries the thinking text.
  • test_anthropic_to_openai_tool_use_injects_placeholder_reasoning_content_when_missing ??tool_use without thinking ??"tool call" placeholder.
  • test_anthropic_to_openai_tool_use_assistant_text_only_does_not_inject_reasoning_content ??guards against accidentally adding the field on plain assistant replies.

Verification

  • rustfmt --check clean on the file.
  • Local cargo test deferred to CI: the first dependency build in this WSL environment was pulling crates from upstream and would have taken ~20 min, so I opted to let CI be authoritative. Diff is small and rustfmt-clean.
  • No frontend changes ??pnpm typecheck / pnpm format:check not run (no .ts touched).

…tant tool calls

Anthropic thinking blocks were dropped during the OpenAI conversion.
Moonshot AI rejects subsequent requests with HTTP 400 ("thinking is
enabled but reasoning_content is missing") when an assistant tool call
message lacks the field.

Collect thinking text into a reasoning_parts vec and inject it as
reasoning_content on assistant messages that carry tool_calls; fall
back to a "tool call" placeholder when no thinking was captured.
Plain assistant messages (no tool_calls) remain unchanged.

Closes farion1231#2365.
Copy link
Copy Markdown
Owner

@codex review

Copy link
Copy Markdown

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

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: 1c92ff2591

ℹ️ 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 +381 to +383
if role == "assistant" && !tool_calls.is_empty() {
let reasoning_content = if reasoning_parts.is_empty() {
"tool call".to_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.

P1 Badge Restrict reasoning_content to compatible openai_chat providers

This change unconditionally adds reasoning_content on every assistant tool-call message, and even fabricates a placeholder when no thinking block exists. Because anthropic_to_openai is the shared transform for all openai_chat backends, strict Chat Completions implementations that validate message schemas can reject these requests with 400 on tool-call turns due to the extra message property. Please gate this field to providers/models that explicitly support it (or at least only emit it when real thinking content was present).

Useful? React with 👍 / 👎.

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.

🐛 Missing reasoning_content causes Moonshot AI 400 error on tool calls

2 participants