diff --git a/gr2gw.go b/gr2gw.go
index b757651..67ed18e 100644
--- a/gr2gw.go
+++ b/gr2gw.go
@@ -658,7 +658,10 @@ type ToolTagPair struct {
var ToolTagPairs = []ToolTagPair{
{Start: "", End: ""},
{Start: "", End: ""},
+ {Start: "", End: ""},
+ {Start: "", End: ""},
{Start: "", End: ""},
+ {Start: "", End: ""},
{Start: "[TOOL_CALLS]", End: "[/TOOL_CALLS]"},
}
@@ -903,6 +906,36 @@ func parseXMLToolCall(block string) ([]ToolCall, bool) {
}
}
+ if argsStr != "" && !json.Valid([]byte(argsStr)) {
+ reTagOpen := regexp.MustCompile(`<([a-zA-Z0-9_-]+)>`)
+ openMatches := reTagOpen.FindAllStringSubmatchIndex(argsStr, -1)
+ if len(openMatches) > 0 {
+ xmlMap := make(map[string]interface{})
+ for _, match := range openMatches {
+ tagName := argsStr[match[2]:match[3]]
+ closeTag := "" + tagName + ">"
+ closeIdx := strings.Index(argsStr[match[1]:], closeTag)
+ if closeIdx != -1 {
+ v := strings.TrimSpace(argsStr[match[1] : match[1]+closeIdx])
+ if num, err := strconv.ParseFloat(v, 64); err == nil && !strings.HasPrefix(v, "0") {
+ xmlMap[tagName] = num
+ } else if v == "true" {
+ xmlMap[tagName] = true
+ } else if v == "false" {
+ xmlMap[tagName] = false
+ } else {
+ xmlMap[tagName] = v
+ }
+ }
+ }
+ if len(xmlMap) > 0 {
+ if b, err := json.Marshal(xmlMap); err == nil {
+ argsStr = string(b)
+ }
+ }
+ }
+ }
+
if fnName != "" {
if argsStr == "" {
argsStr = "{}"
@@ -976,6 +1009,12 @@ func DetectToolCalls(content string) ([]ToolCall, string, bool) {
}
if len(calls) > 0 {
+ remaining = strings.TrimSpace(remaining)
+ if remaining == "```xml" || remaining == "```" || remaining == "```json" {
+ remaining = ""
+ }
+ reEmptyFence := regexp.MustCompile("(?m)^```(?:xml|json)?\\s*\\n?\\s*```\\s*$")
+ remaining = strings.TrimSpace(reEmptyFence.ReplaceAllString(remaining, ""))
return calls, remaining, true
}
diff --git a/gr2gw_test.go b/gr2gw_test.go
index b48b4cc..0a21dc8 100644
--- a/gr2gw_test.go
+++ b/gr2gw_test.go
@@ -670,6 +670,22 @@ Some postamble.`
if rem4 != "" {
t.Errorf("expected empty remaining, got %q", rem4)
}
+
+ // 5. XML toolCall with child tags
+ xmlChildTags := "```xml\n\n get_weather\n \n Paris\n \n\n```"
+ calls5, rem5, ok5 := DetectToolCalls(xmlChildTags)
+ if !ok5 || len(calls5) != 1 {
+ t.Fatalf("expected 1 call from xmlChildTags, got %d", len(calls5))
+ }
+ if calls5[0].Function.Name != "get_weather" {
+ t.Errorf("expected get_weather, got %q", calls5[0].Function.Name)
+ }
+ if !strings.Contains(calls5[0].Function.Arguments, `"Paris"`) {
+ t.Errorf("expected Paris in arguments, got %s", calls5[0].Function.Arguments)
+ }
+ if rem5 != "" {
+ t.Errorf("expected empty remaining, got %q", rem5)
+ }
}
func TestUniversalStreamToolCallFilterVariants(t *testing.T) {