diff --git a/src/llamafactory/data/tool_utils.py b/src/llamafactory/data/tool_utils.py
index 69a13c5745..bcec9bf45c 100644
--- a/src/llamafactory/data/tool_utils.py
+++ b/src/llamafactory/data/tool_utils.py
@@ -490,7 +490,7 @@ def tool_extractor(content: str) -> Union[str, list["FunctionCall"]]:
except json.JSONDecodeError:
continue
- return results
+ return results if results else content
class MiniMaxM2ToolUtils(ToolUtils):
@@ -548,7 +548,7 @@ def tool_extractor(content: str) -> Union[str, list["FunctionCall"]]:
results.append(FunctionCall(func_name.strip(), json.dumps(args_dict, ensure_ascii=False)))
- return results
+ return results if results else content
class MistralToolUtils(ToolUtils):
diff --git a/tests/data/test_tool_utils.py b/tests/data/test_tool_utils.py
new file mode 100644
index 0000000000..26ea0b58b7
--- /dev/null
+++ b/tests/data/test_tool_utils.py
@@ -0,0 +1,45 @@
+# Copyright 2025 the LlamaFactory team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from llamafactory.data.tool_utils import MiniMaxM1ToolUtils, MiniMaxM2ToolUtils
+
+
+def test_minimax_m1_parses_valid_tool_call():
+ content = '\n{"name": "get_weather", "arguments": {"city": "NYC"}}\n'
+ result = MiniMaxM1ToolUtils.tool_extractor(content)
+ assert isinstance(result, list) and len(result) == 1
+ assert result[0].name == "get_weather"
+
+
+def test_minimax_m1_returns_content_when_no_valid_tool_call():
+ # Wrapper tag is present but its body is not valid JSON, so nothing parses.
+ # The extractor should return the original content, not an empty list.
+ content = "\nthis is not json\n"
+ assert MiniMaxM1ToolUtils.tool_extractor(content) == content
+
+
+def test_minimax_m1_empty_wrapper_returns_content():
+ content = "\n\n"
+ assert MiniMaxM1ToolUtils.tool_extractor(content) == content
+
+
+def test_minimax_m2_returns_content_when_no_valid_tool_call():
+ # Wrapper tag is present but there is no well-formed block.
+ content = "\nno invoke block here\n"
+ assert MiniMaxM2ToolUtils.tool_extractor(content) == content
+
+
+def test_minimax_m2_empty_wrapper_returns_content():
+ content = "\n\n"
+ assert MiniMaxM2ToolUtils.tool_extractor(content) == content