Skip to content

Qwen3-Coder tool call outputs can't be parsed when single quotes are used #63

Description

@selimrecep

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>""")

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions