fix(gemini): sanitize unsupported tool schema fields

This commit is contained in:
wucm667
2026-08-23 18:38:11 +08:00
parent d45135d87d
commit 19da0f240e
2 changed files with 69 additions and 1 deletions
@@ -3587,12 +3587,20 @@ func cleanToolSchema(schema any) any {
if key == "$schema" || key == "$id" || key == "$ref" ||
key == "$defs" || key == "definitions" ||
key == "additionalProperties" || key == "patternProperties" || key == "minLength" ||
key == "maxLength" || key == "minItems" || key == "maxItems" || key == "exclusiveMinimum" {
key == "maxLength" || key == "minItems" || key == "maxItems" || key == "exclusiveMinimum" ||
key == "deprecated" {
continue
}
// 递归清理嵌套对象
cleaned[key] = cleanToolSchema(value)
}
if enum, ok := cleaned["enum"].([]any); ok {
if normalized, ok := normalizeGeminiEnum(enum); ok {
cleaned["enum"] = normalized
} else {
delete(cleaned, "enum")
}
}
// 规范化 type 字段为大写
if typeVal, ok := cleaned["type"].(string); ok {
cleaned["type"] = strings.ToUpper(typeVal)
@@ -3627,6 +3635,28 @@ func cleanToolSchema(schema any) any {
}
}
func normalizeGeminiEnum(values []any) ([]any, bool) {
normalized := make([]any, len(values))
for i, value := range values {
if stringValue, ok := value.(string); ok {
normalized[i] = stringValue
continue
}
switch value.(type) {
case nil, bool, float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, json.Number:
encoded, err := json.Marshal(value)
if err != nil {
return nil, false
}
normalized[i] = string(encoded)
default:
return nil, false
}
}
return normalized, true
}
func incrementIntegralSchemaBound(value any) (any, bool) {
switch v := value.(type) {
case float64:
@@ -471,6 +471,44 @@ func TestCleanToolSchema_DropsAmbiguousExclusiveMinimumWithoutConversion(t *test
}
}
func TestCleanToolSchema_RemovesNestedDeprecatedAndNormalizesMixedScalarEnum(t *testing.T) {
schema := map[string]any{
"anyOf": []any{
map[string]any{
"type": "string",
"deprecated": true,
},
map[string]any{
"enum": []any{"enabled", false, float64(1), nil},
},
},
}
cleaned, ok := cleanToolSchema(schema).(map[string]any)
require.True(t, ok)
anyOf, ok := cleaned["anyOf"].([]any)
require.True(t, ok)
require.Len(t, anyOf, 2)
deprecatedSchema, ok := anyOf[0].(map[string]any)
require.True(t, ok)
require.NotContains(t, deprecatedSchema, "deprecated")
enumSchema, ok := anyOf[1].(map[string]any)
require.True(t, ok)
require.Equal(t, []any{"enabled", "false", "1", "null"}, enumSchema["enum"])
}
func TestCleanToolSchema_DropsEnumWithNonScalarValue(t *testing.T) {
schema := map[string]any{
"enum": []any{"valid", map[string]any{"invalid": true}},
}
cleaned, ok := cleanToolSchema(schema).(map[string]any)
require.True(t, ok)
require.NotContains(t, cleaned, "enum")
}
func TestConvertClaudeToolsToGeminiTools_PreservesWebSearchAlongsideFunctions(t *testing.T) {
tools := []any{
map[string]any{