mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:40:44 +00:00
fix(google): strip thinkingBudget=0 for gemini-2.5-pro thinking-required model
Gemini 2.5 Pro only works in thinking mode and rejects thinkingBudget=0 with 'Budget 0 is invalid. This model only works in thinking mode.' The existing sanitizer in the embedded runner only handled negative budgets; now it also removes zero budgets for the thinking-required model so the API uses its default thinking behavior. When thinkingBudget was the only key in thinkingConfig, the empty object is also removed to match the Gemma 4 cleanup path.
This commit is contained in:
committed by
Peter Steinberger
parent
ca1aa08709
commit
8c5a4eb866
@@ -64,6 +64,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/failover: avoid treating bare leading `402 ...` prose as billing errors while still recognizing proxy subscription failures. (#45827) Thanks @junyuc25.
|
||||
- Config/$schema: preserve root-authored `$schema` during partial config rewrites without injecting include-only schema URLs into the root config. (#47322) Thanks @EfeDurmaz16.
|
||||
- Agents/CLI delivery: run the same reply-media path normalizer the auto-reply flow uses before shipping `openclaw agent --deliver` payloads, so relative `MEDIA:./out/photo.png` tokens resolve against the agent workspace instead of being rejected downstream with `LocalMediaAccessError: Local media path is not under an allowed directory`. Thanks @frankekn.
|
||||
- Agents/Google: strip `thinkingBudget=0` for the thinking-required `gemini-2.5-pro` model in the embedded runner Google sanitizer, so requests no longer fail with `Budget 0 is invalid. This model only works in thinking mode.` and the API uses its default thinking behavior instead. Thanks @josmithiii.
|
||||
|
||||
## 2026.4.15
|
||||
|
||||
|
||||
55
src/agents/pi-embedded-runner/google-stream-wrappers.test.ts
Normal file
55
src/agents/pi-embedded-runner/google-stream-wrappers.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { sanitizeGoogleThinkingPayload } from "./google-stream-wrappers.js";
|
||||
|
||||
describe("sanitizeGoogleThinkingPayload — gemini-2.5-pro zero budget", () => {
|
||||
it("removes thinkingBudget=0 for gemini-2.5-pro", () => {
|
||||
const payload = {
|
||||
config: {
|
||||
thinkingConfig: { thinkingBudget: 0 },
|
||||
},
|
||||
};
|
||||
sanitizeGoogleThinkingPayload({ payload, modelId: "gemini-2.5-pro" });
|
||||
expect(payload.config).not.toHaveProperty("thinkingConfig");
|
||||
});
|
||||
|
||||
it("removes thinkingBudget=0 for gemini-2.5-pro with provider prefix", () => {
|
||||
const payload = {
|
||||
config: {
|
||||
thinkingConfig: { thinkingBudget: 0 },
|
||||
},
|
||||
};
|
||||
sanitizeGoogleThinkingPayload({ payload, modelId: "google/gemini-2.5-pro-preview" });
|
||||
expect(payload.config).not.toHaveProperty("thinkingConfig");
|
||||
});
|
||||
|
||||
it("removes only thinkingBudget and preserves other thinkingConfig keys", () => {
|
||||
const payload = {
|
||||
config: {
|
||||
thinkingConfig: { thinkingBudget: 0, includeThoughts: true },
|
||||
},
|
||||
};
|
||||
sanitizeGoogleThinkingPayload({ payload, modelId: "gemini-2.5-pro" });
|
||||
expect(payload.config.thinkingConfig).not.toHaveProperty("thinkingBudget");
|
||||
expect(payload.config.thinkingConfig).toHaveProperty("includeThoughts", true);
|
||||
});
|
||||
|
||||
it("keeps thinkingBudget=0 for gemini-2.5-flash (not thinking-required)", () => {
|
||||
const payload = {
|
||||
config: {
|
||||
thinkingConfig: { thinkingBudget: 0 },
|
||||
},
|
||||
};
|
||||
sanitizeGoogleThinkingPayload({ payload, modelId: "gemini-2.5-flash" });
|
||||
expect(payload.config.thinkingConfig).toHaveProperty("thinkingBudget", 0);
|
||||
});
|
||||
|
||||
it("keeps positive thinkingBudget for gemini-2.5-pro", () => {
|
||||
const payload = {
|
||||
config: {
|
||||
thinkingConfig: { thinkingBudget: 1000 },
|
||||
},
|
||||
};
|
||||
sanitizeGoogleThinkingPayload({ payload, modelId: "gemini-2.5-pro" });
|
||||
expect(payload.config.thinkingConfig).toHaveProperty("thinkingBudget", 1000);
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,12 @@ function isGemma4Model(modelId: string): boolean {
|
||||
return normalizeLowercaseStringOrEmpty(modelId).startsWith("gemma-4");
|
||||
}
|
||||
|
||||
// Gemini 2.5 Pro only works in thinking mode and rejects thinkingBudget=0 with
|
||||
// "Budget 0 is invalid. This model only works in thinking mode."
|
||||
function isThinkingRequiredModel(modelId: string): boolean {
|
||||
return normalizeLowercaseStringOrEmpty(modelId).includes("gemini-2.5-pro");
|
||||
}
|
||||
|
||||
function mapThinkLevelToGoogleThinkingLevel(
|
||||
thinkingLevel: ThinkLevel,
|
||||
): "MINIMAL" | "LOW" | "MEDIUM" | "HIGH" | undefined {
|
||||
@@ -116,6 +122,20 @@ export function sanitizeGoogleThinkingPayload(params: {
|
||||
}
|
||||
|
||||
const thinkingBudget = thinkingConfigObj.thinkingBudget;
|
||||
|
||||
// Gemini 2.5 Pro rejects thinkingBudget=0; remove it so the API uses its default.
|
||||
if (
|
||||
thinkingBudget === 0 &&
|
||||
typeof params.modelId === "string" &&
|
||||
isThinkingRequiredModel(params.modelId)
|
||||
) {
|
||||
delete thinkingConfigObj.thinkingBudget;
|
||||
if (Object.keys(thinkingConfigObj).length === 0) {
|
||||
delete configObj.thinkingConfig;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof thinkingBudget !== "number" || thinkingBudget >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user