mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-30 08:22:34 +00:00
fix(antigravity): preserve string const constraints in tool schemas
This commit is contained in:
@@ -119,6 +119,27 @@ func cleanJSONSchemaRecursive(value any) any {
|
||||
// 0. [NEW] 合并 allOf
|
||||
mergeAllOf(schemaMap)
|
||||
|
||||
// Gemini's enum representation supports strings. Preserve string constants
|
||||
// before the allowlist removes const, including schemas with no explicit type.
|
||||
if constant, ok := schemaMap["const"].(string); ok {
|
||||
values := []any{constant}
|
||||
if existing, ok := schemaMap["enum"].([]any); ok {
|
||||
// Both constraints apply: retain their intersection, not a wider enum.
|
||||
values = []any{}
|
||||
for _, value := range existing {
|
||||
if text, ok := value.(string); ok && text == constant {
|
||||
values = append(values, constant)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
schemaMap["enum"] = values
|
||||
if _, exists := schemaMap["type"]; !exists {
|
||||
schemaMap["type"] = "string"
|
||||
}
|
||||
delete(schemaMap, "const")
|
||||
}
|
||||
|
||||
// 1. [CRITICAL] 深度递归处理子项
|
||||
if props, ok := schemaMap["properties"].(map[string]any); ok {
|
||||
for _, v := range props {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package antigravity
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBuildToolsPreservesStringConst(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
schema string
|
||||
want string
|
||||
}{
|
||||
{"typed", `{"type":"string","const":"browser"}`, `{"type":"string","enum":["browser"]}`},
|
||||
{"inferred", `{"const":"browser"}`, `{"type":"string","enum":["browser"]}`},
|
||||
{"empty", `{"type":"string","const":""}`, `{"type":"string","enum":[""]}`},
|
||||
{"existing enum", `{"type":"string","const":"browser","enum":["browser","shell"]}`, `{"type":"string","enum":["browser"]}`},
|
||||
{"conflicting enum", `{"type":"string","const":"browser","enum":["shell"]}`, `{"type":"string","enum":[]}`},
|
||||
{"ordinary enum", `{"type":"string","enum":["browser","shell"]}`, `{"type":"string","enum":["browser","shell"]}`},
|
||||
{"array items", `{"type":"array","items":{"type":"string","const":"browser"}}`, `{"type":"array","items":{"type":"string","enum":["browser"]}}`},
|
||||
{"nested property named const", `{"type":"object","properties":{"const":{"const":"browser"}}}`, `{"type":"object","properties":{"const":{"type":"string","enum":["browser"]}}}`},
|
||||
{"schema metadata", `{"type":"string","const":"browser","$schema":"https://json-schema.org/draft/2020-12/schema","description":"Tool kind"}`, `{"type":"string","enum":["browser"],"description":"Tool kind"}`},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var property map[string]any
|
||||
require.NoError(t, json.Unmarshal([]byte(tc.schema), &property))
|
||||
tools := buildTools([]ClaudeTool{{
|
||||
Name: "dispatch",
|
||||
InputSchema: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{"action": property},
|
||||
},
|
||||
}})
|
||||
require.Len(t, tools, 1)
|
||||
require.Len(t, tools[0].FunctionDeclarations, 1)
|
||||
properties := tools[0].FunctionDeclarations[0].Parameters["properties"].(map[string]any)
|
||||
got, err := json.Marshal(properties["action"])
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, tc.want, string(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user