From ede4471b84bffc11fe78781c54ea710831acc6fb Mon Sep 17 00:00:00 2001 From: Ben Vargas Date: Wed, 19 Nov 2025 12:46:13 -0700 Subject: [PATCH] feat(translator): add default thinkingConfig for gemini-3-pro-preview Match official Gemini CLI behavior by always sending default thinkingConfig when client doesn't specify reasoning parameters. - Set thinkingBudget=-1 (dynamic) for gemini-3-pro-preview - Set include_thoughts=true to return thinking process - Apply to both /v1/chat/completions and /v1/responses endpoints - See: ai-gemini-cli/packages/core/src/config/defaultModelConfigs.ts --- .../chat-completions/gemini-cli_openai_request.go | 9 +++++++++ .../responses/gemini_openai-responses_request.go | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go b/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go index 6f7ac724..99b50366 100644 --- a/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go +++ b/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go @@ -88,6 +88,15 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo } } + // For gemini-3-pro-preview, always send default thinkingConfig when none specified. + // This matches the official Gemini CLI behavior which always sends: + // { thinkingBudget: -1, includeThoughts: true } + // See: ai-gemini-cli/packages/core/src/config/defaultModelConfigs.ts + if !gjson.GetBytes(out, "request.generationConfig.thinkingConfig").Exists() && modelName == "gemini-3-pro-preview" { + out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.thinkingBudget", -1) + out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.include_thoughts", true) + } + // Temperature/top_p/top_k if tr := gjson.GetBytes(rawJSON, "temperature"); tr.Exists() && tr.Type == gjson.Number { out, _ = sjson.SetBytes(out, "request.generationConfig.temperature", tr.Num) diff --git a/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go b/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go index 4eeebf3c..981fafc1 100644 --- a/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go +++ b/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go @@ -6,6 +6,7 @@ import ( "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" + log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) @@ -294,6 +295,17 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte } } } + + // For gemini-3-pro-preview, always send default thinkingConfig when none specified. + // This matches the official Gemini CLI behavior which always sends: + // { thinkingBudget: -1, includeThoughts: true } + // See: ai-gemini-cli/packages/core/src/config/defaultModelConfigs.ts + if !gjson.Get(out, "generationConfig.thinkingConfig").Exists() && modelName == "gemini-3-pro-preview" { + out, _ = sjson.Set(out, "generationConfig.thinkingConfig.thinkingBudget", -1) + out, _ = sjson.Set(out, "generationConfig.thinkingConfig.include_thoughts", true) + log.Debugf("Applied default thinkingConfig for gemini-3-pro-preview (matches Gemini CLI): thinkingBudget=-1, include_thoughts=true") + } + result := []byte(out) result = common.AttachDefaultSafetySettings(result, "safetySettings") return result