fix(gemini): upgrade @google/genai to support thinkingLevel for Gemini 3 models

The previous SDK version (1.29.1) did not include the `thinkingLevel` property
in `ThinkingConfig`, causing INVALID_ARGUMENT errors when using Gemini 3 Pro
and Flash models which require effort-based reasoning configuration via
`thinkingLevel`.

Changes:
- Update @google/genai from ^1.29.1 to ^1.30.0 (adds thinkingLevel support)
- Import ThinkingLevel enum and map lowercase effort values to SDK enum values
- Update GeminiReasoningParams type to use SDK's ThinkingConfig directly
- Update tests to expect uppercase ThinkingLevel enum values
This commit is contained in:
Claude 2025-12-24 20:12:38 +00:00
parent 1e71015e54
commit 41c99fad5d
No known key found for this signature in database
5 changed files with 46 additions and 20 deletions

View file

@ -0,0 +1,12 @@
---
"roo-cline": patch
---
fix(gemini): upgrade @google/genai to support thinkingLevel for Gemini 3 models
The previous SDK version (1.29.1) did not include the `thinkingLevel` property in
`ThinkingConfig`, causing INVALID_ARGUMENT errors when using Gemini 3 Pro and Flash
models which require effort-based reasoning configuration via `thinkingLevel`.
Updated to @google/genai ^1.30.0 which adds support for `thinkingLevel` with values:
MINIMAL, LOW, MEDIUM, HIGH.

16
pnpm-lock.yaml generated
View file

@ -668,8 +668,8 @@ importers:
specifier: ^3.922.0
version: 3.922.0
'@google/genai':
specifier: ^1.29.1
version: 1.29.1(@modelcontextprotocol/sdk@1.12.0)
specifier: ^1.30.0
version: 1.34.0(@modelcontextprotocol/sdk@1.12.0)
'@lmstudio/sdk':
specifier: ^1.1.1
version: 1.2.0
@ -1973,11 +1973,11 @@ packages:
'@floating-ui/utils@0.2.9':
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
'@google/genai@1.29.1':
resolution: {integrity: sha512-Buywpq0A6xf9cOdhiWCi5KUiDBbZkjCH5xbl+xxNQRItoYQgd31p0OKyn5cUnT0YNzC/pAmszqXoOc7kncqfFQ==}
'@google/genai@1.34.0':
resolution: {integrity: sha512-vu53UMPvjmb7PGzlYu6Tzxso8Dfhn+a7eQFaS2uNemVtDZKwzSpJ5+ikqBbXplF7RGB1STcVDqCkPvquiwb2sw==}
engines: {node: '>=20.0.0'}
peerDependencies:
'@modelcontextprotocol/sdk': ^1.20.1
'@modelcontextprotocol/sdk': ^1.24.0
peerDependenciesMeta:
'@modelcontextprotocol/sdk':
optional: true
@ -11594,7 +11594,7 @@ snapshots:
'@floating-ui/utils@0.2.9': {}
'@google/genai@1.29.1(@modelcontextprotocol/sdk@1.12.0)':
'@google/genai@1.34.0(@modelcontextprotocol/sdk@1.12.0)':
dependencies:
google-auth-library: 10.5.0
ws: 8.18.3
@ -11854,7 +11854,7 @@ snapshots:
'@lmstudio/lms-isomorphic@0.4.5':
dependencies:
ws: 8.18.2
ws: 8.18.3
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@ -18954,7 +18954,7 @@ snapshots:
debug: 4.4.1(supports-color@8.1.1)
devtools-protocol: 0.0.1452169
typed-query-selector: 2.12.0
ws: 8.18.2
ws: 8.18.3
transitivePeerDependencies:
- bare-buffer
- bufferutil

View file

