From cbcfeb92ccfdb1fc63938c81d57b558854fcfbf4 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Fri, 21 Nov 2025 00:48:12 +0800 Subject: [PATCH] Fixed: #291 **feat(executor): add thinking level to budget conversion utility** - Introduced `ConvertThinkingLevelToBudget` to map thinking level ("high"/"low") to corresponding budget values. - Applied the utility in `aistudio_executor.go` before stripping unsupported configs. - Updated dependencies to include `tidwall/gjson` for JSON parsing. --- .../runtime/executor/aistudio_executor.go | 1 + internal/util/gemini_thinking.go | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/internal/runtime/executor/aistudio_executor.go b/internal/runtime/executor/aistudio_executor.go index 8373af47..ac7e3066 100644 --- a/internal/runtime/executor/aistudio_executor.go +++ b/internal/runtime/executor/aistudio_executor.go @@ -264,6 +264,7 @@ func (e *AIStudioExecutor) translateRequest(req cliproxyexecutor.Request, opts c } payload = util.ApplyGeminiThinkingConfig(payload, budgetOverride, includeOverride) } + payload = util.ConvertThinkingLevelToBudget(payload) payload = util.StripThinkingConfigIfUnsupported(req.Model, payload) payload = fixGeminiImageAspectRatio(req.Model, payload) payload = applyPayloadConfig(e.cfg, req.Model, payload) diff --git a/internal/util/gemini_thinking.go b/internal/util/gemini_thinking.go index 80c1730a..d7481621 100644 --- a/internal/util/gemini_thinking.go +++ b/internal/util/gemini_thinking.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) @@ -212,3 +213,49 @@ func StripThinkingConfigIfUnsupported(model string, body []byte) []byte { updated, _ = sjson.DeleteBytes(updated, "generationConfig.thinkingConfig") return updated } + +// ConvertThinkingLevelToBudget checks for "generationConfig.thinkingConfig.thinkingLevel" +// and converts it to "thinkingBudget". +// "high" -> 32768 +// "low" -> 128 +// It removes "thinkingLevel" after conversion. +func ConvertThinkingLevelToBudget(body []byte) []byte { + levelPath := "generationConfig.thinkingConfig.thinkingLevel" + res := gjson.GetBytes(body, levelPath) + if !res.Exists() { + return body + } + + level := strings.ToLower(res.String()) + var budget int + switch level { + case "high": + budget = 32768 + case "low": + budget = 128 + default: + // If unknown level, we might just leave it or default. + // User only specified high and low. We'll assume we shouldn't touch it if it's something else, + // or maybe we should just remove the invalid level? + // For safety adhering to strict instructions: "If high... if low...". + // If it's something else, the upstream might fail anyway if we leave it, + // but let's just delete the level if we processed it. + // Actually, let's check if we need to do anything for other values. + // For now, only handle high/low. + return body + } + + // Set budget + budgetPath := "generationConfig.thinkingConfig.thinkingBudget" + updated, err := sjson.SetBytes(body, budgetPath, budget) + if err != nil { + return body + } + + // Remove level + updated, err = sjson.DeleteBytes(updated, levelPath) + if err != nil { + return body + } + return updated +}