From a251294c8f9fde3d1f07a0c19032f59f972b20fa Mon Sep 17 00:00:00 2001 From: Yufeng He <40085740+he-yufeng@users.noreply.github.com> Date: Tue, 16 Jun 2026 18:42:09 +0800 Subject: [PATCH] fix(data): wrap MiniMax-M2 tool calls in minimax:tool_call tags --- src/llamafactory/data/tool_utils.py | 2 +- tests/data/test_formatter.py | 48 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/llamafactory/data/tool_utils.py b/src/llamafactory/data/tool_utils.py index 69a13c5745..970352838a 100644 --- a/src/llamafactory/data/tool_utils.py +++ b/src/llamafactory/data/tool_utils.py @@ -521,7 +521,7 @@ def function_formatter(functions: list["FunctionCall"]) -> str: prompt += "\n" function_texts.append(prompt) - return "\n".join(function_texts) + return "\n" + "\n".join(function_texts) + "\n" @override @staticmethod diff --git a/tests/data/test_formatter.py b/tests/data/test_formatter.py index 3aaa6f991d..05396c3651 100644 --- a/tests/data/test_formatter.py +++ b/tests/data/test_formatter.py @@ -294,6 +294,54 @@ def test_qwen_multi_tool_extractor(): ] +@pytest.mark.runs_on(["cpu", "mps"]) +def test_minimax2_function_formatter(): + formatter = FunctionFormatter(slots=["{{content}}"], tool_format="minimax2") + tool_calls = json.dumps(FUNCTION) + assert formatter.apply(content=tool_calls) == [ + "\n" + '\n' + 'bar\n' + '10\n' + "\n" + "" + ] + + +@pytest.mark.runs_on(["cpu", "mps"]) +def test_minimax2_multi_function_formatter(): + formatter = FunctionFormatter(slots=["{{content}}"], tool_format="minimax2") + tool_calls = json.dumps([FUNCTION] * 2) + invoke = '\nbar\n10\n' + assert formatter.apply(content=tool_calls) == [f"\n{invoke}\n{invoke}\n"] + + +@pytest.mark.runs_on(["cpu", "mps"]) +def test_minimax2_tool_extractor(): + formatter = ToolFormatter(tool_format="minimax2") + result = ( + "\n" + '\n' + 'bar\n' + '10\n' + "\n" + "" + ) + assert formatter.extract(result) == [("test_tool", """{"foo": "bar", "size": 10}""")] + + +@pytest.mark.runs_on(["cpu", "mps"]) +def test_minimax2_tool_round_trip(): + formatter = FunctionFormatter(slots=["{{content}}"], tool_format="minimax2") + tool_formatter = ToolFormatter(tool_format="minimax2") + original = {"name": "my_func", "arguments": {"arg1": "hello", "arg2": 42, "arg3": True}} + formatted = formatter.apply(content=json.dumps(original)) + extracted = tool_formatter.extract(formatted[0]) + assert len(extracted) == 1 + assert extracted[0][0] == original["name"] + assert json.loads(extracted[0][1]) == original["arguments"] + + @pytest.mark.runs_on(["cpu", "mps"]) def test_lfm2_function_formatter(): formatter = FunctionFormatter(slots=["{{content}}<|im_end|>\n"], tool_format="lfm2")