diff --git a/internal/util/toolcalls_parse_markup.go b/internal/util/toolcalls_parse_markup.go index e2eff83..85e7138 100644 --- a/internal/util/toolcalls_parse_markup.go +++ b/internal/util/toolcalls_parse_markup.go @@ -15,6 +15,7 @@ var antmlArgumentPattern = regexp.MustCompile(`(?is)<(?:[a-z0-9_]+:)?argument\s+ var antmlParametersPattern = regexp.MustCompile(`(?is)<(?:[a-z0-9_]+:)?parameters\s*>\s*(\{.*?\})\s*`) var invokeCallPattern = regexp.MustCompile(`(?is)(.*?)`) var invokeParamPattern = regexp.MustCompile(`(?is)\s*(.*?)\s*`) +var toolUseFunctionPattern = regexp.MustCompile(`(?is)\s*(.*?)\s*`) func parseXMLToolCalls(text string) []ParsedToolCall { matches := xmlToolCallPattern.FindAllString(text, -1) @@ -38,6 +39,9 @@ func parseXMLToolCalls(text string) []ParsedToolCall { if call, ok := parseInvokeFunctionCallStyle(text); ok { return []ParsedToolCall{call} } + if call, ok := parseToolUseFunctionStyle(text); ok { + return []ParsedToolCall{call} + } return nil } @@ -229,6 +233,30 @@ func parseInvokeFunctionCallStyle(text string) (ParsedToolCall, bool) { return ParsedToolCall{Name: name, Input: input}, true } +func parseToolUseFunctionStyle(text string) (ParsedToolCall, bool) { + m := toolUseFunctionPattern.FindStringSubmatch(text) + if len(m) < 3 { + return ParsedToolCall{}, false + } + name := strings.TrimSpace(m[1]) + if name == "" { + return ParsedToolCall{}, false + } + body := m[2] + input := map[string]any{} + for _, pm := range invokeParamPattern.FindAllStringSubmatch(body, -1) { + if len(pm) < 3 { + continue + } + k := strings.TrimSpace(pm[1]) + v := strings.TrimSpace(pm[2]) + if k != "" { + input[k] = v + } + } + return ParsedToolCall{Name: name, Input: input}, true +} + func asString(v any) string { s, _ := v.(string) return s diff --git a/internal/util/toolcalls_test.go b/internal/util/toolcalls_test.go index 9701a46..d66519b 100644 --- a/internal/util/toolcalls_test.go +++ b/internal/util/toolcalls_test.go @@ -236,6 +236,20 @@ func TestParseToolCallsSupportsInvokeFunctionCallStyle(t *testing.T) { } } +func TestParseToolCallsSupportsToolUseFunctionParameterStyle(t *testing.T) { + text := `test` + calls := ParseToolCalls(text, []string{"search_web"}) + if len(calls) != 1 { + t.Fatalf("expected 1 call, got %#v", calls) + } + if calls[0].Name != "search_web" { + t.Fatalf("expected canonical tool name search_web, got %q", calls[0].Name) + } + if calls[0].Input["query"] != "test" { + t.Fatalf("expected query argument, got %#v", calls[0].Input) + } +} + func TestParseToolCallsSupportsNestedToolTagStyle(t *testing.T) { text := `pwdshow cwd` calls := ParseToolCalls(text, []string{"bash"})