Skip to content

Eval bug: Tool calling fails for Qwen3.6 XML format — peg-native parser doesn't support TAG_WITH_TAGGED #199

Description

@dgacias

Name and Version

~/repos/llama-cpp-turboquant feature/turboquant-kv-cache
❯ ./build-rocm/bin/llama-cli --version
version: 9907 (a33ef00)
built with GNU 16.1.1 for Linux x86_64

~/repos/llama-cpp-turboquant feature/turboquant-kv-cache
❯ ./build-rocm/bin/llama-server --version
version: 9907 (a33ef00)
built with GNU 16.1.1 for Linux x86_64

This is my compilation script:
rm -rf build-rocm
cmake -B build-rocm
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_C_FLAGS="-march=native -O3"
-DCMAKE_CXX_FLAGS="-march=native -O3"
-DGGML_LTO=ON
-DGGML_HIP=ON
-DCMAKE_HIP_COMPILER=/opt/rocm/llvm/bin/clang++
-DCMAKE_HIP_ARCHITECTURES=gfx1201
-DGGML_HIP_ROCWMMA_FATTN=ON
-DGGML_HIP_GRAPHS=ON

cmake --build build-rocm -j"$(nproc)"

Operating systems

Linux

GGML backends

HIP

Hardware

  • GPU: AMD RX 9070 XT (gfx1201, RDNA4), ROCm build
  • Branch: feature/turboquant-kv-cache
  • Client: Pi coding agent (OpenAI-compatible endpoint)
  • Flags include --jinja and --chat-template-file with the official Qwen3.6 template

Models

https://huggingface.co/ggufbench/Qwen3.6-27B-4bpw-16GB-VRAM

Problem description & steps to reproduce

Tool calls from Qwen3.6 models in the XML format (like <tool_call><function=name><parameter=key>value</parameter></function></tool_call>) fail intermittently.

They are emitted as plain text in the assistant's content instead of being parsed as tool calls.

This happens most reliably when the model does multiple tool calls in a single turn, or when executing commands with many pipes/escapes.

The server log shows Chat format: peg-native on every request. This fork's peg-native parser does not support the TAG_WITH_TAGGED format that Qwen3.6 uses for tool calls. The Jinja template (--chat-template-file) correctly builds the prompt, but the response parser falls back to the generic peg-native, which fails to extract the XML-style tool calls in edge cases.

I have checked main llama.cpp and it parses this format correctly via its TAG_WITH_TAGGED auto-detection mechanism:

  • The auto-detection logic classifies Qwen3.6 as TAG_WITH_TAGGED when function/parameter names appear inside XML-like tags (rather than as JSON keys).
  • build_tool_parser_tag_tagged() generates the correct PEG parser for the <function=...> / <parameter=...> format.
  • The test test_example_qwen3_coder validates incremental parsing of exactly this XML format.
  • Relevant files upstream: chat-auto-parser*.cpp/h, chat-diff-analyzer.cpp.

Thank you!

First Bad Commit

No response

Relevant log output

Server starts correctly with the Qwen3.6 chat template, but the response parser still falls back to peg-native:

init: chat template, example_format: '<|im_start|>system
You are a helpful assistant<|im_end|>
<|im_start|>user
Hello<|im_end|>
<|im_start|>assistant
...'
srv  init: init: chat template, thinking = 1
srv  llama_server: model loaded
srv  llama_server: server is listening on http://127.0.0.1:8089

On every request that involves tool calls, the parser reports:

srv  params_from_: Chat format: peg-native
srv  params_from_: Chat format: peg-native
srv  params_from_: Chat format: peg-native

The model emits the tool call in the correct Qwen XML format, but it arrives as plain text in the assistant content instead of being parsed as a structured tool call. Example of what the model produces (and which peg-native fails to extract when multiple calls appear in one turn):

<tool_call>
<function=bash>
<parameter=command>
grep -i "installed\|upgraded" /var/log/pacman.log | tail -30
</parameter>
</function>
</tool_call>
<tool_call>
<function=bash>
<parameter=command>
stat ~/.config/kxkbrc 2>/dev/null
</parameter>
</function>
</tool_call>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions