fix: expand shared tool schema extraction

This commit is contained in:
shern-point
2026-04-29 01:59:05 +08:00
parent fa3e6d040d
commit a550de30af
3 changed files with 78 additions and 19 deletions

View File

@@ -30,13 +30,7 @@ func injectToolPrompt(messages []map[string]any, tools []any, policy ToolChoiceP
if !ok {
continue
}
fn, _ := tool["function"].(map[string]any)
if len(fn) == 0 {
fn = tool
}
name, _ := fn["name"].(string)
desc, _ := fn["description"].(string)
schema, _ := fn["parameters"].(map[string]any)
name, desc, schema := toolcall.ExtractToolMeta(tool)
name = strings.TrimSpace(name)
if !isAllowed(name) {
continue

View File

@@ -48,7 +48,7 @@ func buildToolSchemaIndex(toolsRaw any) map[string]any {
if !ok {
continue
}
name, schema := extractToolNameAndSchema(tool)
name, _, schema := ExtractToolMeta(tool)
if name == "" || schema == nil {
continue
}
@@ -60,24 +60,31 @@ func buildToolSchemaIndex(toolsRaw any) map[string]any {
return out
}
func extractToolNameAndSchema(tool map[string]any) (string, any) {
func ExtractToolMeta(tool map[string]any) (string, string, any) {
name := strings.TrimSpace(asStringValue(tool["name"]))
schema := tool["parameters"]
if schema == nil {
schema = tool["input_schema"]
}
desc := strings.TrimSpace(asStringValue(tool["description"]))
schema := firstNonNil(
tool["parameters"],
tool["input_schema"],
tool["inputSchema"],
tool["schema"],
)
if fn, ok := tool["function"].(map[string]any); ok {
if name == "" {
name = strings.TrimSpace(asStringValue(fn["name"]))
}
if schema == nil {
schema = fn["parameters"]
}
if schema == nil {
schema = fn["input_schema"]
if desc == "" {
desc = strings.TrimSpace(asStringValue(fn["description"]))
}
schema = firstNonNil(
schema,
fn["parameters"],
fn["input_schema"],
fn["inputSchema"],
fn["schema"],
)
}
return name, schema
return name, desc, schema
}
func normalizeToolValueWithSchema(value any, schema any) (any, bool) {
@@ -264,3 +271,12 @@ func asStringValue(v any) string {
}
return ""
}
func firstNonNil(values ...any) any {
for _, value := range values {
if value != nil {
return value
}
}
return nil
}

View File

@@ -110,3 +110,52 @@ func TestNormalizeParsedToolCallsForSchemasLeavesAmbiguousUnionUnchanged(t *test
t.Fatalf("expected ambiguous union to stay unchanged, got %#v", got[0].Input["taskId"])
}
}
func TestNormalizeParsedToolCallsForSchemasSupportsCamelCaseInputSchema(t *testing.T) {
toolsRaw := []any{
map[string]any{
"name": "Write",
"inputSchema": map[string]any{
"type": "object",
"properties": map[string]any{
"content": map[string]any{"type": "string"},
},
},
},
}
calls := []ParsedToolCall{{Name: "Write", Input: map[string]any{"content": map[string]any{"message": "hi"}}}}
got := NormalizeParsedToolCallsForSchemas(calls, toolsRaw)
if got[0].Input["content"] != `{"message":"hi"}` {
t.Fatalf("expected camelCase inputSchema content coercion, got %#v", got[0].Input["content"])
}
}
func TestNormalizeParsedToolCallsForSchemasPreservesArrayWhenSchemaSaysArray(t *testing.T) {
toolsRaw := []any{
map[string]any{
"name": "todowrite",
"inputSchema": map[string]any{
"type": "object",
"properties": map[string]any{
"todos": map[string]any{
"type": "array",
"items": map[string]any{
"type": "object",
"properties": map[string]any{
"content": map[string]any{"type": "string"},
"status": map[string]any{"type": "string"},
"priority": map[string]any{"type": "string"},
},
},
},
},
},
},
}
todos := []any{map[string]any{"content": "x", "status": "pending", "priority": "high"}}
calls := []ParsedToolCall{{Name: "todowrite", Input: map[string]any{"todos": todos}}}
got := NormalizeParsedToolCallsForSchemas(calls, toolsRaw)
if !reflect.DeepEqual(got[0].Input["todos"], todos) {
t.Fatalf("expected todos array preserved, got %#v want %#v", got[0].Input["todos"], todos)
}
}