fix(opencode): preserve reasoning_content for Kimi K2.5/K2.6 multi-turn tool calls#28542
fix(opencode): preserve reasoning_content for Kimi K2.5/K2.6 multi-turn tool calls#28542j0hk3r1 wants to merge 1 commit into
Conversation
…rn tool calls
Kimi K2.5 and K2.6 (Moonshot AI) reject subsequent requests in multi-turn
tool-calling conversations with:
thinking is enabled but reasoning_content is missing in assistant
tool call message at index [N]
The model returns reasoning_content alongside tool_calls in the first
turn, but opencode strips it before building the next request's message
history. Kimi requires reasoning_content to be preserved on every
assistant message (even empty) when thinking mode is active — which is
the default on Moonshot's API, Cloudflare Workers AI, Cerebras, and any
other openai-compatible Kimi K2.5/K2.6 hosting.
Same class of bug already fixed for DeepSeek in this file. This patch:
1. Extends the existing reasoning-placeholder injection guard to also
match Kimi K2.5/K2.6 model IDs (kimi-k2.5, kimi-k2.6, kimi-k2p5,
kimi-k2p6, kimi-k2-5, kimi-k2-6). Kimi K2-Thinking is excluded — it
returns reasoning_content natively and uses a different SDK path.
2. Adds an explicit interleaved-style serialization block that places
reasoning_content into providerOptions.openaiCompatible for Kimi
K2.5/K2.6 on @ai-sdk/openai-compatible, even when the models.dev
catalog does not yet declare interleaved={field:"reasoning_content"}
for these models. This avoids depending on a separate models.dev
change to fix the bug.
Adds 4 test cases covering: placeholder injection, existing reasoning
preservation, Kimi K2-Thinking exclusion, and Kimi K2 base no-op.
Fixes anomalyco#10996.
Related: anomalyco#24722, anomalyco#24190 (same bug class for DeepSeek)
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
|
The following comment was made by an LLM, it may be inaccurate: Based on my search, PR #28542 (the current PR) does not have exact duplicates, but there are related PRs that address similar reasoning_content preservation issues: Related PRs (Not Duplicates):
These are related work but not duplicate PRs. PR #28542 is specifically targeting the Kimi K2.5/K2.6 regression, following the established pattern used for DeepSeek fixes. |
|
Closing — not ready to upstream yet, will revisit after more local validation. |
Summary
Kimi K2.5 and K2.6 (Moonshot AI) reject subsequent requests in multi-turn tool-calling conversations with:
The model returns
reasoning_contentalongsidetool_callsin the first turn, but opencode strips it before building the next request's message history. Kimi requiresreasoning_contentto be preserved on every assistant message (even empty) when thinking mode is active — which is the default on Moonshot's API, Cloudflare Workers AI, Cerebras, and any other openai-compatible Kimi K2.5/K2.6 hosting.This is the same class of bug already fixed for DeepSeek in
transform.ts.Symptom in the wild
Observed empirically with
cloudflare/@cf/moonshotai/kimi-k2.6via the openai-compatible SDK:tool_callis emitted in the responsefinish_reason: stopMultiple matching reports:
Fix
Two changes in
packages/opencode/src/provider/transform.ts:Extend the reasoning-placeholder injection guard (
isKimiK2RequiringReasoning) to match Kimi K2.5/K2.6 model IDs alongside DeepSeek. Matches:kimi-k2.5,kimi-k2.6,kimi-k2p5,kimi-k2p6,kimi-k2-5,kimi-k2-6. Kimi K2-Thinking is excluded — it returnsreasoning_contentnatively and uses a different SDK path.Add an explicit interleaved-style serialization block that places
reasoning_contentintoproviderOptions.openaiCompatiblefor Kimi K2.5/K2.6 on@ai-sdk/openai-compatible, even when the models.dev catalog has not yet declaredinterleaved: { field: "reasoning_content" }for these models. This avoids depending on a separate models.dev change to fix the bug.Tests
Added 4 test cases following the existing DeepSeek test pattern:
Kimi K2.6 injects empty reasoning placeholder on assistant tool-call without reasoningKimi K2.5 preserves existing reasoning content in providerOptionsKimi K2-Thinking is NOT touched (returns reasoning_content natively)Kimi K2 base (non-K2.5/K2.6) is not affectedAll 228 existing + new transform tests pass. All 340 tests under
test/provider/pass with no regressions.Test plan
bun test packages/opencode/test/provider/transform.test.ts— 228 pass, 0 failbun test packages/opencode/test/provider/— 340 pass, 0 failkimi-k2.6(via Cloudflare Workers AI or Cerebras openai-compatible endpoint) through a multi-turn tool-calling sessionFixes #10996
Related: #24722, #24190