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