From 6ac5e4e1ceb0e7cf766efa038c868c9164313525 Mon Sep 17 00:00:00 2001 From: zolszabo Date: Thu, 14 May 2026 16:27:37 +0200 Subject: [PATCH] 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. --- packages/ai/src/providers/openai-completions-compat.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/ai/src/providers/openai-completions-compat.ts b/packages/ai/src/providers/openai-completions-compat.ts index 8b4b706d6..efb19600c 100644 --- a/packages/ai/src/providers/openai-completions-compat.ts +++ b/packages/ai/src/providers/openai-completions-compat.ts @@ -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.