Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions tests/test_reasoning_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def test_list_parsers_includes_builtin(self):
assert "qwen3" in parsers
assert "deepseek_r1" in parsers
assert "gemma4" in parsers
assert "glm4" in parsers

def test_get_parser_qwen3(self):
"""Should be able to get Qwen3 parser."""
Expand All @@ -43,6 +44,12 @@ def test_get_parser_deepseek(self):
parser = parser_cls()
assert isinstance(parser, ReasoningParser)

def test_get_parser_glm4(self):
"""Should be able to get GLM4 parser."""
parser_cls = get_parser("glm4")
parser = parser_cls()
assert isinstance(parser, ReasoningParser)

def test_get_unknown_parser_raises(self):
"""Unknown parser name should raise KeyError."""
with pytest.raises(KeyError) as exc_info:
Expand Down Expand Up @@ -962,8 +969,7 @@ def test_streaming_constrain_format(self, parser):
def test_constrain_tokens_stripped(self, parser):
"""<|constrain|> should not leak into output."""
output = (
"<|channel|>final <|constrain|>JSON<|message|>"
'{"hello":"world"}<|return|>'
'<|channel|>final <|constrain|>JSON<|message|>{"hello":"world"}<|return|>'
)
reasoning, content = parser.extract_reasoning(output)
assert "<|constrain|>" not in (content or "")
Expand Down
61 changes: 58 additions & 3 deletions tests/test_tool_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
DeepSeekToolParser,
FunctionaryToolParser,
Gemma4ToolParser,
Glm47ToolParser,
GraniteToolParser,
HermesToolParser,
KimiToolParser,
Expand Down Expand Up @@ -857,9 +858,7 @@ def test_qwen3_coder_multiline_parameter(self):
def test_bare_function_without_tool_call_wrapper(self):
"""Test bare <function=...> blocks without <tool_call> wrapper."""
parser = HermesToolParser()
text = (
"<function=get_weather>" "<parameter=city>Berlin</parameter>" "</function>"
)
text = "<function=get_weather><parameter=city>Berlin</parameter></function>"
result = parser.extract_tool_calls(text)

assert result.tools_called
Expand Down Expand Up @@ -1403,3 +1402,59 @@ def test_streaming_multiple_function_blocks(self, parser):
assert len(emitted_calls) == 2
assert emitted_calls[0]["function"]["name"] == "func1"
assert emitted_calls[1]["function"]["name"] == "func2"


class TestGLM47ToolParser:
"""Tests for GLM47 tool parser."""

def test_zero_arguments_tool_call(self):
"""Test Fix 2: Handle zero-argument tool calls without crashing."""
parser = Glm47ToolParser()

output = "<tool_call>get_current_time</tool_call>"

result = parser.extract_tool_calls(output)

assert result.tools_called is True
assert len(result.tool_calls) == 1
assert result.tool_calls[0]["name"] == "get_current_time"
args = json.loads(result.tool_calls[0]["arguments"])
assert args == {}

def test_with_arguments(self):
"""Test tool call with arguments."""
parser = Glm47ToolParser()

output = "<tool_call>search\n<arg_key>query</arg_key><arg_value>Python</arg_value></tool_call>"

result = parser.extract_tool_calls(output)

assert result.tools_called is True
assert len(result.tool_calls) == 1
assert result.tool_calls[0]["name"] == "search"
args = json.loads(result.tool_calls[0]["arguments"])
assert args["query"] == "Python"

def test_streaming_zero_args(self):
"""Test Fix 2: Streaming with zero-argument tool call."""
parser = Glm47ToolParser()

chunks = ["<tool_call>", "get_status", "</tool_call>"]
accumulated = ""
tool_calls_found = False

for chunk in chunks:
prev = accumulated
accumulated += chunk
r = parser.extract_tool_calls_streaming(
previous_text=prev,
current_text=accumulated,
delta_text=chunk,
)
if r is not None and "tool_calls" in r:
tool_calls_found = True
assert r["tool_calls"][0]["function"]["name"] == "get_status"
args = json.loads(r["tool_calls"][0]["function"]["arguments"])
assert args == {}

assert tool_calls_found, "Zero-argument tool call should have been detected"
Loading