From f13e4bf486905f840fa12ffa81d99b7a83d5b194 Mon Sep 17 00:00:00 2001 From: RedwindA Date: Fri, 6 Jun 2025 00:56:38 +0800 Subject: [PATCH 1/3] Fix: Correctly relay FunctionResponse content for Gemini API --- relay/channel/gemini/dto.go | 12 +++--- relay/channel/gemini/relay-gemini.go | 55 +++++++++++++++------------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/relay/channel/gemini/dto.go b/relay/channel/gemini/dto.go index a0e38cb4..65d6fc65 100644 --- a/relay/channel/gemini/dto.go +++ b/relay/channel/gemini/dto.go @@ -27,14 +27,14 @@ type FunctionCall struct { Arguments any `json:"args"` } -type GeminiFunctionResponseContent struct { - Name string `json:"name"` - Content any `json:"content"` -} +// type GeminiFunctionResponseContent struct { +// Name string `json:"name"` +// Content any `json:"content"` +// } type FunctionResponse struct { - Name string `json:"name"` - Response GeminiFunctionResponseContent `json:"response"` + Name string `json:"name"` + Response map[string]interface{} `json:"response"` } type GeminiPartExecutableCode struct { diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go index bf1ece57..e37e9762 100644 --- a/relay/channel/gemini/relay-gemini.go +++ b/relay/channel/gemini/relay-gemini.go @@ -57,22 +57,22 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon } if model_setting.GetGeminiSettings().ThinkingAdapterEnabled { - if strings.HasSuffix(info.OriginModelName, "-thinking") { - // 如果模型名以 gemini-2.5-pro 开头,不设置 ThinkingBudget - if strings.HasPrefix(info.OriginModelName, "gemini-2.5-pro") { - geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{ - IncludeThoughts: true, - } - } else { - budgetTokens := model_setting.GetGeminiSettings().ThinkingAdapterBudgetTokensPercentage * float64(geminiRequest.GenerationConfig.MaxOutputTokens) - if budgetTokens == 0 || budgetTokens > 24576 { - budgetTokens = 24576 - } - geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{ - ThinkingBudget: common.GetPointer(int(budgetTokens)), - IncludeThoughts: true, - } - } + if strings.HasSuffix(info.OriginModelName, "-thinking") { + // 如果模型名以 gemini-2.5-pro 开头,不设置 ThinkingBudget + if strings.HasPrefix(info.OriginModelName, "gemini-2.5-pro") { + geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{ + IncludeThoughts: true, + } + } else { + budgetTokens := model_setting.GetGeminiSettings().ThinkingAdapterBudgetTokensPercentage * float64(geminiRequest.GenerationConfig.MaxOutputTokens) + if budgetTokens == 0 || budgetTokens > 24576 { + budgetTokens = 24576 + } + geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{ + ThinkingBudget: common.GetPointer(int(budgetTokens)), + IncludeThoughts: true, + } + } } else if strings.HasSuffix(info.OriginModelName, "-nothinking") { geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{ ThinkingBudget: common.GetPointer(0), @@ -173,17 +173,22 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon } else if val, exists := tool_call_ids[message.ToolCallId]; exists { name = val } - content := common.StrToMap(message.StringContent()) + contentMap := common.StrToMap(message.StringContent()) functionResp := &FunctionResponse{ - Name: name, - Response: GeminiFunctionResponseContent{ - Name: name, - Content: content, - }, - } - if content == nil { - functionResp.Response.Content = message.StringContent() + Name: name, + Response: contentMap, } + // If StrToMap returns nil because message.StringContent() is not a valid JSON object string, + // and Gemini strictly requires an object (e.g., {}), this might need adjustment. + // For example: + // if contentMap == nil && message.StringContent() != "" { + // // Option 1: Send an empty object if that's preferred over null + // // functionResp.Response = make(map[string]interface{}) + // // Option 2: Wrap the plain string if that's ever the case and needs to be an object + // // functionResp.Response = map[string]interface{}{"text_content": message.StringContent()} + // } + // For now, directly assigning contentMap is the most straightforward fix for the reported issue. + *parts = append(*parts, GeminiPart{ FunctionResponse: functionResp, }) From a52a67176e8c37228fb81c952ab2df61a1f5fce5 Mon Sep 17 00:00:00 2001 From: RedwindA Date: Fri, 6 Jun 2025 01:09:51 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=B8=85=E7=90=86=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- relay/channel/gemini/dto.go | 5 ----- relay/channel/gemini/relay-gemini.go | 10 ---------- 2 files changed, 15 deletions(-) diff --git a/relay/channel/gemini/dto.go b/relay/channel/gemini/dto.go index 65d6fc65..b1dffe90 100644 --- a/relay/channel/gemini/dto.go +++ b/relay/channel/gemini/dto.go @@ -27,11 +27,6 @@ type FunctionCall struct { Arguments any `json:"args"` } -// type GeminiFunctionResponseContent struct { -// Name string `json:"name"` -// Content any `json:"content"` -// } - type FunctionResponse struct { Name string `json:"name"` Response map[string]interface{} `json:"response"` diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go index e37e9762..0a4a900e 100644 --- a/relay/channel/gemini/relay-gemini.go +++ b/relay/channel/gemini/relay-gemini.go @@ -178,16 +178,6 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon Name: name, Response: contentMap, } - // If StrToMap returns nil because message.StringContent() is not a valid JSON object string, - // and Gemini strictly requires an object (e.g., {}), this might need adjustment. - // For example: - // if contentMap == nil && message.StringContent() != "" { - // // Option 1: Send an empty object if that's preferred over null - // // functionResp.Response = make(map[string]interface{}) - // // Option 2: Wrap the plain string if that's ever the case and needs to be an object - // // functionResp.Response = map[string]interface{}{"text_content": message.StringContent()} - // } - // For now, directly assigning contentMap is the most straightforward fix for the reported issue. *parts = append(*parts, GeminiPart{ FunctionResponse: functionResp, From c2f209abb7010cb3cc64925c4427882b2611d639 Mon Sep 17 00:00:00 2001 From: RedwindA Date: Fri, 6 Jun 2025 01:29:06 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- relay/channel/gemini/relay-gemini.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go index 0a4a900e..161ff61e 100644 --- a/relay/channel/gemini/relay-gemini.go +++ b/relay/channel/gemini/relay-gemini.go @@ -58,8 +58,21 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon if model_setting.GetGeminiSettings().ThinkingAdapterEnabled { if strings.HasSuffix(info.OriginModelName, "-thinking") { - // 如果模型名以 gemini-2.5-pro 开头,不设置 ThinkingBudget - if strings.HasPrefix(info.OriginModelName, "gemini-2.5-pro") { + // 硬编码不支持 ThinkingBudget 的旧模型 + unsupportedModels := []string{ + "gemini-2.5-pro-preview-05-06", + "gemini-2.5-pro-preview-03-25", + } + + isUnsupported := false + for _, unsupportedModel := range unsupportedModels { + if strings.HasPrefix(info.OriginModelName, unsupportedModel) { + isUnsupported = true + break + } + } + + if isUnsupported { geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{ IncludeThoughts: true, }