fix(thinking): centralize reasoning_effort mapping

Move OpenAI `reasoning_effort` -> Gemini `thinkingConfig` budget logic into
shared helpers used by Gemini, Gemini CLI, and antigravity translators.

Normalize Claude thinking handling by preferring positive budgets, applying
budget token normalization, and gating by model support.

Always convert Gemini `thinkingBudget` back to OpenAI `reasoning_effort` to
support allowCompat models, and update tests for normalization behavior.
This commit is contained in:
hkfires
2025-12-14 14:45:01 +08:00
parent e8976f9898
commit 716aa71f6e
8 changed files with 83 additions and 102 deletions

View File

@@ -152,6 +152,59 @@ func NormalizeGeminiCLIThinkingBudget(model string, body []byte) []byte {
return updated
}
// ReasoningEffortBudgetMapping defines the thinkingBudget values for each reasoning effort level.
var ReasoningEffortBudgetMapping = map[string]int{
"none": 0,
"auto": -1,
"minimal": 512,
"low": 1024,
"medium": 8192,
"high": 24576,
"xhigh": 32768,
}
// ApplyReasoningEffortToGemini applies OpenAI reasoning_effort to Gemini thinkingConfig
// for standard Gemini API format (generationConfig.thinkingConfig path).
// Returns the modified body with thinkingBudget and include_thoughts set.
func ApplyReasoningEffortToGemini(body []byte, effort string) []byte {
budget, ok := ReasoningEffortBudgetMapping[effort]
if !ok {
budget = -1 // default to auto
}
budgetPath := "generationConfig.thinkingConfig.thinkingBudget"
includePath := "generationConfig.thinkingConfig.include_thoughts"
if effort == "none" {
body, _ = sjson.DeleteBytes(body, "generationConfig.thinkingConfig")
} else {
body, _ = sjson.SetBytes(body, budgetPath, budget)
body, _ = sjson.SetBytes(body, includePath, true)
}
return body
}
// ApplyReasoningEffortToGeminiCLI applies OpenAI reasoning_effort to Gemini CLI thinkingConfig
// for Gemini CLI API format (request.generationConfig.thinkingConfig path).
// Returns the modified body with thinkingBudget and include_thoughts set.
func ApplyReasoningEffortToGeminiCLI(body []byte, effort string) []byte {
budget, ok := ReasoningEffortBudgetMapping[effort]
if !ok {
budget = -1 // default to auto
}
budgetPath := "request.generationConfig.thinkingConfig.thinkingBudget"
includePath := "request.generationConfig.thinkingConfig.include_thoughts"
if effort == "none" {
body, _ = sjson.DeleteBytes(body, "request.generationConfig.thinkingConfig")
} else {
body, _ = sjson.SetBytes(body, budgetPath, budget)
body, _ = sjson.SetBytes(body, includePath, true)
}
return body
}
// ConvertThinkingLevelToBudget checks for "generationConfig.thinkingConfig.thinkingLevel"
// and converts it to "thinkingBudget".
// "high" -> 32768