From e13dfb030c4dd30393f0da8b867cf4ebcb285274 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Wed, 24 Dec 2025 20:29:13 +0000 Subject: [PATCH 1/3] fix: clarify Gemini thinkingConfig support guard to prevent 400 errors --- src/api/transform/reasoning.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/api/transform/reasoning.ts b/src/api/transform/reasoning.ts index e726ce32234..887810db006 100644 --- a/src/api/transform/reasoning.ts +++ b/src/api/transform/reasoning.ts @@ -131,6 +131,11 @@ export const getGeminiReasoning = ({ reasoningEffort, settings, }: GetModelReasoningOptions): GeminiReasoningParams | undefined => { + // Only send thinkingConfig to models that advertise support for it; Gemini 3 rejects + // the field and returns 400 otherwise. + const supportsThinkingConfig = + typeof model.maxThinkingTokens === "number" || model.supportsReasoningBudget || model.supportsReasoningEffort + // Budget-based (2.5) models: use thinkingBudget, not thinkingLevel. if (shouldUseReasoningBudget({ model, settings })) { return { thinkingBudget: reasoningBudget!, includeThoughts: true } @@ -150,6 +155,11 @@ export const getGeminiReasoning = ({ return undefined } + // Skip thinking config for models that don't advertise support (e.g., Gemini 3). + if (!supportsThinkingConfig) { + return undefined + } + // Effort-based models on Google GenAI support minimal/low/medium/high levels. if (!isGeminiThinkingLevel(selectedEffort)) { return undefined From 3b759c78063dc187ae69211f395bf5627846f2d5 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Wed, 24 Dec 2025 21:05:10 +0000 Subject: [PATCH 2/3] fix: support models with fixed reasoning levels in Gemini thinkingConfig guard --- src/api/transform/reasoning.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/api/transform/reasoning.ts b/src/api/transform/reasoning.ts index 887810db006..eecb2483d1a 100644 --- a/src/api/transform/reasoning.ts +++ b/src/api/transform/reasoning.ts @@ -133,8 +133,12 @@ export const getGeminiReasoning = ({ }: GetModelReasoningOptions): GeminiReasoningParams | undefined => { // Only send thinkingConfig to models that advertise support for it; Gemini 3 rejects // the field and returns 400 otherwise. + // This checks for: budget support, effort UI support, or fixed reasoning level defaults. const supportsThinkingConfig = - typeof model.maxThinkingTokens === "number" || model.supportsReasoningBudget || model.supportsReasoningEffort + typeof model.maxThinkingTokens === "number" || + model.supportsReasoningBudget || + model.supportsReasoningEffort || + !!model.reasoningEffort // Budget-based (2.5) models: use thinkingBudget, not thinkingLevel. if (shouldUseReasoningBudget({ model, settings })) { From f3dede7be3913259a55bdaaf52c283423cb949c4 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Wed, 24 Dec 2025 21:08:52 +0000 Subject: [PATCH 3/3] test: add tests for models with fixed reasoningEffort --- src/api/transform/__tests__/reasoning.spec.ts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/api/transform/__tests__/reasoning.spec.ts b/src/api/transform/__tests__/reasoning.spec.ts index 352aac8e7bb..1076103b310 100644 --- a/src/api/transform/__tests__/reasoning.spec.ts +++ b/src/api/transform/__tests__/reasoning.spec.ts @@ -838,6 +838,57 @@ describe("reasoning.ts", () => { const result = getGeminiReasoning(options) as GeminiReasoningParams | undefined expect(result).toEqual({ thinkingLevel: "medium", includeThoughts: true }) }) + + it("should return thinkingLevel when model has fixed reasoningEffort without supportsReasoningEffort", () => { + // This tests the scenario where a model has a hardcoded default reasoning level + // (reasoningEffort) but doesn't expose UI controls (supportsReasoningEffort: false) + const geminiModel: ModelInfo = { + ...baseModel, + // No supportsReasoningEffort - UI selector won't show + // But model has a fixed default reasoning level + reasoningEffort: "medium", + } + + const settings: ProviderSettings = { + apiProvider: "gemini", + } + + const options: GetModelReasoningOptions = { + model: geminiModel, + reasoningBudget: undefined, + reasoningEffort: undefined, + settings, + } + + const result = getGeminiReasoning(options) as GeminiReasoningParams | undefined + // Should use the model's fixed default and send thinkingConfig + expect(result).toEqual({ thinkingLevel: "medium", includeThoughts: true }) + }) + + it("should return undefined for models without any thinking support (e.g., Gemini 3)", () => { + // This tests that Gemini 3 models (no maxThinkingTokens, supportsReasoningBudget, + // supportsReasoningEffort, or reasoningEffort) don't receive thinkingConfig + const gemini3Model: ModelInfo = { + ...baseModel, + // No thinking-related properties at all + } + + const settings: ProviderSettings = { + apiProvider: "gemini", + reasoningEffort: "high", // Even if user tries to set this, model doesn't support it + } + + const options: GetModelReasoningOptions = { + model: gemini3Model, + reasoningBudget: undefined, + reasoningEffort: "high", + settings, + } + + const result = getGeminiReasoning(options) + // Should return undefined because model has no thinking support + expect(result).toBeUndefined() + }) }) describe("Integration scenarios", () => {