From 62cb5c1d157f1d93105bdfb1cdab4609ea8e60d9 Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Sat, 21 Feb 2026 02:39:26 +0900 Subject: [PATCH] fix: handle mismatched XML tool call close tags from MiniMax MiniMax returns but closes with (underscore variant). The XML parser constructed the close tag from the opening tag and failed to match. Now tries both :toolcall> and :tool_call> variants as fallback. Co-Authored-By: Claude Opus 4.6 --- pkg/providers/claude_cli_provider_test.go | 37 +++++++++++++++++++++++ pkg/providers/tool_call_extract.go | 10 ++++++ 2 files changed, 47 insertions(+) diff --git a/pkg/providers/claude_cli_provider_test.go b/pkg/providers/claude_cli_provider_test.go index 4cf0797a5..9f237cb68 100644 --- a/pkg/providers/claude_cli_provider_test.go +++ b/pkg/providers/claude_cli_provider_test.go @@ -1058,6 +1058,43 @@ Done.` } } +func TestExtractXMLToolCalls_MismatchedCloseTag(t *testing.T) { + // MiniMax uses but closes with (underscore) + text := ` + +/home/user/project/pyproject.toml + +` + + calls := extractXMLToolCalls(text) + if len(calls) != 1 { + t.Fatalf("expected 1 tool call, got %d", len(calls)) + } + if calls[0].Name != "readfile" { + t.Errorf("Name = %q, want %q", calls[0].Name, "readfile") + } + if calls[0].Arguments["path"] != "/home/user/project/pyproject.toml" { + t.Errorf("Arguments[path] = %v, want pyproject.toml path", calls[0].Arguments["path"]) + } +} + +func TestStripXMLToolCalls_MismatchedCloseTag(t *testing.T) { + text := `今テスト走らせるね。 + + +cd /home/user && pytest + +` + + got := stripXMLToolCalls(text) + if strings.Contains(got, "toolcall") || strings.Contains(got, "tool_call") { + t.Errorf("should remove XML block, got %q", got) + } + if !strings.Contains(got, "今テスト走らせるね。") { + t.Errorf("should keep text before, got %q", got) + } +} + func TestStripXMLToolCalls_NoXML(t *testing.T) { text := "Just regular text." got := stripXMLToolCalls(text) diff --git a/pkg/providers/tool_call_extract.go b/pkg/providers/tool_call_extract.go index addfeecfc..93740c573 100644 --- a/pkg/providers/tool_call_extract.go +++ b/pkg/providers/tool_call_extract.go @@ -86,6 +86,11 @@ func extractXMLToolCalls(text string) []ToolCall { ns := remaining[tagStart+1 : idx] closeTag := "" closeIdx := strings.Index(remaining, closeTag) + // Fallback: some models use inconsistent close tags (e.g. tool_call vs toolcall) + if closeIdx == -1 { + closeTag = "" + closeIdx = strings.Index(remaining, closeTag) + } if closeIdx == -1 { break } @@ -191,6 +196,11 @@ func stripXMLToolCalls(text string) string { ns := text[tagStart+1 : idx] closeTag := "" closeIdx := strings.Index(text, closeTag) + // Fallback: some models use inconsistent close tags (e.g. tool_call vs toolcall) + if closeIdx == -1 { + closeTag = "" + closeIdx = strings.Index(text, closeTag) + } if closeIdx == -1 { return text }