I've ocurred to this bug while trying out opencode. When using models like Qwen-Coder, tool calls often output arguments using Python literal syntax instead of strict JSON. This causes the HermesToolParser to fail or incorrectly return arguments as raw strings because json.loads does not support single quotes or capitalized booleans (True/False). Parser's own behaviour is actually correct but the model tends to use single quotes instead of double quotes. We can add a recovery mechanism to handle these malformed inputs.
Example script to test parser's behaviour with single quotes:
Output:
============================================================
Test: Python List Syntax
============================================================
Input:
<tool_call>
<function=search>
<parameter=query>['python', 'tutorial']</parameter>
</function>
</tool_call>
✅ Parsed tool call: search
Arguments:
query: "['python', 'tutorial']" (str)
⚠️ Should be list, not string!
============================================================
Test: Python Dict Syntax
============================================================
Input:
<tool_call>
<function=config>
<parameter=settings>{'theme': 'dark', 'size': 14}</parameter>
</function>
</tool_call>
✅ Parsed tool call: config
Arguments:
settings: "{'theme': 'dark', 'size': 14}" (str)
⚠️ Should be dict, not string!
============================================================
Test: Python Bool Syntax
============================================================
Input:
<tool_call>
<function=toggle>
<parameter=enabled>True</parameter>
</function>
</tool_call>
✅ Parsed tool call: toggle
Arguments:
enabled: 'True' (str)
⚠️ Should be bool, not string!
============================================================
Test: JSON Syntax (control)
============================================================
Input:
<tool_call>
<function=search>
<parameter=query>["python", "tutorial"]</parameter>
</function>
</tool_call>
✅ Parsed tool call: search
Arguments:
query: ['python', 'tutorial'] (list)
Code:
#!/usr/bin/env python3
from vllm_mlx.tool_parsers.hermes_tool_parser import HermesToolParser
import json
def test(name, xml_input):
print(f"\n{'='*60}")
print(f"Test: {name}")
print(f"{'='*60}")
print(f"Input:\n{xml_input}\n")
parser = HermesToolParser()
result = parser.extract_tool_calls(xml_input)
if result.tool_calls:
tc = result.tool_calls[0]
args = json.loads(tc['arguments'])
print(f"✅ Parsed tool call: {tc['name']}")
print(f"Arguments:")
for key, val in args.items():
print(f" {key}: {repr(val)} ({type(val).__name__})")
# Check if it's wrongly a string
if isinstance(val, str) and (
(val.startswith('[') and val.endswith(']')) or
(val.startswith('{') and val.endswith('}')) or
val in ['True', 'False']
):
print(f" ⚠️ Should be {val[0] == '[' and 'list' or val[0] == '{' and 'dict' or 'bool'}, not string!")
else:
print("❌ No tool calls found")
# Test 1: Python list (the bug)
test("Python List Syntax", """<tool_call>
<function=search>
<parameter=query>['python', 'tutorial']</parameter>
</function>
</tool_call>""")
# Test 2: Python dict (the bug)
test("Python Dict Syntax", """<tool_call>
<function=config>
<parameter=settings>{'theme': 'dark', 'size': 14}</parameter>
</function>
</tool_call>""")
# Test 3: Python bool (the bug)
test("Python Bool Syntax", """<tool_call>
<function=toggle>
<parameter=enabled>True</parameter>
</function>
</tool_call>""")
# Test 4: JSON (works fine)
test("JSON Syntax (control)", """<tool_call>
<function=search>
<parameter=query>["python", "tutorial"]</parameter>
</function>
</tool_call>""")
I've ocurred to this bug while trying out opencode. When using models like Qwen-Coder, tool calls often output arguments using Python literal syntax instead of strict JSON. This causes the
HermesToolParserto fail or incorrectly return arguments as raw strings because json.loads does not support single quotes or capitalized booleans (True/False). Parser's own behaviour is actually correct but the model tends to use single quotes instead of double quotes. We can add a recovery mechanism to handle these malformed inputs.Example script to test parser's behaviour with single quotes:
Output:
Code: