fix(gemini): switch thinking default logic (#50841)

Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
This commit is contained in:
Mark McDonald 2026-09-24 00:41:37 +08:00 committed by GitHub
parent 7cb044ee89
commit 610df0b566
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 131 additions and 12 deletions

View file

@ -517,8 +517,11 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re
return msgs
}
const GEMINI_2_5_RE = /gemini-2[.-]5(?:[.-]|$)/i
const GEMINI_LEGACY_RE = /gemini-(?:(?:flash|pro)-)?[12](?:[.-]|$)/i
const GEMINI_MODELS_WITH_SAMPLING_DEFAULTS = [
/gemini-2[.-]5(?:[.-]|$)/,
GEMINI_2_5_RE,
/gemini-3-(?:flash|pro)(?:[.-]|$)/,
/gemini-3[.-]1(?:[.-]|$)/,
/gemini-3[.-]5-flash(?!-lite)(?:[.-]|$)/,
@ -735,9 +738,19 @@ function anthropicBlockBinding(model: Provider.Model, options: { [x: string]: an
return options
}
function isLegacyGemini(apiId: string) {
return GEMINI_LEGACY_RE.test(apiId)
}
function isGemini25(apiId: string) {
return GEMINI_2_5_RE.test(apiId)
}
function googleThinkingLevelEfforts(apiId: string) {
const id = apiId.toLowerCase()
if (!id.includes("gemini-3")) return ["low", "high"]
// Gemma 4 only toggles thinking: "minimal" disables it and "high" enables it.
if (id.includes("gemma")) return ["minimal", "high"]
if (isLegacyGemini(id)) return ["low", "high"]
if (id.includes("flash-image")) return ["minimal", "high"]
if (id.includes("pro-image")) return ["high"]
if (id.includes("flash")) return ["minimal", "low", "medium", "high"]
@ -746,7 +759,7 @@ function googleThinkingLevelEfforts(apiId: string) {
function googleThinkingBudgetMax(apiId: string) {
const id = apiId.toLowerCase()
if (id.includes("2.5") && id.includes("pro") && !id.includes("flash")) return 32_768
if (isGemini25(id) && id.includes("pro") && !id.includes("flash")) return 32_768
return 24_576
}
@ -758,7 +771,7 @@ function wrapInSapModelParams(variants: Record<string, Record<string, any>>): Re
function googleThinkingVariants(model: Provider.Model): Record<string, Record<string, any>> {
const id = model.api.id.toLowerCase()
if (id.includes("2.5")) {
if (isGemini25(id)) {
return {
high: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } },
max: {
@ -912,7 +925,7 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
}
}
if (model.api.id.includes("google")) {
if (model.api.id.includes("2.5")) {
if (isGemini25(model.api.id)) {
return {
high: {
thinkingConfig: {
@ -1189,7 +1202,7 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
max: { thinking: { type: "enabled", budget_tokens: 31999 } },
})
}
if (id.includes("gemini") && id.includes("2.5")) {
if (isGemini25(id) || isGemini25(model.api.id)) {
return wrapInSapModelParams(googleThinkingVariants(model))
}
if (id.includes("gpt") || /\bo[1-9]/.test(id)) {
@ -1237,7 +1250,7 @@ export function options(input: {
result["usage"] = {
include: true,
}
if (input.model.api.id.includes("gemini-3")) {
if (input.model.api.id.toLowerCase().includes("gemini") && !isLegacyGemini(input.model.api.id)) {
result["reasoning"] = { effort: "high" }
}
}
@ -1269,7 +1282,7 @@ export function options(input: {
result["thinkingConfig"] = {
includeThoughts: true,
}
if (input.model.api.id.includes("gemini-3")) {
if (!isLegacyGemini(input.model.api.id)) {
result["thinkingConfig"]["thinkingLevel"] = "high"
}
}

View file

@ -393,16 +393,20 @@ describe("ProviderTransform.options - minimax m3 thinking", () => {
describe("ProviderTransform.options - google thinkingConfig gating", () => {
const sessionID = "test-session-123"
const createGoogleModel = (reasoning: boolean, npm: "@ai-sdk/google" | "@ai-sdk/google-vertex") =>
const createGoogleModel = (
reasoning: boolean,
npm: "@ai-sdk/google" | "@ai-sdk/google-vertex",
apiId = "gemini-2.0-flash",
) =>
({
id: `${npm === "@ai-sdk/google" ? "google" : "google-vertex"}/gemini-2.0-flash`,
id: `${npm === "@ai-sdk/google" ? "google" : "google-vertex"}/${apiId}`,
providerID: npm === "@ai-sdk/google" ? "google" : "google-vertex",
api: {
id: "gemini-2.0-flash",
id: apiId,
url: npm === "@ai-sdk/google" ? "https://generativelanguage.googleapis.com" : "https://vertexai.googleapis.com",
npm,
},
name: "Gemini 2.0 Flash",
name: apiId,
capabilities: {
temperature: true,
reasoning,
@ -454,6 +458,73 @@ describe("ProviderTransform.options - google thinkingConfig gating", () => {
})
expect(result.thinkingConfig).toBeUndefined()
})
test.each(["gemini-1.5-pro", "gemini-2.0-flash", "gemini-2.5-pro", "gemini-2.5-flash"])(
"omits default thinkingLevel for legacy model %s",
(apiId) => {
for (const npm of ["@ai-sdk/google", "@ai-sdk/google-vertex"] as const) {
const result = ProviderTransform.options({
model: createGoogleModel(true, npm, apiId),
sessionID,
providerOptions: {},
})
expect(result.thinkingConfig).toEqual({
includeThoughts: true,
})
}
const openrouterResult = ProviderTransform.options({
model: {
...createGoogleModel(true, "@ai-sdk/google", `google/${apiId}`),
providerID: "openrouter",
api: {
id: `google/${apiId}`,
url: "https://openrouter.ai/api/v1",
npm: "@openrouter/ai-sdk-provider",
},
},
sessionID,
providerOptions: {},
})
expect(openrouterResult.reasoning).toBeUndefined()
},
)
test.each([
"gemini-3-pro-preview",
"gemini-3-flash-preview",
"gemini-9-pro",
"gemini-9-flash",
"gemini-pro-latest",
"gemini-flash-latest",
])("sets default thinkingLevel=high and OpenRouter reasoning effort=high for %s", (apiId) => {
for (const npm of ["@ai-sdk/google", "@ai-sdk/google-vertex"] as const) {
const result = ProviderTransform.options({
model: createGoogleModel(true, npm, apiId),
sessionID,
providerOptions: {},
})
expect(result.thinkingConfig).toEqual({
includeThoughts: true,
thinkingLevel: "high",
})
}
const openrouterResult = ProviderTransform.options({
model: {
...createGoogleModel(true, "@ai-sdk/google", `google/${apiId}`),
providerID: "openrouter",
api: {
id: `google/${apiId}`,
url: "https://openrouter.ai/api/v1",
npm: "@openrouter/ai-sdk-provider",
},
},
sessionID,
providerOptions: {},
})
expect(openrouterResult.reasoning).toEqual({ effort: "high" })
})
})
describe("ProviderTransform.options - gpt-5 textVerbosity", () => {
@ -5651,6 +5722,16 @@ describe("ProviderTransform.variants", () => {
]) {
describe(provider.name, () => {
for (const testCase of [
{
apiId: "gemini-1.5-pro",
efforts: ["low", "high"],
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
},
{
apiId: "gemini-2.0-flash",
efforts: ["low", "high"],
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
},
{
apiId: "gemini-2.5-pro",
efforts: ["high", "max"],
@ -5693,6 +5774,31 @@ describe("ProviderTransform.variants", () => {
efforts: ["high"],
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
},
{
apiId: "gemini-9-pro",
efforts: ["low", "medium", "high"],
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
},
{
apiId: "gemini-9-flash",
efforts: ["minimal", "low", "medium", "high"],
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
},
{
apiId: "gemini-pro-latest",
efforts: ["low", "medium", "high"],
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
},
{
apiId: "gemini-flash-latest",
efforts: ["minimal", "low", "medium", "high"],
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
},
{
apiId: "gemma-4-31b-it",
efforts: ["minimal", "high"],
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
},
]) {
test(`${testCase.apiId} returns supported thinking controls`, () => {
const result = ProviderTransform.variants(