From 7168f4014d07ace2a02375f02806e5a78e109079 Mon Sep 17 00:00:00 2001 From: Paijo <14921983+oyi77@users.noreply.github.com> Date: Mon, 30 Mar 2026 02:12:19 +0700 Subject: [PATCH] fix: strip reasoning/thinking params for models that don't support them (#766) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Models like antigravity/claude-sonnet-4-6 route through Google's internal Cloud Code API which returns HTTP 400 when thinking/reasoning parameters are included in the request body. Changes: - open-sse/services/modelCapabilities.ts: add supportsReasoning() function with a denylist of known-unsupported patterns (antigravity/claude-sonnet-*) and a registry-based lookup hook (supportsReasoning flag per model) - open-sse/services/thinkingBudget.ts: in applyThinkingBudget(), add early exit before the mode switch — if model string is present and supportsReasoning() returns false, call stripThinkingConfig() immediately regardless of the configured ThinkingMode This is fully backward-compatible: models not in the denylist are unaffected, and the supportsReasoning registry flag defaults to null (pass-through). Fixes: HTTP 400 errors on antigravity provider when client sends requests with thinking/reasoning budget parameters (e.g. claude-sonnet-4-6 via AG). Co-authored-by: oyi77 Co-authored-by: oyi77 --- open-sse/services/modelCapabilities.ts | 51 ++++++++++++++++++++++++++ open-sse/services/thinkingBudget.ts | 8 ++++ 2 files changed, 59 insertions(+) diff --git a/open-sse/services/modelCapabilities.ts b/open-sse/services/modelCapabilities.ts index de86e1fd7a..b3822e9672 100644 --- a/open-sse/services/modelCapabilities.ts +++ b/open-sse/services/modelCapabilities.ts @@ -48,3 +48,54 @@ export function supportsToolCalling(modelStr: string): boolean { return !blocked; } + +// Models that do NOT support reasoning/thinking parameters. +// AG (Antigravity) claude-sonnet-4-6 routes through a Google internal API +// that returns 400 if thinking params are included. +const REASONING_UNSUPPORTED_PATTERNS = [ + "antigravity/claude-sonnet-4-6", + "antigravity/claude-sonnet-4-5", + "antigravity/claude-sonnet-4", + "ag/claude-sonnet-4-6", + "ag/claude-sonnet-4-5", + "ag/claude-sonnet-4", +]; + +function getRegistryReasoningFlag(providerIdOrAlias: string, modelId: string): boolean | null { + const providerAlias = PROVIDER_ID_TO_ALIAS[providerIdOrAlias] || providerIdOrAlias; + const models = PROVIDER_MODELS[providerAlias]; + if (!Array.isArray(models)) return null; + const found = models.find((m) => m?.id === modelId); + if (!found) return null; + return typeof found.supportsReasoning === "boolean" ? found.supportsReasoning : null; +} + +/** + * Returns whether a model supports reasoning/thinking parameters. + * + * Decision order: + * 1) Provider registry metadata (supportsReasoning flag) when available. + * 2) Explicit denylist for known unsupported models (e.g. AG Claude Sonnet). + * 3) Default true (pass through — safe, provider will ignore if unsupported). + */ +export function supportsReasoning(modelStr: string): boolean { + const parsed = parseModel(modelStr); + const provider = parsed.provider || parsed.providerAlias || ""; + const model = parsed.model || modelStr; + + if (provider) { + const fromRegistry = getRegistryReasoningFlag(provider, model); + if (fromRegistry !== null) return fromRegistry; + } + + const normalized = String(modelStr || "").toLowerCase(); + if (!normalized) return true; + + const blocked = REASONING_UNSUPPORTED_PATTERNS.some((pattern) => + normalized === pattern || + normalized.endsWith(`/${pattern}`) || + normalized.includes(pattern) + ); + + return !blocked; +} diff --git a/open-sse/services/thinkingBudget.ts b/open-sse/services/thinkingBudget.ts index 4028037c97..e47cfbc9fb 100644 --- a/open-sse/services/thinkingBudget.ts +++ b/open-sse/services/thinkingBudget.ts @@ -14,6 +14,7 @@ export const ThinkingMode = { }; import { capThinkingBudget, getDefaultThinkingBudget } from "@/shared/constants/modelSpecs"; +import { supportsReasoning } from "./modelCapabilities.ts"; // Effort → budget token mapping export const EFFORT_BUDGETS = { @@ -151,6 +152,13 @@ export function applyThinkingBudget(body, config = null) { const cfg = config || _config; if (!body || typeof body !== "object") return body; + // Early exit: strip ALL reasoning/thinking params for models that don't support them. + // Sending thinking params to unsupported models (e.g. AG claude-sonnet-4-6) causes 400 errors. + const modelStr = typeof body.model === "string" ? body.model : ""; + if (modelStr && !supportsReasoning(modelStr)) { + return stripThinkingConfig(body); + } + // Pre-processing: convert string thinkingLevel to numeric budget let processed = normalizeThinkingLevel(body);