fix(provider): align Gemini thinking controls (#26279)
This commit is contained in:
@@ -603,6 +603,29 @@ function anthropicAdaptiveEfforts(apiId: string): string[] | null {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function googleThinkingLevelEfforts(apiId: string) {
|
||||||
|
const id = apiId.toLowerCase()
|
||||||
|
if (!id.includes("gemini-3")) 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"]
|
||||||
|
return ["low", "medium", "high"]
|
||||||
|
}
|
||||||
|
|
||||||
|
function googleThinkingBudgetMax(apiId: string) {
|
||||||
|
const id = apiId.toLowerCase()
|
||||||
|
if (id.includes("2.5") && id.includes("pro") && !id.includes("flash")) return 32_768
|
||||||
|
return 24_576
|
||||||
|
}
|
||||||
|
|
||||||
|
function googleSmallThinkingConfig(apiId: string) {
|
||||||
|
const levels = googleThinkingLevelEfforts(apiId)
|
||||||
|
if (apiId.toLowerCase().includes("gemini-3")) {
|
||||||
|
return { thinkingLevel: levels.includes("minimal") ? "minimal" : levels.includes("low") ? "low" : "high" }
|
||||||
|
}
|
||||||
|
return { thinkingBudget: googleThinkingBudgetMax(apiId) === 32_768 ? 128 : 0 }
|
||||||
|
}
|
||||||
|
|
||||||
export function variants(model: Provider.Model): Record<string, Record<string, any>> {
|
export function variants(model: Provider.Model): Record<string, Record<string, any>> {
|
||||||
if (!model.capabilities.reasoning) return {}
|
if (!model.capabilities.reasoning) return {}
|
||||||
|
|
||||||
@@ -908,18 +931,14 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
|
|||||||
max: {
|
max: {
|
||||||
thinkingConfig: {
|
thinkingConfig: {
|
||||||
includeThoughts: true,
|
includeThoughts: true,
|
||||||
thinkingBudget: 24576,
|
thinkingBudget: googleThinkingBudgetMax(id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let levels = ["low", "high"]
|
|
||||||
if (id.includes("3.1")) {
|
|
||||||
levels = ["low", "medium", "high"]
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
levels.map((effort) => [
|
googleThinkingLevelEfforts(id).map((effort) => [
|
||||||
effort,
|
effort,
|
||||||
{
|
{
|
||||||
thinkingConfig: {
|
thinkingConfig: {
|
||||||
@@ -1186,10 +1205,7 @@ export function smallOptions(model: Provider.Model) {
|
|||||||
}
|
}
|
||||||
if (model.providerID === "google") {
|
if (model.providerID === "google") {
|
||||||
// gemini-3 uses thinkingLevel, gemini-2.5 uses thinkingBudget
|
// gemini-3 uses thinkingLevel, gemini-2.5 uses thinkingBudget
|
||||||
if (model.api.id.includes("gemini-3")) {
|
return { thinkingConfig: googleSmallThinkingConfig(model.api.id) }
|
||||||
return { thinkingConfig: { thinkingLevel: "minimal" } }
|
|
||||||
}
|
|
||||||
return { thinkingConfig: { thinkingBudget: 0 } }
|
|
||||||
}
|
}
|
||||||
if (model.providerID === "openrouter" || model.providerID === "llmgateway") {
|
if (model.providerID === "openrouter" || model.providerID === "llmgateway") {
|
||||||
if (model.api.id.includes("google")) {
|
if (model.api.id.includes("google")) {
|
||||||
|
|||||||
@@ -3292,89 +3292,74 @@ describe("ProviderTransform.variants", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("@ai-sdk/google", () => {
|
for (const provider of [
|
||||||
test("gemini-2.5 returns high and max with thinkingConfig and thinkingBudget", () => {
|
{ name: "@ai-sdk/google", providerID: "google", url: "https://generativelanguage.googleapis.com" },
|
||||||
const model = createMockModel({
|
{ name: "@ai-sdk/google-vertex", providerID: "google-vertex", url: "https://vertexai.googleapis.com" },
|
||||||
id: "google/gemini-2.5-pro",
|
]) {
|
||||||
providerID: "google",
|
describe(provider.name, () => {
|
||||||
api: {
|
for (const testCase of [
|
||||||
id: "gemini-2.5-pro",
|
{
|
||||||
url: "https://generativelanguage.googleapis.com",
|
apiId: "gemini-2.5-pro",
|
||||||
npm: "@ai-sdk/google",
|
efforts: ["high", "max"],
|
||||||
|
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16_000 } },
|
||||||
|
expectedMax: { thinkingConfig: { includeThoughts: true, thinkingBudget: 32_768 } },
|
||||||
},
|
},
|
||||||
})
|
{
|
||||||
const result = ProviderTransform.variants(model)
|
apiId: "gemini-2.5-flash",
|
||||||
expect(Object.keys(result)).toEqual(["high", "max"])
|
efforts: ["high", "max"],
|
||||||
expect(result.high).toEqual({
|
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16_000 } },
|
||||||
thinkingConfig: {
|
expectedMax: { thinkingConfig: { includeThoughts: true, thinkingBudget: 24_576 } },
|
||||||
includeThoughts: true,
|
|
||||||
thinkingBudget: 16000,
|
|
||||||
},
|
},
|
||||||
})
|
{
|
||||||
expect(result.max).toEqual({
|
apiId: "gemini-3-pro-preview",
|
||||||
thinkingConfig: {
|
efforts: ["low", "medium", "high"],
|
||||||
includeThoughts: true,
|
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
|
||||||
thinkingBudget: 24576,
|
|
||||||
},
|
},
|
||||||
})
|
{
|
||||||
|
apiId: "gemini-3.1-pro-preview",
|
||||||
|
efforts: ["low", "medium", "high"],
|
||||||
|
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiId: "gemini-3-flash-preview",
|
||||||
|
efforts: ["minimal", "low", "medium", "high"],
|
||||||
|
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiId: "gemini-3.1-flash-lite",
|
||||||
|
efforts: ["minimal", "low", "medium", "high"],
|
||||||
|
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiId: "gemini-3.1-flash-image-preview",
|
||||||
|
efforts: ["minimal", "high"],
|
||||||
|
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apiId: "gemini-3-pro-image-preview",
|
||||||
|
efforts: ["high"],
|
||||||
|
expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
|
||||||
|
},
|
||||||
|
]) {
|
||||||
|
test(`${testCase.apiId} returns supported thinking controls`, () => {
|
||||||
|
const result = ProviderTransform.variants(
|
||||||
|
createMockModel({
|
||||||
|
id: `${provider.providerID}/${testCase.apiId}`,
|
||||||
|
providerID: provider.providerID,
|
||||||
|
api: {
|
||||||
|
id: testCase.apiId,
|
||||||
|
url: provider.url,
|
||||||
|
npm: provider.name,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
expect(Object.keys(result)).toEqual(testCase.efforts)
|
||||||
|
expect(result.high).toEqual(testCase.expectedHigh)
|
||||||
|
if (testCase.expectedMax) expect(result.max).toEqual(testCase.expectedMax)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
test("other gemini models return low and high with thinkingLevel", () => {
|
|
||||||
const model = createMockModel({
|
|
||||||
id: "google/gemini-2.0-pro",
|
|
||||||
providerID: "google",
|
|
||||||
api: {
|
|
||||||
id: "gemini-2.0-pro",
|
|
||||||
url: "https://generativelanguage.googleapis.com",
|
|
||||||
npm: "@ai-sdk/google",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const result = ProviderTransform.variants(model)
|
|
||||||
expect(Object.keys(result)).toEqual(["low", "high"])
|
|
||||||
expect(result.low).toEqual({
|
|
||||||
thinkingConfig: {
|
|
||||||
includeThoughts: true,
|
|
||||||
thinkingLevel: "low",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
expect(result.high).toEqual({
|
|
||||||
thinkingConfig: {
|
|
||||||
includeThoughts: true,
|
|
||||||
thinkingLevel: "high",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("@ai-sdk/google-vertex", () => {
|
|
||||||
test("gemini-2.5 returns high and max with thinkingConfig and thinkingBudget", () => {
|
|
||||||
const model = createMockModel({
|
|
||||||
id: "google-vertex/gemini-2.5-pro",
|
|
||||||
providerID: "google-vertex",
|
|
||||||
api: {
|
|
||||||
id: "gemini-2.5-pro",
|
|
||||||
url: "https://vertexai.googleapis.com",
|
|
||||||
npm: "@ai-sdk/google-vertex",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const result = ProviderTransform.variants(model)
|
|
||||||
expect(Object.keys(result)).toEqual(["high", "max"])
|
|
||||||
})
|
|
||||||
|
|
||||||
test("other vertex models return low and high with thinkingLevel", () => {
|
|
||||||
const model = createMockModel({
|
|
||||||
id: "google-vertex/gemini-2.0-pro",
|
|
||||||
providerID: "google-vertex",
|
|
||||||
api: {
|
|
||||||
id: "gemini-2.0-pro",
|
|
||||||
url: "https://vertexai.googleapis.com",
|
|
||||||
npm: "@ai-sdk/google-vertex",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const result = ProviderTransform.variants(model)
|
|
||||||
expect(Object.keys(result)).toEqual(["low", "high"])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("@ai-sdk/cohere", () => {
|
describe("@ai-sdk/cohere", () => {
|
||||||
test("returns empty object", () => {
|
test("returns empty object", () => {
|
||||||
@@ -3640,6 +3625,32 @@ describe("ProviderTransform.smallOptions - gpt-5 chat/search", () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("ProviderTransform.smallOptions - google thinking controls", () => {
|
||||||
|
const createGoogleModel = (apiId: string) =>
|
||||||
|
({
|
||||||
|
id: `google/${apiId}`,
|
||||||
|
providerID: "google",
|
||||||
|
api: {
|
||||||
|
id: apiId,
|
||||||
|
url: "https://generativelanguage.googleapis.com",
|
||||||
|
npm: "@ai-sdk/google",
|
||||||
|
},
|
||||||
|
}) as any
|
||||||
|
|
||||||
|
for (const testCase of [
|
||||||
|
{ id: "gemini-3-pro-preview", options: { thinkingConfig: { thinkingLevel: "low" } } },
|
||||||
|
{ id: "gemini-3-flash-preview", options: { thinkingConfig: { thinkingLevel: "minimal" } } },
|
||||||
|
{ id: "gemini-3.1-flash-image-preview", options: { thinkingConfig: { thinkingLevel: "minimal" } } },
|
||||||
|
{ id: "gemini-3-pro-image-preview", options: { thinkingConfig: { thinkingLevel: "high" } } },
|
||||||
|
{ id: "gemini-2.5-pro", options: { thinkingConfig: { thinkingBudget: 128 } } },
|
||||||
|
{ id: "gemini-2.5-flash", options: { thinkingConfig: { thinkingBudget: 0 } } },
|
||||||
|
]) {
|
||||||
|
test(`${testCase.id} returns supported small thinking options`, () => {
|
||||||
|
expect(ProviderTransform.smallOptions(createGoogleModel(testCase.id))).toEqual(testCase.options)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
describe("ProviderTransform.providerOptions - ai-gateway-provider", () => {
|
describe("ProviderTransform.providerOptions - ai-gateway-provider", () => {
|
||||||
const createModel = (overrides: Partial<any> = {}) =>
|
const createModel = (overrides: Partial<any> = {}) =>
|
||||||
({
|
({
|
||||||
|
|||||||
Reference in New Issue
Block a user