fix(thinking): normalize effort mapping

Route OpenAI reasoning effort through ThinkingEffortToBudget for Claude
translators, preserve "minimal" when translating OpenAI Responses, and
treat blank/unknown efforts as no-ops for Gemini thinking configs.

Also map budget -1 to "auto" and expand cross-protocol thinking tests.
This commit is contained in:
hkfires
2025-12-14 20:11:30 +08:00
parent 712ce9f781
commit d20b71deb9
6 changed files with 160 additions and 142 deletions
+26 -14
View File
@@ -167,20 +167,26 @@ var ReasoningEffortBudgetMapping = map[string]int{
// 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
normalized := strings.ToLower(strings.TrimSpace(effort))
if normalized == "" {
return body
}
budgetPath := "generationConfig.thinkingConfig.thinkingBudget"
includePath := "generationConfig.thinkingConfig.include_thoughts"
if effort == "none" {
if normalized == "none" {
body, _ = sjson.DeleteBytes(body, "generationConfig.thinkingConfig")
} else {
body, _ = sjson.SetBytes(body, budgetPath, budget)
body, _ = sjson.SetBytes(body, includePath, true)
return body
}
budget, ok := ReasoningEffortBudgetMapping[normalized]
if !ok {
return body
}
body, _ = sjson.SetBytes(body, budgetPath, budget)
body, _ = sjson.SetBytes(body, includePath, true)
return body
}
@@ -188,20 +194,26 @@ func ApplyReasoningEffortToGemini(body []byte, effort string) []byte {
// 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
normalized := strings.ToLower(strings.TrimSpace(effort))
if normalized == "" {
return body
}
budgetPath := "request.generationConfig.thinkingConfig.thinkingBudget"
includePath := "request.generationConfig.thinkingConfig.include_thoughts"
if effort == "none" {
if normalized == "none" {
body, _ = sjson.DeleteBytes(body, "request.generationConfig.thinkingConfig")
} else {
body, _ = sjson.SetBytes(body, budgetPath, budget)
body, _ = sjson.SetBytes(body, includePath, true)
return body
}
budget, ok := ReasoningEffortBudgetMapping[normalized]
if !ok {
return body
}
body, _ = sjson.SetBytes(body, budgetPath, budget)
body, _ = sjson.SetBytes(body, includePath, true)
return body
}
+5 -2
View File
@@ -5,15 +5,18 @@ package util
//
// Ranges:
// - 0 -> "none"
// - -1 -> "auto"
// - 1..1024 -> "low"
// - 1025..8192 -> "medium"
// - 8193..24576 -> "high"
// - 24577.. -> highest supported level for the model (defaults to "xhigh")
//
// Negative values (except the dynamic -1 handled elsewhere) are treated as unsupported.
// Negative values other than -1 are treated as unsupported.
func OpenAIThinkingBudgetToEffort(model string, budget int) (string, bool) {
switch {
case budget < 0:
case budget == -1:
return "auto", true
case budget < -1:
return "", false
case budget == 0:
return "none", true