fix: MiniMax tool call normalization and thinking block handling (#458)
Co-authored-by: 0xallam <ahmed39652003@gmail.com>
This commit is contained in:
+20
-3
@@ -1,4 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
import re
|
||||||
from collections.abc import AsyncIterator
|
from collections.abc import AsyncIterator
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -25,6 +26,19 @@ from strix.utils.resource_paths import get_strix_resource_path
|
|||||||
litellm.drop_params = True
|
litellm.drop_params = True
|
||||||
litellm.modify_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):
|
class LLMRequestFailedError(Exception):
|
||||||
def __init__(self, message: str, details: str | None = None):
|
def __init__(self, message: str, details: str | None = None):
|
||||||
@@ -197,9 +211,10 @@ class LLM:
|
|||||||
delta = self._get_chunk_content(chunk)
|
delta = self._get_chunk_content(chunk)
|
||||||
if delta:
|
if delta:
|
||||||
accumulated += delta
|
accumulated += delta
|
||||||
if "</function>" in accumulated or "</invoke>" in accumulated:
|
check_content = _THINKING_BLOCK_OR_OPEN_RE.sub("", accumulated)
|
||||||
end_tag = "</function>" if "</function>" in accumulated else "</invoke>"
|
if "</function>" in check_content or "</invoke>" in check_content:
|
||||||
pos = accumulated.find(end_tag)
|
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)]
|
accumulated = accumulated[: pos + len(end_tag)]
|
||||||
yield LLMResponse(content=accumulated)
|
yield LLMResponse(content=accumulated)
|
||||||
done_streaming = 1
|
done_streaming = 1
|
||||||
@@ -209,8 +224,10 @@ class LLM:
|
|||||||
if chunks:
|
if chunks:
|
||||||
self._update_usage_stats(stream_chunk_builder(chunks))
|
self._update_usage_stats(stream_chunk_builder(chunks))
|
||||||
|
|
||||||
|
accumulated = _THINKING_BLOCK_RE.sub("", accumulated)
|
||||||
accumulated = normalize_tool_format(accumulated)
|
accumulated = normalize_tool_format(accumulated)
|
||||||
accumulated = fix_incomplete_tool_call(_truncate_to_first_function(accumulated))
|
accumulated = fix_incomplete_tool_call(_truncate_to_first_function(accumulated))
|
||||||
|
|
||||||
yield LLMResponse(
|
yield LLMResponse(
|
||||||
content=accumulated,
|
content=accumulated,
|
||||||
tool_invocations=parse_tool_invocations(accumulated),
|
tool_invocations=parse_tool_invocations(accumulated),
|
||||||
|
|||||||
+6
-1
@@ -20,7 +20,12 @@ def normalize_tool_format(content: str) -> str:
|
|||||||
<function="X"> → <function=X>
|
<function="X"> → <function=X>
|
||||||
<parameter="X"> → <parameter=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 = _FUNCTION_CALLS_TAG.sub("", content)
|
||||||
content = _INVOKE_OPEN.sub(r"<function=\1>", content)
|
content = _INVOKE_OPEN.sub(r"<function=\1>", content)
|
||||||
content = _PARAM_NAME_ATTR.sub(r"<parameter=\1>", content)
|
content = _PARAM_NAME_ATTR.sub(r"<parameter=\1>", content)
|
||||||
|
|||||||
Reference in New Issue
Block a user