From 9e5a7ed541e502b92da0c71fda03a6d83d743bd0 Mon Sep 17 00:00:00 2001 From: Yan <1964649083@qq.com> Date: Sun, 29 Dec 2024 03:58:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Gemini=20=E5=87=BD=E6=95=B0=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E6=96=87=E6=9C=AC=E8=BD=AC=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- relay/channel/gemini/relay-gemini.go | 115 ++++++++++++++++++--------- 1 file changed, 78 insertions(+), 37 deletions(-) diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go index ebdd1dd3..89f3e202 100644 --- a/relay/channel/gemini/relay-gemini.go +++ b/relay/channel/gemini/relay-gemini.go @@ -12,6 +12,7 @@ import ( relaycommon "one-api/relay/common" "one-api/service" "strings" + "unicode/utf8" "github.com/gin-gonic/gin" ) @@ -279,57 +280,97 @@ func removeAdditionalPropertiesWithDepth(schema interface{}, depth int) interfac return v } -// func (g *GeminiChatResponse) GetResponseText() string { -// if g == nil { -// return "" -// } -// if len(g.Candidates) > 0 && len(g.Candidates[0].Content.Parts) > 0 { -// return g.Candidates[0].Content.Parts[0].Text -// } -// return "" -// } +func unescapeString(s string) (string, error) { + var result []rune + escaped := false + i := 0 + + for i < len(s) { + r, size := utf8.DecodeRuneInString(s[i:]) // 正确解码UTF-8字符 + if r == utf8.RuneError { + return "", fmt.Errorf("invalid UTF-8 encoding") + } + + if escaped { + // 如果是转义符后的字符,检查其类型 + switch r { + case '"': + result = append(result, '"') + case '\\': + result = append(result, '\\') + case '/': + result = append(result, '/') + case 'b': + result = append(result, '\b') + case 'f': + result = append(result, '\f') + case 'n': + result = append(result, '\n') + case 'r': + result = append(result, '\r') + case 't': + result = append(result, '\t') + case '\'': + result = append(result, '\'') + default: + // 如果遇到一个非法的转义字符,直接按原样输出 + result = append(result, '\\', r) + } + escaped = false + } else { + if r == '\\' { + escaped = true // 记录反斜杠作为转义符 + } else { + result = append(result, r) + } + } + i += size // 移动到下一个字符 + } + + return string(result), nil +} +func unescapeMapOrSlice(data interface{}) interface{} { + switch v := data.(type) { + case map[string]interface{}: + for k, val := range v { + v[k] = unescapeMapOrSlice(val) + } + case []interface{}: + for i, val := range v { + v[i] = unescapeMapOrSlice(val) + } + case string: + if unescaped, err := unescapeString(v); err != nil { + return v + } else { + return unescaped + } + } + return data +} func getToolCall(item *GeminiPart) *dto.ToolCall { - argsBytes, err := json.Marshal(item.FunctionCall.Arguments) + var argsBytes []byte + var err error + if result, ok := item.FunctionCall.Arguments.(map[string]interface{}); ok { + argsBytes, err = json.Marshal(unescapeMapOrSlice(result)) + } else { + argsBytes, err = json.Marshal(item.FunctionCall.Arguments) + } + if err != nil { - //common.SysError("getToolCall failed: " + err.Error()) return nil } return &dto.ToolCall{ ID: fmt.Sprintf("call_%s", common.GetUUID()), Type: "function", Function: dto.FunctionCall{ - // 不好评价,得去转义一下反斜杠,Gemini 的特性好像是,Google 返回的时候本身就会转义“\” - Arguments: strings.ReplaceAll(string(argsBytes), "\\\\", "\\"), + Arguments: string(argsBytes), Name: item.FunctionCall.FunctionName, }, } } -// func getToolCalls(candidate *GeminiChatCandidate, index int) []dto.ToolCall { -// var toolCalls []dto.ToolCall - -// item := candidate.Content.Parts[index] -// if item.FunctionCall == nil { -// return toolCalls -// } -// argsBytes, err := json.Marshal(item.FunctionCall.Arguments) -// if err != nil { -// //common.SysError("getToolCalls failed: " + err.Error()) -// return toolCalls -// } -// toolCall := dto.ToolCall{ -// ID: fmt.Sprintf("call_%s", common.GetUUID()), -// Type: "function", -// Function: dto.FunctionCall{ -// Arguments: string(argsBytes), -// Name: item.FunctionCall.FunctionName, -// }, -// } -// toolCalls = append(toolCalls, toolCall) -// return toolCalls -// } - func responseGeminiChat2OpenAI(response *GeminiChatResponse) *dto.OpenAITextResponse { fullTextResponse := dto.OpenAITextResponse{ Id: fmt.Sprintf("chatcmpl-%s", common.GetUUID()),