mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 12:57:57 +08:00
fix(gemini): normalize exclusive minimum tool schemas
This commit is contained in:
@@ -3542,7 +3542,7 @@ 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 == "maxLength" || key == "minItems" || key == "maxItems" || key == "exclusiveMinimum" {
|
||||
continue
|
||||
}
|
||||
// 递归清理嵌套对象
|
||||
@@ -3563,6 +3563,13 @@ func cleanToolSchema(schema any) any {
|
||||
delete(cleaned, "type")
|
||||
}
|
||||
}
|
||||
if cleaned["type"] == "INTEGER" {
|
||||
if minimum, ok := incrementIntegralSchemaBound(v["exclusiveMinimum"]); ok {
|
||||
if existing, exists := cleaned["minimum"]; !exists || schemaNumberLess(existing, minimum) {
|
||||
cleaned["minimum"] = minimum
|
||||
}
|
||||
}
|
||||
}
|
||||
return cleaned
|
||||
case []any:
|
||||
cleaned := make([]any, len(v))
|
||||
@@ -3575,6 +3582,56 @@ func cleanToolSchema(schema any) any {
|
||||
}
|
||||
}
|
||||
|
||||
func incrementIntegralSchemaBound(value any) (any, bool) {
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) || v != math.Trunc(v) || v+1 <= v {
|
||||
return nil, false
|
||||
}
|
||||
return v + 1, true
|
||||
case int:
|
||||
if v == math.MaxInt {
|
||||
return nil, false
|
||||
}
|
||||
return v + 1, true
|
||||
case int64:
|
||||
if v == math.MaxInt64 {
|
||||
return nil, false
|
||||
}
|
||||
return v + 1, true
|
||||
case json.Number:
|
||||
i, err := v.Int64()
|
||||
if err != nil || i == math.MaxInt64 {
|
||||
return nil, false
|
||||
}
|
||||
return json.Number(fmt.Sprintf("%d", i+1)), true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func schemaNumberLess(left, right any) bool {
|
||||
leftNumber, leftOK := schemaNumberFloat64(left)
|
||||
rightNumber, rightOK := schemaNumberFloat64(right)
|
||||
return leftOK && rightOK && leftNumber < rightNumber
|
||||
}
|
||||
|
||||
func schemaNumberFloat64(value any) (float64, bool) {
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
return v, !math.IsNaN(v) && !math.IsInf(v, 0)
|
||||
case int:
|
||||
return float64(v), true
|
||||
case int64:
|
||||
return float64(v), true
|
||||
case json.Number:
|
||||
n, err := v.Float64()
|
||||
return n, err == nil && !math.IsInf(n, 0)
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func convertClaudeGenerationConfig(req map[string]any) map[string]any {
|
||||
out := make(map[string]any)
|
||||
if mt, ok := asInt(req["max_tokens"]); ok && mt > 0 {
|
||||
|
||||
@@ -405,6 +405,64 @@ func TestCleanToolSchema_NormalizesGeminiUnsupportedSchemaFields(t *testing.T) {
|
||||
require.NotContains(t, emptySchema, "type")
|
||||
}
|
||||
|
||||
func TestCleanToolSchema_ConvertsNestedIntegerExclusiveMinimum(t *testing.T) {
|
||||
schema := map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"counts": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": float64(0),
|
||||
},
|
||||
},
|
||||
"strict": map[string]any{
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"minimum": 5,
|
||||
},
|
||||
"weak": map[string]any{
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 2,
|
||||
"minimum": 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cleaned := cleanToolSchema(schema).(map[string]any)
|
||||
properties := cleaned["properties"].(map[string]any)
|
||||
items := properties["counts"].(map[string]any)["items"].(map[string]any)
|
||||
require.NotContains(t, items, "exclusiveMinimum")
|
||||
require.Equal(t, float64(1), items["minimum"])
|
||||
|
||||
strict := properties["strict"].(map[string]any)
|
||||
require.NotContains(t, strict, "exclusiveMinimum")
|
||||
require.Equal(t, 5, strict["minimum"])
|
||||
|
||||
weak := properties["weak"].(map[string]any)
|
||||
require.NotContains(t, weak, "exclusiveMinimum")
|
||||
require.Equal(t, 3, weak["minimum"])
|
||||
}
|
||||
|
||||
func TestCleanToolSchema_DropsAmbiguousExclusiveMinimumWithoutConversion(t *testing.T) {
|
||||
for name, schema := range map[string]map[string]any{
|
||||
"number schema": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0,
|
||||
},
|
||||
"fractional integer bound": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0.5,
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
cleaned := cleanToolSchema(schema).(map[string]any)
|
||||
require.NotContains(t, cleaned, "exclusiveMinimum")
|
||||
require.NotContains(t, cleaned, "minimum")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertClaudeToolsToGeminiTools_PreservesWebSearchAlongsideFunctions(t *testing.T) {
|
||||
tools := []any{
|
||||
map[string]any{
|
||||
|
||||
Reference in New Issue
Block a user