mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-19 04:40:52 +08:00
fix(gemini): add optional skip for gemini3 thinking conversion
This commit is contained in:
@@ -325,8 +325,8 @@ func (e *AIStudioExecutor) translateRequest(req cliproxyexecutor.Request, opts c
|
|||||||
payload = ApplyThinkingMetadata(payload, req.Metadata, req.Model)
|
payload = ApplyThinkingMetadata(payload, req.Metadata, req.Model)
|
||||||
payload = util.ApplyGemini3ThinkingLevelFromMetadata(req.Model, req.Metadata, payload)
|
payload = util.ApplyGemini3ThinkingLevelFromMetadata(req.Model, req.Metadata, payload)
|
||||||
payload = util.ApplyDefaultThinkingIfNeeded(req.Model, payload)
|
payload = util.ApplyDefaultThinkingIfNeeded(req.Model, payload)
|
||||||
payload = util.ConvertThinkingLevelToBudget(payload, req.Model)
|
payload = util.ConvertThinkingLevelToBudget(payload, req.Model, true)
|
||||||
payload = util.NormalizeGeminiThinkingBudget(req.Model, payload)
|
payload = util.NormalizeGeminiThinkingBudget(req.Model, payload, true)
|
||||||
payload = util.StripThinkingConfigIfUnsupported(req.Model, payload)
|
payload = util.StripThinkingConfigIfUnsupported(req.Model, payload)
|
||||||
payload = fixGeminiImageAspectRatio(req.Model, payload)
|
payload = fixGeminiImageAspectRatio(req.Model, payload)
|
||||||
payload = applyPayloadConfig(e.cfg, req.Model, payload)
|
payload = applyPayloadConfig(e.cfg, req.Model, payload)
|
||||||
|
|||||||
@@ -352,8 +352,9 @@ func StripThinkingConfigIfUnsupported(model string, body []byte) []byte {
|
|||||||
|
|
||||||
// NormalizeGeminiThinkingBudget normalizes the thinkingBudget value in a standard Gemini
|
// NormalizeGeminiThinkingBudget normalizes the thinkingBudget value in a standard Gemini
|
||||||
// request body (generationConfig.thinkingConfig.thinkingBudget path).
|
// request body (generationConfig.thinkingConfig.thinkingBudget path).
|
||||||
// For Gemini 3 models, converts thinkingBudget to thinkingLevel per Google's documentation.
|
// For Gemini 3 models, converts thinkingBudget to thinkingLevel per Google's documentation,
|
||||||
func NormalizeGeminiThinkingBudget(model string, body []byte) []byte {
|
// unless skipGemini3Check is provided and true.
|
||||||
|
func NormalizeGeminiThinkingBudget(model string, body []byte, skipGemini3Check ...bool) []byte {
|
||||||
const budgetPath = "generationConfig.thinkingConfig.thinkingBudget"
|
const budgetPath = "generationConfig.thinkingConfig.thinkingBudget"
|
||||||
const levelPath = "generationConfig.thinkingConfig.thinkingLevel"
|
const levelPath = "generationConfig.thinkingConfig.thinkingLevel"
|
||||||
|
|
||||||
@@ -363,7 +364,8 @@ func NormalizeGeminiThinkingBudget(model string, body []byte) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For Gemini 3 models, convert thinkingBudget to thinkingLevel
|
// For Gemini 3 models, convert thinkingBudget to thinkingLevel
|
||||||
if IsGemini3Model(model) {
|
skipGemini3 := len(skipGemini3Check) > 0 && skipGemini3Check[0]
|
||||||
|
if IsGemini3Model(model) && !skipGemini3 {
|
||||||
if level, ok := ThinkingBudgetToGemini3Level(model, int(budget.Int())); ok {
|
if level, ok := ThinkingBudgetToGemini3Level(model, int(budget.Int())); ok {
|
||||||
updated, _ := sjson.SetBytes(body, levelPath, level)
|
updated, _ := sjson.SetBytes(body, levelPath, level)
|
||||||
updated, _ = sjson.DeleteBytes(updated, budgetPath)
|
updated, _ = sjson.DeleteBytes(updated, budgetPath)
|
||||||
@@ -382,8 +384,9 @@ func NormalizeGeminiThinkingBudget(model string, body []byte) []byte {
|
|||||||
|
|
||||||
// NormalizeGeminiCLIThinkingBudget normalizes the thinkingBudget value in a Gemini CLI
|
// NormalizeGeminiCLIThinkingBudget normalizes the thinkingBudget value in a Gemini CLI
|
||||||
// request body (request.generationConfig.thinkingConfig.thinkingBudget path).
|
// request body (request.generationConfig.thinkingConfig.thinkingBudget path).
|
||||||
// For Gemini 3 models, converts thinkingBudget to thinkingLevel per Google's documentation.
|
// For Gemini 3 models, converts thinkingBudget to thinkingLevel per Google's documentation,
|
||||||
func NormalizeGeminiCLIThinkingBudget(model string, body []byte) []byte {
|
// unless skipGemini3Check is provided and true.
|
||||||
|
func NormalizeGeminiCLIThinkingBudget(model string, body []byte, skipGemini3Check ...bool) []byte {
|
||||||
const budgetPath = "request.generationConfig.thinkingConfig.thinkingBudget"
|
const budgetPath = "request.generationConfig.thinkingConfig.thinkingBudget"
|
||||||
const levelPath = "request.generationConfig.thinkingConfig.thinkingLevel"
|
const levelPath = "request.generationConfig.thinkingConfig.thinkingLevel"
|
||||||
|
|
||||||
@@ -393,7 +396,8 @@ func NormalizeGeminiCLIThinkingBudget(model string, body []byte) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For Gemini 3 models, convert thinkingBudget to thinkingLevel
|
// For Gemini 3 models, convert thinkingBudget to thinkingLevel
|
||||||
if IsGemini3Model(model) {
|
skipGemini3 := len(skipGemini3Check) > 0 && skipGemini3Check[0]
|
||||||
|
if IsGemini3Model(model) && !skipGemini3 {
|
||||||
if level, ok := ThinkingBudgetToGemini3Level(model, int(budget.Int())); ok {
|
if level, ok := ThinkingBudgetToGemini3Level(model, int(budget.Int())); ok {
|
||||||
updated, _ := sjson.SetBytes(body, levelPath, level)
|
updated, _ := sjson.SetBytes(body, levelPath, level)
|
||||||
updated, _ = sjson.DeleteBytes(updated, budgetPath)
|
updated, _ = sjson.DeleteBytes(updated, budgetPath)
|
||||||
@@ -477,7 +481,7 @@ func ApplyReasoningEffortToGeminiCLI(body []byte, effort string) []byte {
|
|||||||
|
|
||||||
// ConvertThinkingLevelToBudget checks for "generationConfig.thinkingConfig.thinkingLevel"
|
// ConvertThinkingLevelToBudget checks for "generationConfig.thinkingConfig.thinkingLevel"
|
||||||
// and converts it to "thinkingBudget" for Gemini 2.5 models.
|
// and converts it to "thinkingBudget" for Gemini 2.5 models.
|
||||||
// For Gemini 3 models, preserves thinkingLevel as-is (does not convert).
|
// For Gemini 3 models, preserves thinkingLevel unless skipGemini3Check is provided and true.
|
||||||
// Mappings for Gemini 2.5:
|
// Mappings for Gemini 2.5:
|
||||||
// - "high" -> 32768
|
// - "high" -> 32768
|
||||||
// - "medium" -> 8192
|
// - "medium" -> 8192
|
||||||
@@ -485,43 +489,31 @@ func ApplyReasoningEffortToGeminiCLI(body []byte, effort string) []byte {
|
|||||||
// - "minimal" -> 512
|
// - "minimal" -> 512
|
||||||
//
|
//
|
||||||
// It removes "thinkingLevel" after conversion (for Gemini 2.5 only).
|
// It removes "thinkingLevel" after conversion (for Gemini 2.5 only).
|
||||||
func ConvertThinkingLevelToBudget(body []byte, model string) []byte {
|
func ConvertThinkingLevelToBudget(body []byte, model string, skipGemini3Check ...bool) []byte {
|
||||||
levelPath := "generationConfig.thinkingConfig.thinkingLevel"
|
levelPath := "generationConfig.thinkingConfig.thinkingLevel"
|
||||||
res := gjson.GetBytes(body, levelPath)
|
res := gjson.GetBytes(body, levelPath)
|
||||||
if !res.Exists() {
|
if !res.Exists() {
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Gemini 3 models, preserve thinkingLevel - don't convert to budget
|
// For Gemini 3 models, preserve thinkingLevel unless explicitly skipped
|
||||||
if IsGemini3Model(model) {
|
skipGemini3 := len(skipGemini3Check) > 0 && skipGemini3Check[0]
|
||||||
|
if IsGemini3Model(model) && !skipGemini3 {
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
level := strings.ToLower(res.String())
|
budget, ok := ThinkingLevelToBudget(res.String())
|
||||||
var budget int
|
if !ok {
|
||||||
switch level {
|
|
||||||
case "high":
|
|
||||||
budget = 32768
|
|
||||||
case "medium":
|
|
||||||
budget = 8192
|
|
||||||
case "low":
|
|
||||||
budget = 1024
|
|
||||||
case "minimal":
|
|
||||||
budget = 512
|
|
||||||
default:
|
|
||||||
// Unknown level - remove it and let the API use defaults
|
|
||||||
updated, _ := sjson.DeleteBytes(body, levelPath)
|
updated, _ := sjson.DeleteBytes(body, levelPath)
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set budget
|
|
||||||
budgetPath := "generationConfig.thinkingConfig.thinkingBudget"
|
budgetPath := "generationConfig.thinkingConfig.thinkingBudget"
|
||||||
updated, err := sjson.SetBytes(body, budgetPath, budget)
|
updated, err := sjson.SetBytes(body, budgetPath, budget)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove level
|
|
||||||
updated, err = sjson.DeleteBytes(updated, levelPath)
|
updated, err = sjson.DeleteBytes(updated, levelPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return body
|
return body
|
||||||
@@ -544,31 +536,18 @@ func ConvertThinkingLevelToBudgetCLI(body []byte, model string) []byte {
|
|||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
level := strings.ToLower(res.String())
|
budget, ok := ThinkingLevelToBudget(res.String())
|
||||||
var budget int
|
if !ok {
|
||||||
switch level {
|
|
||||||
case "high":
|
|
||||||
budget = 32768
|
|
||||||
case "medium":
|
|
||||||
budget = 8192
|
|
||||||
case "low":
|
|
||||||
budget = 1024
|
|
||||||
case "minimal":
|
|
||||||
budget = 512
|
|
||||||
default:
|
|
||||||
// Unknown level - remove it and let the API use defaults
|
|
||||||
updated, _ := sjson.DeleteBytes(body, levelPath)
|
updated, _ := sjson.DeleteBytes(body, levelPath)
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set budget
|
|
||||||
budgetPath := "request.generationConfig.thinkingConfig.thinkingBudget"
|
budgetPath := "request.generationConfig.thinkingConfig.thinkingBudget"
|
||||||
updated, err := sjson.SetBytes(body, budgetPath, budget)
|
updated, err := sjson.SetBytes(body, budgetPath, budget)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove level
|
|
||||||
updated, err = sjson.DeleteBytes(updated, levelPath)
|
updated, err = sjson.DeleteBytes(updated, levelPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return body
|
return body
|
||||||
|
|||||||
@@ -160,6 +160,34 @@ func ThinkingEffortToBudget(model, effort string) (int, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ThinkingLevelToBudget maps a Gemini thinkingLevel to a numeric thinking budget (tokens).
|
||||||
|
//
|
||||||
|
// Mappings:
|
||||||
|
// - "minimal" -> 512
|
||||||
|
// - "low" -> 1024
|
||||||
|
// - "medium" -> 8192
|
||||||
|
// - "high" -> 32768
|
||||||
|
//
|
||||||
|
// Returns false when the level is empty or unsupported.
|
||||||
|
func ThinkingLevelToBudget(level string) (int, bool) {
|
||||||
|
if level == "" {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
normalized := strings.ToLower(strings.TrimSpace(level))
|
||||||
|
switch normalized {
|
||||||
|
case "minimal":
|
||||||
|
return 512, true
|
||||||
|
case "low":
|
||||||
|
return 1024, true
|
||||||
|
case "medium":
|
||||||
|
return 8192, true
|
||||||
|
case "high":
|
||||||
|
return 32768, true
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ThinkingBudgetToEffort maps a numeric thinking budget (tokens)
|
// ThinkingBudgetToEffort maps a numeric thinking budget (tokens)
|
||||||
// to a reasoning effort level for level-based models.
|
// to a reasoning effort level for level-based models.
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user