fix: handle mismatched XML tool call close tags from MiniMax
MiniMax returns <minimax:toolcall> but closes with </minimax:tool_call> (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 <noreply@anthropic.com>
This commit is contained in:
parent
db8bc53265
commit
62cb5c1d15
2 changed files with 47 additions and 0 deletions
|
|
@ -1058,6 +1058,43 @@ Done.`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExtractXMLToolCalls_MismatchedCloseTag(t *testing.T) {
|
||||||
|
// MiniMax uses <minimax:toolcall> but closes with </minimax:tool_call> (underscore)
|
||||||
|
text := `<minimax:toolcall>
|
||||||
|
<invoke name="readfile">
|
||||||
|
<parameter name="path">/home/user/project/pyproject.toml</parameter>
|
||||||
|
</invoke>
|
||||||
|
</minimax:tool_call>`
|
||||||
|
|
||||||
|
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 := `今テスト走らせるね。
|
||||||
|
<minimax:toolcall>
|
||||||
|
<invoke name="exec">
|
||||||
|
<parameter name="command">cd /home/user && pytest</parameter>
|
||||||
|
</invoke>
|
||||||
|
</minimax:tool_call>`
|
||||||
|
|
||||||
|
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) {
|
func TestStripXMLToolCalls_NoXML(t *testing.T) {
|
||||||
text := "Just regular text."
|
text := "Just regular text."
|
||||||
got := stripXMLToolCalls(text)
|
got := stripXMLToolCalls(text)
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,11 @@ func extractXMLToolCalls(text string) []ToolCall {
|
||||||
ns := remaining[tagStart+1 : idx]
|
ns := remaining[tagStart+1 : idx]
|
||||||
closeTag := "</" + ns + ":toolcall>"
|
closeTag := "</" + ns + ":toolcall>"
|
||||||
closeIdx := strings.Index(remaining, closeTag)
|
closeIdx := strings.Index(remaining, closeTag)
|
||||||
|
// Fallback: some models use inconsistent close tags (e.g. tool_call vs toolcall)
|
||||||
|
if closeIdx == -1 {
|
||||||
|
closeTag = "</" + ns + ":tool_call>"
|
||||||
|
closeIdx = strings.Index(remaining, closeTag)
|
||||||
|
}
|
||||||
if closeIdx == -1 {
|
if closeIdx == -1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +196,11 @@ func stripXMLToolCalls(text string) string {
|
||||||
ns := text[tagStart+1 : idx]
|
ns := text[tagStart+1 : idx]
|
||||||
closeTag := "</" + ns + ":toolcall>"
|
closeTag := "</" + ns + ":toolcall>"
|
||||||
closeIdx := strings.Index(text, closeTag)
|
closeIdx := strings.Index(text, closeTag)
|
||||||
|
// Fallback: some models use inconsistent close tags (e.g. tool_call vs toolcall)
|
||||||
|
if closeIdx == -1 {
|
||||||
|
closeTag = "</" + ns + ":tool_call>"
|
||||||
|
closeIdx = strings.Index(text, closeTag)
|
||||||
|
}
|
||||||
if closeIdx == -1 {
|
if closeIdx == -1 {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue