diff --git a/docs/toolcall-semantics.md b/docs/toolcall-semantics.md index 50165a9..b386762 100644 --- a/docs/toolcall-semantics.md +++ b/docs/toolcall-semantics.md @@ -19,7 +19,8 @@ This document defines the cross-runtime contract for `ParseToolCallsDetailed` / - first `{` to last `}` object slice. 3. Parse each candidate in order: - JSON payload parser (`tool_calls`, list, single call object), - - markup parser (``, ``, ``; supports attributes + nested fields). + - XML/Markup parser (``, ``, ``; supports attributes + nested fields), + - Text KV fallback parser (`function.name: ` ... `function.arguments: {json}`). 4. Stop at first candidate that yields at least one call. ## Name normalization policy diff --git a/internal/util/toolcalls_parse.go b/internal/util/toolcalls_parse.go index 53eac8e..d08ffe7 100644 --- a/internal/util/toolcalls_parse.go +++ b/internal/util/toolcalls_parse.go @@ -45,6 +45,9 @@ func ParseToolCallsDetailed(text string, availableToolNames []string) ToolCallPa if len(tc) == 0 { tc = parseMarkupToolCalls(candidate) } + if len(tc) == 0 { + tc = parseTextKVToolCalls(candidate) + } if len(tc) > 0 { parsed = tc result.SawToolCallSyntax = true @@ -54,7 +57,10 @@ func ParseToolCallsDetailed(text string, availableToolNames []string) ToolCallPa if len(parsed) == 0 { parsed = parseXMLToolCalls(text) if len(parsed) == 0 { - return result + parsed = parseTextKVToolCalls(text) + if len(parsed) == 0 { + return result + } } result.SawToolCallSyntax = true } @@ -93,6 +99,9 @@ func ParseStandaloneToolCallsDetailed(text string, availableToolNames []string) if len(parsed) == 0 { parsed = parseMarkupToolCalls(candidate) } + if len(parsed) == 0 { + parsed = parseTextKVToolCalls(candidate) + } if len(parsed) > 0 { result.SawToolCallSyntax = true calls, rejectedNames := filterToolCallsDetailed(parsed, availableToolNames) @@ -207,7 +216,8 @@ func looksLikeToolCallSyntax(text string) bool { return strings.Contains(lower, "tool_calls") || strings.Contains(lower, "