diff --git a/pkg/providers/http_provider.go b/pkg/providers/http_provider.go
index 967d089d5..6a731ccc7 100644
--- a/pkg/providers/http_provider.go
+++ b/pkg/providers/http_provider.go
@@ -23,7 +23,14 @@ func NewHTTPProvider(apiKey, apiBase, proxy string) *HTTPProvider {
}
func (p *HTTPProvider) Chat(ctx context.Context, messages []Message, tools []ToolDefinition, model string, options map[string]interface{}) (*LLMResponse, error) {
- return p.delegate.Chat(ctx, messages, tools, model, options)
+ resp, err := p.delegate.Chat(ctx, messages, tools, model, options)
+ if err != nil {
+ return nil, err
+ }
+ // Strip provider-specific XML tool call artifacts (e.g. minimax)
+ // that leak into Content alongside structured tool_calls.
+ resp.Content = stripXMLToolCalls(resp.Content)
+ return resp, nil
}
func (p *HTTPProvider) GetDefaultModel() string {
diff --git a/pkg/providers/tool_call_extract.go b/pkg/providers/tool_call_extract.go
index 97a219283..7436cb908 100644
--- a/pkg/providers/tool_call_extract.go
+++ b/pkg/providers/tool_call_extract.go
@@ -56,6 +56,35 @@ func extractToolCallsFromText(text string) []ToolCall {
return result
}
+// stripXMLToolCalls removes XML tool call blocks (e.g. ...)
+// from response text. Some providers embed raw XML tool calls in Content alongside
+// structured tool_calls; this prevents them from leaking to users.
+func stripXMLToolCalls(text string) string {
+ // Match ... blocks (any namespace prefix)
+ idx := strings.Index(text, ":toolcall>")
+ if idx == -1 {
+ return text
+ }
+ // Find the opening tag start: scan backwards for '<'
+ tagStart := strings.LastIndex(text[:idx], "<")
+ if tagStart == -1 {
+ return text
+ }
+ // Extract namespace (e.g. "minimax" from "")
+ ns := text[tagStart+1 : idx]
+ closeTag := "" + ns + ":toolcall>"
+ closeIdx := strings.Index(text, closeTag)
+ if closeIdx == -1 {
+ return text
+ }
+ cleaned := text[:tagStart] + text[closeIdx+len(closeTag):]
+ // Recursively strip if there are more blocks
+ if strings.Contains(cleaned, ":toolcall>") {
+ cleaned = stripXMLToolCalls(cleaned)
+ }
+ return strings.TrimSpace(cleaned)
+}
+
// stripToolCallsFromText removes tool call JSON from response text.
func stripToolCallsFromText(text string) string {
start := strings.Index(text, `{"tool_calls"`)