@ -1,6 +1,7 @@
// npx vitest run src/api/transform/__tests__/reasoning.spec.ts
import type { ModelInfo, ProviderSettings, ReasoningEffortWithMinimal } from "@roo-code/types"
import { ThinkingLevel } from "@google/genai"
import {
getOpenRouterReasoning,
@ -615,7 +616,7 @@ describe("reasoning.ts", () => {
const result = getGeminiReasoning(options) as GeminiReasoningParams | undefined
// Budget should not be used for effort-only models
expect(result).toEqual({ thinkingLevel: "high", includeThoughts: true })
expect(result).toEqual({ thinkingLevel: ThinkingLevel.HIGH, includeThoughts: true })
})
it("should still return thinkingLevel when enableReasoningEffort is false but effort is explicitly set", () => {
@ -641,7 +642,7 @@ describe("reasoning.ts", () => {
}
const result = getGeminiReasoning(options) as GeminiReasoningParams | undefined
expect(result).toEqual({ thinkingLevel: "high", includeThoughts: true })
expect(result).toEqual({ thinkingLevel: ThinkingLevel.HIGH, includeThoughts: true })
})
it("should return thinkingLevel for minimal effort", () => {
@ -664,7 +665,7 @@ describe("reasoning.ts", () => {
}
const result = getGeminiReasoning(options) as GeminiReasoningParams | undefined
expect(result).toEqual({ thinkingLevel: "minimal", includeThoughts: true })
expect(result).toEqual({ thinkingLevel: ThinkingLevel.MINIMAL, includeThoughts: true })
})
it("should return thinkingLevel for medium effort", () => {
@ -687,11 +688,17 @@ describe("reasoning.ts", () => {
}
const result = getGeminiReasoning(options) as GeminiReasoningParams | undefined
expect(result).toEqual({ thinkingLevel: "medium", includeThoughts: true })
expect(result).toEqual({ thinkingLevel: ThinkingLevel.MEDIUM, includeThoughts: true })
})
it("should handle all four Gemini thinking levels", () => {
const levels: GeminiThinkingLevel[] = ["minimal", "low", "medium", "high"]
const expectedThinkingLevels: Record<GeminiThinkingLevel, ThinkingLevel> = {
minimal: ThinkingLevel.MINIMAL,
low: ThinkingLevel.LOW,
medium: ThinkingLevel.MEDIUM,
high: ThinkingLevel.HIGH,
}
levels.forEach((level) => {
const geminiModel: ModelInfo = {
@ -718,7 +725,7 @@ describe("reasoning.ts", () => {
}
const result = getGeminiReasoning(options) as GeminiReasoningParams | undefined
expect(result).toEqual({ thinkingLevel: level, includeThoughts: true })
expect(result).toEqual({ thinkingLevel: expectedThinkingLevels[level], includeThoughts: true })
})
})
@ -836,7 +843,7 @@ describe("reasoning.ts", () => {
}
const result = getGeminiReasoning(options) as GeminiReasoningParams | undefined
expect(result).toEqual({ thinkingLevel: "medium", includeThoughts: true })
expect(result).toEqual({ thinkingLevel: ThinkingLevel.MEDIUM, includeThoughts: true })
})
})

View file

@ -1,6 +1,6 @@
import { BetaThinkingConfigParam } from "@anthropic-ai/sdk/resources/beta"
import OpenAI from "openai"
import type { GenerateContentConfig } from "@google/genai"
import { ThinkingLevel, type GenerateContentConfig } from "@google/genai"
import type { ModelInfo, ProviderSettings, ReasoningEffortExtended } from "@roo-code/types"
@ -21,7 +21,7 @@ export type AnthropicReasoningParams = BetaThinkingConfigParam
export type OpenAiReasoningParams = { reasoning_effort: OpenAI.Chat.ChatCompletionCreateParams["reasoning_effort"] }
// Valid Gemini thinking levels for effort-based reasoning
// Valid Gemini thinking levels for effort-based reasoning (lowercase for internal use)
const GEMINI_THINKING_LEVELS = ["minimal", "low", "medium", "high"] as const
export type GeminiThinkingLevel = (typeof GEMINI_THINKING_LEVELS)[number]
@ -30,10 +30,16 @@ export function isGeminiThinkingLevel(value: unknown): value is GeminiThinkingLe
return typeof value === "string" && GEMINI_THINKING_LEVELS.includes(value as GeminiThinkingLevel)
}
export type GeminiReasoningParams = GenerateContentConfig["thinkingConfig"] & {
thinkingLevel?: GeminiThinkingLevel
// Map lowercase thinking levels to SDK's ThinkingLevel enum
const THINKING_LEVEL_MAP: Record<GeminiThinkingLevel, ThinkingLevel> = {
minimal: ThinkingLevel.MINIMAL,
low: ThinkingLevel.LOW,
medium: ThinkingLevel.MEDIUM,
high: ThinkingLevel.HIGH,
}
export type GeminiReasoningParams = GenerateContentConfig["thinkingConfig"]
export type GetModelReasoningOptions = {
model: ModelInfo
reasoningBudget: number | undefined
@ -155,5 +161,6 @@ export const getGeminiReasoning = ({
return undefined
}
return { thinkingLevel: selectedEffort, includeThoughts: true }
// Map lowercase effort to SDK's ThinkingLevel enum (uppercase values)
return { thinkingLevel: THINKING_LEVEL_MAP[selectedEffort], includeThoughts: true }
}

View file

@ -435,7 +435,7 @@
"@anthropic-ai/vertex-sdk": "^0.7.0",
"@aws-sdk/client-bedrock-runtime": "^3.922.0",
"@aws-sdk/credential-providers": "^3.922.0",
"@google/genai": "^1.29.1",
"@google/genai": "^1.30.0",
"@lmstudio/sdk": "^1.1.1",
"@mistralai/mistralai": "^1.9.18",
"@modelcontextprotocol/sdk": "1.12.0",