From d3170310ff3b3ea0f913389ab53b98ca819270ce Mon Sep 17 00:00:00 2001 From: CaIon Date: Mon, 11 Aug 2025 19:48:04 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20feat:=20Refactor=20Gemini=20too?= =?UTF-8?q?ls=20handling=20to=20support=20JSON=20raw=20message=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dto/gemini.go | 38 +++++++++++++++++++++++++++- relay/channel/gemini/relay-gemini.go | 11 ++++---- service/convert.go | 4 +-- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/dto/gemini.go b/dto/gemini.go index 915b7b81..6cb3e17a 100644 --- a/dto/gemini.go +++ b/dto/gemini.go @@ -3,16 +3,52 @@ package dto import ( "encoding/json" "one-api/common" + "strings" ) type GeminiChatRequest struct { Contents []GeminiChatContent `json:"contents"` SafetySettings []GeminiChatSafetySettings `json:"safetySettings,omitempty"` GenerationConfig GeminiChatGenerationConfig `json:"generationConfig,omitempty"` - Tools []GeminiChatTool `json:"tools,omitempty"` + Tools json.RawMessage `json:"tools,omitempty"` SystemInstructions *GeminiChatContent `json:"systemInstruction,omitempty"` } +func (r *GeminiChatRequest) GetTools() []GeminiChatTool { + var tools []GeminiChatTool + if strings.HasSuffix(string(r.Tools), "[") { + // is array + if err := common.Unmarshal(r.Tools, &tools); err != nil { + common.LogError(nil, "error_unmarshalling_tools: "+err.Error()) + return nil + } + } else if strings.HasPrefix(string(r.Tools), "{") { + // is object + singleTool := GeminiChatTool{} + if err := common.Unmarshal(r.Tools, &singleTool); err != nil { + common.LogError(nil, "error_unmarshalling_single_tool: "+err.Error()) + return nil + } + tools = []GeminiChatTool{singleTool} + } + return tools +} + +func (r *GeminiChatRequest) SetTools(tools []GeminiChatTool) { + if len(tools) == 0 { + r.Tools = json.RawMessage("[]") + return + } + + // Marshal the tools to JSON + data, err := common.Marshal(tools) + if err != nil { + common.LogError(nil, "error_marshalling_tools: "+err.Error()) + return + } + r.Tools = data +} + type GeminiThinkingConfig struct { IncludeThoughts bool `json:"includeThoughts,omitempty"` ThinkingBudget *int `json:"thinkingBudget,omitempty"` diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go index 24b42942..58efa1a5 100644 --- a/relay/channel/gemini/relay-gemini.go +++ b/relay/channel/gemini/relay-gemini.go @@ -267,24 +267,23 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon tool.Function.Parameters = cleanedParams functions = append(functions, tool.Function) } + geminiTools := geminiRequest.GetTools() if codeExecution { - geminiRequest.Tools = append(geminiRequest.Tools, dto.GeminiChatTool{ + geminiTools = append(geminiTools, dto.GeminiChatTool{ CodeExecution: make(map[string]string), }) } if googleSearch { - geminiRequest.Tools = append(geminiRequest.Tools, dto.GeminiChatTool{ + geminiTools = append(geminiTools, dto.GeminiChatTool{ GoogleSearch: make(map[string]string), }) } if len(functions) > 0 { - geminiRequest.Tools = append(geminiRequest.Tools, dto.GeminiChatTool{ + geminiTools = append(geminiTools, dto.GeminiChatTool{ FunctionDeclarations: functions, }) } - // common.SysLog("tools: " + fmt.Sprintf("%+v", geminiRequest.Tools)) - // json_data, _ := json.Marshal(geminiRequest.Tools) - // common.SysLog("tools_json: " + string(json_data)) + geminiRequest.SetTools(geminiTools) } if textRequest.ResponseFormat != nil && (textRequest.ResponseFormat.Type == "json_schema" || textRequest.ResponseFormat.Type == "json_object") { diff --git a/service/convert.go b/service/convert.go index a6cfce6c..ea219c4f 100644 --- a/service/convert.go +++ b/service/convert.go @@ -569,9 +569,9 @@ func GeminiToOpenAIRequest(geminiRequest *dto.GeminiChatRequest, info *relaycomm } // 转换工具调用 - if len(geminiRequest.Tools) > 0 { + if len(geminiRequest.GetTools()) > 0 { var tools []dto.ToolCallRequest - for _, tool := range geminiRequest.Tools { + for _, tool := range geminiRequest.GetTools() { if tool.FunctionDeclarations != nil { // 将 Gemini 的 FunctionDeclarations 转换为 OpenAI 的 ToolCallRequest functionDeclarations, ok := tool.FunctionDeclarations.([]dto.FunctionRequest) From e74c6f5de7d1b224d25683ce72e3560bcbb69aa3 Mon Sep 17 00:00:00 2001 From: CaIon Date: Mon, 11 Aug 2025 20:07:24 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20feat:=20Simplify=20response=20h?= =?UTF-8?q?andling=20by=20returning=20raw=20response=20body=20directly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- relay/channel/gemini/relay-gemini-native.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/relay/channel/gemini/relay-gemini-native.go b/relay/channel/gemini/relay-gemini-native.go index 247b41fd..7f2f51fb 100644 --- a/relay/channel/gemini/relay-gemini-native.go +++ b/relay/channel/gemini/relay-gemini-native.go @@ -53,13 +53,7 @@ func GeminiTextGenerationHandler(c *gin.Context, info *relaycommon.RelayInfo, re } } - // 直接返回 Gemini 原生格式的 JSON 响应 - jsonResponse, err := common.Marshal(geminiResponse) - if err != nil { - return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError) - } - - common.IOCopyBytesGracefully(c, resp, jsonResponse) + common.IOCopyBytesGracefully(c, resp, responseBody) return &usage, nil } From 14a9a99e2d1b006f00a053c6310453d321196b67 Mon Sep 17 00:00:00 2001 From: nekohy Date: Mon, 11 Aug 2025 21:35:20 +0800 Subject: [PATCH 3/3] fix: zhipu_v4 thinking --- dto/openai_request.go | 2 +- relay/channel/zhipu_4v/relay-zhipu_v4.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dto/openai_request.go b/dto/openai_request.go index 7337004f..7a23ca5c 100644 --- a/dto/openai_request.go +++ b/dto/openai_request.go @@ -54,7 +54,7 @@ type GeneralOpenAIRequest struct { Modalities json.RawMessage `json:"modalities,omitempty"` Audio json.RawMessage `json:"audio,omitempty"` EnableThinking any `json:"enable_thinking,omitempty"` // ali - THINKING json.RawMessage `json:"thinking,omitempty"` // doubao + THINKING json.RawMessage `json:"thinking,omitempty"` // doubao,zhipu_v4 ExtraBody json.RawMessage `json:"extra_body,omitempty"` SearchParameters any `json:"search_parameters,omitempty"` //xai WebSearchOptions *WebSearchOptions `json:"web_search_options,omitempty"` diff --git a/relay/channel/zhipu_4v/relay-zhipu_v4.go b/relay/channel/zhipu_4v/relay-zhipu_v4.go index cb8adfe4..aec87dd5 100644 --- a/relay/channel/zhipu_4v/relay-zhipu_v4.go +++ b/relay/channel/zhipu_4v/relay-zhipu_v4.go @@ -50,5 +50,6 @@ func requestOpenAI2Zhipu(request dto.GeneralOpenAIRequest) *dto.GeneralOpenAIReq Stop: Stop, Tools: request.Tools, ToolChoice: request.ToolChoice, + THINKING: request.THINKING, } }