From 2ec45656015e9e3c834aa185eb630863432180e9 Mon Sep 17 00:00:00 2001 From: CaIon <1808837298@qq.com> Date: Thu, 10 Apr 2025 22:35:03 +0800 Subject: [PATCH] feat: implement parameter cleaning for Gemini functions --- relay/channel/gemini/relay-gemini.go | 104 +++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go index e1cd110c..2a7fa9ac 100644 --- a/relay/channel/gemini/relay-gemini.go +++ b/relay/channel/gemini/relay-gemini.go @@ -56,6 +56,7 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest) (*GeminiChatReque continue } if tool.Function.Parameters != nil { + params, ok := tool.Function.Parameters.(map[string]interface{}) if ok { if props, hasProps := params["properties"].(map[string]interface{}); hasProps { @@ -65,6 +66,9 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest) (*GeminiChatReque } } } + // Clean the parameters before appending + cleanedParams := cleanFunctionParameters(tool.Function.Parameters) + tool.Function.Parameters = cleanedParams functions = append(functions, tool.Function) } if codeExecution { @@ -86,11 +90,11 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest) (*GeminiChatReque // json_data, _ := json.Marshal(geminiRequest.Tools) // common.SysLog("tools_json: " + string(json_data)) } else if textRequest.Functions != nil { - geminiRequest.Tools = []GeminiChatTool{ - { - FunctionDeclarations: textRequest.Functions, - }, - } + //geminiRequest.Tools = []GeminiChatTool{ + // { + // FunctionDeclarations: textRequest.Functions, + // }, + //} } if textRequest.ResponseFormat != nil && (textRequest.ResponseFormat.Type == "json_schema" || textRequest.ResponseFormat.Type == "json_object") { @@ -229,6 +233,96 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest) (*GeminiChatReque return &geminiRequest, nil } + +// cleanFunctionParameters recursively removes unsupported fields from Gemini function parameters. +func cleanFunctionParameters(params interface{}) interface{} { + if params == nil { + return nil + } + + paramMap, ok := params.(map[string]interface{}) + if !ok { + // Not a map, return as is (e.g., could be an array or primitive) + return params + } + + // Create a copy to avoid modifying the original + cleanedMap := make(map[string]interface{}) + for k, v := range paramMap { + cleanedMap[k] = v + } + + // Clean properties + if props, ok := cleanedMap["properties"].(map[string]interface{}); ok && props != nil { + cleanedProps := make(map[string]interface{}) + for propName, propValue := range props { + propMap, ok := propValue.(map[string]interface{}) + if !ok { + cleanedProps[propName] = propValue // Keep non-map properties + continue + } + + // Create a copy of the property map + cleanedPropMap := make(map[string]interface{}) + for k, v := range propMap { + cleanedPropMap[k] = v + } + + // Remove unsupported fields + delete(cleanedPropMap, "default") + delete(cleanedPropMap, "exclusiveMaximum") + delete(cleanedPropMap, "exclusiveMinimum") + + // Check and clean 'format' for string types + if propType, typeExists := cleanedPropMap["type"].(string); typeExists && propType == "string" { + if formatValue, formatExists := cleanedPropMap["format"].(string); formatExists { + if formatValue != "enum" && formatValue != "date-time" { + delete(cleanedPropMap, "format") + } + } + } + + // Recursively clean nested properties within this property if it's an object/array + // Check the type before recursing + if propType, typeExists := cleanedPropMap["type"].(string); typeExists && (propType == "object" || propType == "array") { + cleanedProps[propName] = cleanFunctionParameters(cleanedPropMap) + } else { + cleanedProps[propName] = cleanedPropMap // Assign the cleaned map back if not recursing + } + + } + cleanedMap["properties"] = cleanedProps + } + + // Recursively clean items in arrays if needed (e.g., type: array, items: { ... }) + if items, ok := cleanedMap["items"].(map[string]interface{}); ok && items != nil { + cleanedMap["items"] = cleanFunctionParameters(items) + } + // Also handle items if it's an array of schemas + if itemsArray, ok := cleanedMap["items"].([]interface{}); ok { + cleanedItemsArray := make([]interface{}, len(itemsArray)) + for i, item := range itemsArray { + cleanedItemsArray[i] = cleanFunctionParameters(item) + } + cleanedMap["items"] = cleanedItemsArray + } + + + // Recursively clean other schema composition keywords if necessary + for _, field := range []string{"allOf", "anyOf", "oneOf"} { + if nested, ok := cleanedMap[field].([]interface{}); ok { + cleanedNested := make([]interface{}, len(nested)) + for i, item := range nested { + cleanedNested[i] = cleanFunctionParameters(item) + } + cleanedMap[field] = cleanedNested + } + } + + return cleanedMap +} + + func removeAdditionalPropertiesWithDepth(schema interface{}, depth int) interface{} { if depth >= 5 { return schema