From 4a930e79668d28f96080b03600619f649a0d529c Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sat, 18 Apr 2026 04:33:50 -0300 Subject: [PATCH] fix: Claude passthrough (#1359), kimi-k2 reasoning (#1360), thinking leak (#1361), Ollama redirect (#1381) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Eliminate lossy Claude→OpenAI→Claude round-trip for Claude-format providers - Expand isReasoner to include kimi-k2 and opencode-go provider models - Block thinking param leak to non-Claude antigravity models (gemini, gpt-oss) - Allow redirects for Ollama Cloud /v1/models endpoint (301) --- open-sse/handlers/chatCore.ts | 50 +++------------------- open-sse/translator/index.ts | 4 +- src/app/api/providers/[id]/models/route.ts | 2 + src/lib/modelCapabilities.ts | 5 +++ 4 files changed, 17 insertions(+), 44 deletions(-) diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index a3d8e48378..dd38270a38 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -1164,52 +1164,16 @@ export async function handleChatCore({ preserveCacheControl, }); log?.debug?.("FORMAT", "claude-code-compatible bridge enabled"); - } else if (isClaudePassthrough && preserveCacheControl) { - // Pure passthrough: when preserveCacheControl is true, forward the body - // as-is without prior normalization. The OpenAI round-trip would strip - // cache_control markers; even prepareClaudeRequest can alter structure. - // Claude Code sends well-formed Messages API payloads — trust it. + } else if (isClaudePassthrough) { + // Pure passthrough: forward the body as-is without OpenAI round-trip. + // The Claude→OpenAI→Claude double translation was lossy and corrupted + // payloads at high context (150+ msgs, 100+ tools). Fix: #1359. + // Claude Code sends well-formed Messages API payloads — trust them + // regardless of combo strategy or cache_control settings. translatedBody = { ...body }; translatedBody._disableToolPrefix = true; - log?.debug?.("FORMAT", "claude passthrough with cache_control preservation"); - } else if (isClaudePassthrough) { - // Claude OAuth expects the same Claude Code prompt + structural normalization - // as the OpenAI-compatible chat path. Round-trip through OpenAI to reuse the - // working Claude translator instead of forwarding raw Messages payloads. - const normalizeToolCallId = getModelNormalizeToolCallId( - provider || "", - model || "", - sourceFormat - ); - const preserveDeveloperRole = getModelPreserveOpenAIDeveloperRole( - provider || "", - model || "", - sourceFormat - ); - translatedBody = translateRequest( - FORMATS.CLAUDE, - FORMATS.OPENAI, - model, - { ...body }, - stream, - credentials, - provider, - reqLogger, - { normalizeToolCallId, preserveDeveloperRole, preserveCacheControl } - ); - translatedBody = translateRequest( - FORMATS.OPENAI, - FORMATS.CLAUDE, - model, - { ...translatedBody, _disableToolPrefix: true }, - stream, - credentials, - provider, - reqLogger, - { normalizeToolCallId, preserveDeveloperRole, preserveCacheControl } - ); - log?.debug?.("FORMAT", "claude->openai->claude normalized passthrough"); + log?.debug?.("FORMAT", `claude passthrough (preserveCache=${preserveCacheControl})`); } else { translatedBody = { ...body }; diff --git a/open-sse/translator/index.ts b/open-sse/translator/index.ts index 6c8786c382..6055716a36 100644 --- a/open-sse/translator/index.ts +++ b/open-sse/translator/index.ts @@ -207,7 +207,9 @@ export function translateRequest( // Inject reasoning_content = "" for DeepSeek/Reasoning models assistant messages with tool_calls // if omitted by the client, to avoid upstream 400 errors (e.g. "Messages with role 'assistant' that contain tool_calls must also include reasoning_content") const isReasoner = - provider === "deepseek" || (typeof model === "string" && /r1|reason/i.test(model)); + provider === "deepseek" || + provider === "opencode-go" || + (typeof model === "string" && /r1|reason|kimi-k2/i.test(model)); if (isReasoner && result.messages && Array.isArray(result.messages)) { for (const msg of result.messages) { if ( diff --git a/src/app/api/providers/[id]/models/route.ts b/src/app/api/providers/[id]/models/route.ts index dd91c75c5c..5503b047cd 100755 --- a/src/app/api/providers/[id]/models/route.ts +++ b/src/app/api/providers/[id]/models/route.ts @@ -892,6 +892,8 @@ export async function GET( ...SAFE_OUTBOUND_FETCH_PRESETS.modelsPagination, guard: getProviderOutboundGuard(), proxyConfig: proxy, + // Ollama Cloud /v1/models returns 301 redirects (#1381) + ...(provider === "ollama-cloud" ? { allowRedirect: true } : {}), ...fetchOptions, }); diff --git a/src/lib/modelCapabilities.ts b/src/lib/modelCapabilities.ts index a451f03981..7ee0354550 100644 --- a/src/lib/modelCapabilities.ts +++ b/src/lib/modelCapabilities.ts @@ -11,6 +11,11 @@ const REASONING_UNSUPPORTED_PATTERNS = [ "antigravity/claude-sonnet-4-6", "antigravity/claude-sonnet-4-5", "antigravity/claude-sonnet-4", + // Non-Claude antigravity models don't support thinking params (#1361) + "antigravity/gemini-", + "antigravity/gpt-oss-", + "antigravity/gemini-3", + "antigravity/tab_", ]; type CapabilityInput =