fix: MiniMax tool call normalization and thinking block handling (#458)

Co-authored-by: 0xallam <ahmed39652003@gmail.com>
This commit is contained in:
Dr Alex Mitre
2026-05-03 17:12:37 -06:00
committed by GitHub
parent 6da7315aa3
commit a75ad2960e
2 changed files with 26 additions and 4 deletions
+20 -3
View File
@@ -1,4 +1,5 @@
import asyncio
import re
from collections.abc import AsyncIterator
from dataclasses import dataclass
from typing import Any
@@ -25,6 +26,19 @@ from strix.utils.resource_paths import get_strix_resource_path
litellm.drop_params = True
litellm.modify_params = True
_THINKING_BLOCK_RE = re.compile(r"<thinking[^>]*>.*?</thinking>", re.DOTALL)
_THINKING_BLOCK_OR_OPEN_RE = re.compile(r"<thinking[^>]*>.*?(?:</thinking>|\Z)", re.DOTALL)
def _find_end_tag_outside_thinking(content: str, end_tag: str) -> int:
thinking_spans = [(m.start(), m.end()) for m in _THINKING_BLOCK_OR_OPEN_RE.finditer(content)]
start = 0
while (idx := content.find(end_tag, start)) != -1:
if not any(s <= idx < e for s, e in thinking_spans):
return idx
start = idx + 1
return -1
class LLMRequestFailedError(Exception):
def __init__(self, message: str, details: str | None = None):
@@ -197,9 +211,10 @@ class LLM:
delta = self._get_chunk_content(chunk)
if delta:
accumulated += delta
if "</function>" in accumulated or "</invoke>" in accumulated:
end_tag = "</function>" if "</function>" in accumulated else "</invoke>"
pos = accumulated.find(end_tag)
check_content = _THINKING_BLOCK_OR_OPEN_RE.sub("", accumulated)
if "</function>" in check_content or "</invoke>" in check_content:
end_tag = "</function>" if "</function>" in check_content else "</invoke>"
pos = _find_end_tag_outside_thinking(accumulated, end_tag)
accumulated = accumulated[: pos + len(end_tag)]
yield LLMResponse(content=accumulated)
done_streaming = 1
@@ -209,8 +224,10 @@ class LLM:
if chunks:
self._update_usage_stats(stream_chunk_builder(chunks))
accumulated = _THINKING_BLOCK_RE.sub("", accumulated)
accumulated = normalize_tool_format(accumulated)
accumulated = fix_incomplete_tool_call(_truncate_to_first_function(accumulated))
yield LLMResponse(
content=accumulated,
tool_invocations=parse_tool_invocations(accumulated),
+6 -1
View File
@@ -20,7 +20,12 @@ def normalize_tool_format(content: str) -> str:
<function="X"> → <function=X>
<parameter="X"> → <parameter=X>
"""
if "<invoke" in content or "<function_calls" in content:
if (
"<invoke" in content
or "<function_calls" in content
or '<parameter name="' in content
or "<parameter name='" in content
):
content = _FUNCTION_CALLS_TAG.sub("", content)
content = _INVOKE_OPEN.sub(r"<function=\1>", content)
content = _PARAM_NAME_ATTR.sub(r"<parameter=\1>", content)