Просмотр исходного кода

fix: Gemini 函数调用的文本转义

Yan 1 год назад
Родитель
Сommit
9e5a7ed541
1 измененных файлов с 78 добавлено и 37 удалено
  1. 78 37
      relay/channel/gemini/relay-gemini.go

+ 78 - 37
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()),