fix: exclude OpenCode providers from synthetic reasoning_content injection for Kimi models

OpenCode-Go and OpenCode-Zen handle reasoning content internally
and reject client-supplied reasoning_content in message history.
When retry fallback forwards conversation history to
opencode-go/kimi-k2.6, the compat layer was injecting synthetic
reasoning_content: '.' on assistant tool-call turns, causing
HTTP 400: 'Extra inputs are not permitted'.

Gate isKimiModel in requiresReasoningContentForToolCalls on
!isOpenCodeProvider so the injection path is skipped for
OpenCode providers while preserving existing behavior for
native Kimi API, DeepSeek, and OpenRouter.
This commit is contained in:
zolszabo
2026-05-14 16:27:37 +02:00
parent 09e4b58672
commit 6ac5e4e1ce
@@ -90,6 +90,7 @@ export function detectOpenAICompat(model: Model<"openai-completions">, resolvedB
provider === "opencode-zen" ||
provider === "opencode-go" ||
baseUrl.includes("opencode.ai");
const isOpenCodeProvider = provider === "opencode-go" || provider === "opencode-zen";
const useMaxTokens =
provider === "mistral" ||
@@ -182,13 +183,15 @@ export function detectOpenAICompat(model: Model<"openai-completions">, resolvedB
: "openai",
reasoningContentField: "reasoning_content",
// Backends that 400 follow-up requests when prior assistant tool-call turns lack `reasoning_content`:
// - Kimi: documented invariant on its native API and via OpenCode-Go.
// - Kimi: documented invariant on its native API.
// - Any reasoning-capable model reached through OpenRouter: DeepSeek V4 Pro and similar enforce
// this server-side whenever the request is in thinking mode. We can't translate Anthropic's
// redacted/encrypted reasoning into DeepSeek's plaintext form, so cross-provider continuations
// rely on a placeholder — see `convertMessages` for the placeholder injection.
// - OpenCode-Go and OpenCode-Zen handle reasoning content internally and reject
// `reasoning_content` in client-sent messages — exclude them even for Kimi models.
requiresReasoningContentForToolCalls:
isKimiModel ||
(isKimiModel && !isOpenCodeProvider) ||
(isDeepseekFamily && Boolean(model.reasoning)) ||
((provider === "openrouter" || baseUrl.includes("openrouter.ai")) && Boolean(model.reasoning)),
// DeepSeek V4 rejects synthetic reasoning_content placeholders (".") on tool-call turns.