CanIToolCall caniuse.com for tool calling

Qwen3 (Hermes-style JSON tool calls) on ollama 7af39318

fail 78% strict pass 39 pass · 9 soft pass · 2 fail

Run

Engine
ollama 7af39318 (7af393188defd52d370464de0d2064649cab9b41)
Run
to
Platform
linux-x86_64, Python 3.12.3, canitoolcall 0.1.0.dev0
Strategies
one, special, token, rand:1:8, rand:2:8, rand:3:8, rand:4:8, rand:5:8 (normalization soft-v1); not counted for this engine: one, special, rand:1:8, rand:2:8, rand:3:8, rand:4:8, rand:5:8
Fixtures digest
a1e7b55367e195e8aa6e2ef746b2cc141b44f37caad0887ff8f30b104616f836 the fixture corpus used for this site matches the run
Engine details
{
  "go_version": "go1.26.0",
  "llama_cpp_build": "b11081",
  "llama_cpp_commit": "161755f",
  "pinned_version": "7af393188defd52d370464de0d2064649cab9b41",
  "runner": "llama-server token rendering (preserved_tokens) + model/parsers via server/routes.go flow",
  "tokens_per_step": "one"
}
Results file
data/ollama-7af39318.json

Checks

Per fixture, the worst result of each check over the non-streaming parse and every realistic chunking strategy.
CheckPassSoft FailErrorStrict pass rate
expected_match 33 9 1 0 77%
expected_error 6 0 1 0 86%
stream_equals_nonstream 50 0 0 0 100%
no_leakage 50 0 0 0 100%
arguments_json 32 0 0 0 100%
arguments_schema 32 0 0 0 100%
parallel_order 7 0 0 0 100%

2 fixture(s) failed only a synthetic strategy: char:* (which can split special tokens), or a multi-token strategy this engine never produces because its server streams one token per event (one, special, rand:1:8, rand:2:8, rand:3:8, rand:4:8, rand:5:8). These are listed below but never count toward the status.

Fixtures needing attention

fail qwen3-hermes/marker-in-arguments expected_match
Checks that did not pass for qwen3-hermes/marker-in-arguments
CheckStrategyResultDetail
expected_matchnonstream failparser raised 'OllamaError: failed to parse JSON: unexpected end of JSON input'
expected_matchtoken failparser raised 'OllamaError: failed to parse JSON: unexpected end of JSON input'

Synthetic strategies (not counted): expected_match@one fail, expected_match@special fail, expected_match@rand:1:8 fail, expected_match@rand:2:8 fail, expected_match@rand:3:8 fail, expected_match@rand:4:8 fail, expected_match@rand:5:8 fail

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/edge.jsonl --id qwen3-hermes/marker-in-arguments --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 3 of fixtures/qwen3-hermes/edge.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, one, special, token, rand:1:8, rand:2:8, rand:3:8, rand:4:8, rand:5:8

--- expected
+++ observed
@@ -1,12 +1,6 @@
 {
   "content": null,
+  "exception": "OllamaError: failed to parse JSON: unexpected end of JSON input",
   "reasoning_content": null,
-  "tool_calls": [
-    {
-      "arguments": {
-        "text": "Use <tool_call> ... </tool_call> tags, or \"<think>\" blocks."
-      },
-      "name": "echo"
-    }
-  ]
+  "tool_calls": []
 }

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: single-call, marker-in-arguments, string-escapes.

Raw output

<tool_call>
{"name": "echo", "arguments": {"text": "Use <tool_call> ... </tool_call> tags, or \"<think>\" blocks."}}
</tool_call>

Expected parse

{
  "content": null,
  "reasoning_content": null,
  "tool_calls": [
    {
      "arguments": {
        "text": "Use <tool_call> ... </tool_call> tags, or \"<think>\" blocks."
      },
      "name": "echo"
    }
  ]
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/marker-in-arguments", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "echo", "description": "Echo a string back verbatim.", "parameters": {"type": "object", "properties": {"text": {"type": "string"}}, "required": ["text"]}}}], "raw_output": "<tool_call>\n{\"name\": \"echo\", \"arguments\": {\"text\": \"Use <tool_call> ... </tool_call> tags, or \\\"<think>\\\" blocks.\"}}\n</tool_call>", "output_token_ids": [151657, 198, 4913, 606, 788, 330, 3047, 497, 330, 16370, 788, 5212, 1318, 788, 330, 10253, 220, 151657, 2503, 220, 151658, 9492, 11, 476, 7245, 151667, 2105, 10010, 1189, 11248, 151658], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n<think>\n\n</think>\n\n", "thinking": false, "expected": {"content": null, "reasoning_content": null, "tool_calls": [{"name": "echo", "arguments": {"text": "Use <tool_call> ... </tool_call> tags, or \"<think>\" blocks."}}]}, "tags": ["single-call", "marker-in-arguments", "string-escapes"], "notes": "The argument string contains the literal text of the format markers. The official tokenizer maps them to their added-token ids (151657/151658/151667) inside the JSON string; a correct parser must not end the call at the inner </tool_call>, because the JSON is still open there. History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": false,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": false,
  "think_requested": false,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
fail qwen3-hermes/sglang-malformed-json-in-tags expected_error
Checks that did not pass for qwen3-hermes/sglang-malformed-json-in-tags
CheckStrategyResultDetail
expected_errornonstream failoutcome exception "OllamaError: failed to parse JSON: invalid character 'o' in literal null (expecting 'u')"; accepted: ['content_passthrough', 'no_tool_calls']
expected_errortoken failoutcome exception "OllamaError: failed to parse JSON: invalid character 'o' in literal null (expecting 'u')"; accepted: ['content_passthrough', 'no_tool_calls']

Synthetic strategies (not counted): expected_error@one fail, expected_error@special fail, expected_error@rand:1:8 fail, expected_error@rand:2:8 fail, expected_error@rand:3:8 fail, expected_error@rand:4:8 fail, expected_error@rand:5:8 fail

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/malformed.jsonl --id qwen3-hermes/sglang-malformed-json-in-tags --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 2 of fixtures/qwen3-hermes/malformed.jsonl.

Observed

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, one, special, token, rand:1:8, rand:2:8, rand:3:8, rand:4:8, rand:5:8

{
  "content": null,
  "exception": "OllamaError: failed to parse JSON: invalid character 'o' in literal null (expecting 'u')",
  "reasoning_content": null,
  "tool_calls": []
}

Fixture

Provenance: engine_test, https://github.com/sgl-project/sglang/blob/94602c9c2b7cbdb8efd5c52802dac6a1c180089e/test/registered/unit/function_call/test_hermes_detector.py#L107. Tags: malformed.

Expected graceful failure: The tool_call body is not JSON. SGLang documents returning the original text as content. (accept: no_tool_calls, content_passthrough).

Raw output

<tool_call>not valid json</tool_call>
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/sglang-malformed-json-in-tags", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "engine_test", "source_url": "https://github.com/sgl-project/sglang/blob/94602c9c2b7cbdb8efd5c52802dac6a1c180089e/test/registered/unit/function_call/test_hermes_detector.py#L107", "revision": "94602c9c2b7cbdb8efd5c52802dac6a1c180089e", "license": "Apache-2.0", "attribution": "Copyright SGLang Team"}, "tools": [{"type": "function", "function": {"name": "get_weather", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}}, "required": ["city"]}}}], "raw_output": "<tool_call>not valid json</tool_call>", "output_token_ids": [151657, 1921, 2697, 2951, 151658], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n<think>\n\n</think>\n\n", "thinking": false, "expected_error": {"reason": "The tool_call body is not JSON. SGLang documents returning the original text as content.", "accept": ["no_tool_calls", "content_passthrough"]}, "tags": ["malformed"], "notes": "output_token_ids: tokenizer.encode(raw_output)."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": false,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": false,
  "think_requested": false,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
soft pass qwen3-hermes/empty-arguments-thinking expected_match
Checks that did not pass for qwen3-hermes/empty-arguments-thinking
CheckStrategyResultDetail
expected_matchnonstream soft passreasoning_content (whitespace only): expected 'get_time takes no arguments.', got '\nget_time takes no arguments.'
expected_matchtoken soft passreasoning_content (whitespace only): expected 'get_time takes no arguments.', got '\nget_time takes no arguments.'

Synthetic strategies (not counted): expected_match@rand:2:8 soft pass, stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/basic.jsonl --id qwen3-hermes/empty-arguments-thinking --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 8 of fixtures/qwen3-hermes/basic.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, token, rand:2:8

--- expected
+++ observed
@@ -1,5 +1,5 @@
 {
   "content": null,
-  "reasoning_content": "get_time takes no arguments.",
+  "reasoning_content": "\nget_time takes no arguments.",
   "tool_calls": [
     {

Strategies: one, special, rand:1:8, rand:3:8, rand:4:8, rand:5:8

Matches the expected parse.

{
  "content": null,
  "reasoning_content": "get_time takes no arguments.",
  "tool_calls": [
    {
      "arguments": {},
      "name": "get_time"
    }
  ]
}

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: single-call, empty-arguments, reasoning.

Raw output

<think>
get_time takes no arguments.
</think>

<tool_call>
{"name": "get_time", "arguments": {}}
</tool_call>

Expected parse

{
  "content": null,
  "reasoning_content": "get_time takes no arguments.",
  "tool_calls": [
    {
      "arguments": {},
      "name": "get_time"
    }
  ]
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/empty-arguments-thinking", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "get_time", "description": "Get the current UTC time. Takes no arguments.", "parameters": {"type": "object", "properties": {}}}}], "raw_output": "<think>\nget_time takes no arguments.\n</think>\n\n<tool_call>\n{\"name\": \"get_time\", \"arguments\": {}}\n</tool_call>", "output_token_ids": [151667, 198, 455, 3009, 4990, 902, 5977, 624, 151668, 271, 151657, 198, 4913, 606, 788, 330, 455, 3009, 497, 330, 16370, 788, 4687, 532, 151658], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected": {"content": null, "reasoning_content": "get_time takes no arguments.", "tool_calls": [{"name": "get_time", "arguments": {}}]}, "tags": ["single-call", "empty-arguments", "reasoning"], "notes": "History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
soft pass qwen3-hermes/multi-turn-final-answer-thinking expected_match
Checks that did not pass for qwen3-hermes/multi-turn-final-answer-thinking
CheckStrategyResultDetail
expected_matchnonstream soft passreasoning_content (whitespace only): expected 'The tool returned 20C.', got '\nThe tool returned 20C.'
expected_matchtoken soft passreasoning_content (whitespace only): expected 'The tool returned 20C.', got '\nThe tool returned 20C.'

Synthetic strategies (not counted): expected_match@rand:2:8 soft pass, stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/multiturn.jsonl --id qwen3-hermes/multi-turn-final-answer-thinking --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 2 of fixtures/qwen3-hermes/multiturn.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, token, rand:2:8

--- expected
+++ observed
@@ -1,5 +1,5 @@
 {
   "content": "It is 20 °C in Zürich right now.",
-  "reasoning_content": "The tool returned 20C.",
+  "reasoning_content": "\nThe tool returned 20C.",
   "tool_calls": []
 }

Strategies: one, special, rand:1:8, rand:3:8, rand:4:8, rand:5:8

Matches the expected parse.

{
  "content": "It is 20 °C in Zürich right now.",
  "reasoning_content": "The tool returned 20C.",
  "tool_calls": []
}

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: no-call, multi-turn, reasoning, unicode.

Raw output

<think>
The tool returned 20C.
</think>

It is 20 °C in Zürich right now.

Expected parse

{
  "content": "It is 20 °C in Zürich right now.",
  "reasoning_content": "The tool returned 20C.",
  "tool_calls": []
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/multi-turn-final-answer-thinking", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["c", "f"]}}, "required": ["city"]}}}], "raw_output": "<think>\nThe tool returned 20C.\n</think>\n\nIt is 20 °C in Zürich right now.", "output_token_ids": [151667, 198, 785, 5392, 5927, 220, 17, 15, 34, 624, 151668, 271, 2132, 374, 220, 17, 15, 36286, 34, 304, 1863, 5186, 713, 1290, 1431, 13], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected": {"content": "It is 20 °C in Zürich right now.", "reasoning_content": "The tool returned 20C.", "tool_calls": []}, "tags": ["no-call", "multi-turn", "reasoning", "unicode"], "notes": "History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
soft pass qwen3-hermes/multi-turn-second-call-thinking expected_match
Checks that did not pass for qwen3-hermes/multi-turn-second-call-thinking
CheckStrategyResultDetail
expected_matchnonstream soft passreasoning_content (whitespace only): expected 'It is 20C in Zürich. Now search for cafés.', got '\nIt is 20C in Zürich. Now search for cafés.'
expected_matchtoken soft passreasoning_content (whitespace only): expected 'It is 20C in Zürich. Now search for cafés.', got '\nIt is 20C in Zürich. Now search for cafés.'

Synthetic strategies (not counted): expected_match@rand:2:8 soft pass, stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/multiturn.jsonl --id qwen3-hermes/multi-turn-second-call-thinking --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 1 of fixtures/qwen3-hermes/multiturn.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, token, rand:2:8

--- expected
+++ observed
@@ -1,5 +1,5 @@
 {
   "content": null,
-  "reasoning_content": "It is 20C in Zürich. Now search for cafés.",
+  "reasoning_content": "\nIt is 20C in Zürich. Now search for cafés.",
   "tool_calls": [
     {

Strategies: one, special, rand:1:8, rand:3:8, rand:4:8, rand:5:8

Matches the expected parse.

{
  "content": null,
  "reasoning_content": "It is 20C in Zürich. Now search for cafés.",
  "tool_calls": [
    {
      "arguments": {
        "filters": {
          "open_now": true
        },
        "query": "café Zürich"
      },
      "name": "search"
    }
  ]
}

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: single-call, multi-turn, reasoning, unicode.

Raw output

<think>
It is 20C in Zürich. Now search for cafés.
</think>

<tool_call>
{"name": "search", "arguments": {"query": "café Zürich", "filters": {"open_now": true}}}
</tool_call>

Expected parse

{
  "content": null,
  "reasoning_content": "It is 20C in Zürich. Now search for cafés.",
  "tool_calls": [
    {
      "arguments": {
        "filters": {
          "open_now": true
        },
        "query": "café Zürich"
      },
      "name": "search"
    }
  ]
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/multi-turn-second-call-thinking", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["c", "f"]}}, "required": ["city"]}}}, {"type": "function", "function": {"name": "search", "description": "Search the web.", "parameters": {"type": "object", "properties": {"query": {"type": "string"}, "filters": {"type": "object"}}, "required": ["query"]}}}], "raw_output": "<think>\nIt is 20C in Zürich. Now search for cafés.\n</think>\n\n<tool_call>\n{\"name\": \"search\", \"arguments\": {\"query\": \"café Zürich\", \"filters\": {\"open_now\": true}}}\n</tool_call>", "output_token_ids": [151667, 198, 2132, 374, 220, 17, 15, 34, 304, 1863, 5186, 713, 13, 4695, 2711, 369, 29103, 5397, 624, 151668, 271, 151657, 198, 4913, 606, 788, 330, 1836, 497, 330, 16370, 788, 5212, 1631, 788, 330, 924, 58858, 1863, 5186, 713, 497, 330, 24608, 788, 5212, 2508, 20813, 788, 830, 3417, 532, 151658], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected": {"content": null, "reasoning_content": "It is 20C in Zürich. Now search for cafés.", "tool_calls": [{"name": "search", "arguments": {"query": "café Zürich", "filters": {"open_now": true}}}]}, "tags": ["single-call", "multi-turn", "reasoning", "unicode"], "notes": "History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
soft pass qwen3-hermes/no-call-thinking expected_match
Checks that did not pass for qwen3-hermes/no-call-thinking
CheckStrategyResultDetail
expected_matchnonstream soft passreasoning_content (whitespace only): expected 'The user is just greeting me. No tool is needed.', got '\nThe user is just greeting me. No tool is needed.'
expected_matchtoken soft passreasoning_content (whitespace only): expected 'The user is just greeting me. No tool is needed.', got '\nThe user is just greeting me. No tool is needed.'

Synthetic strategies (not counted): expected_match@rand:2:8 soft pass, stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/basic.jsonl --id qwen3-hermes/no-call-thinking --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 3 of fixtures/qwen3-hermes/basic.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, token, rand:2:8

--- expected
+++ observed
@@ -1,5 +1,5 @@
 {
   "content": "Hello! How can I help you today?",
-  "reasoning_content": "The user is just greeting me. No tool is needed.",
+  "reasoning_content": "\nThe user is just greeting me. No tool is needed.",
   "tool_calls": []
 }

Strategies: one, special, rand:1:8, rand:3:8, rand:4:8, rand:5:8

Matches the expected parse.

{
  "content": "Hello! How can I help you today?",
  "reasoning_content": "The user is just greeting me. No tool is needed.",
  "tool_calls": []
}

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: no-call, reasoning.

Raw output

<think>
The user is just greeting me. No tool is needed.
</think>

Hello! How can I help you today?

Expected parse

{
  "content": "Hello! How can I help you today?",
  "reasoning_content": "The user is just greeting me. No tool is needed.",
  "tool_calls": []
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/no-call-thinking", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["c", "f"]}}, "required": ["city"]}}}], "raw_output": "<think>\nThe user is just greeting me. No tool is needed.\n</think>\n\nHello! How can I help you today?", "output_token_ids": [151667, 198, 785, 1196, 374, 1101, 42113, 752, 13, 2308, 5392, 374, 4362, 624, 151668, 271, 9707, 0, 2585, 646, 358, 1492, 498, 3351, 30], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected": {"content": "Hello! How can I help you today?", "reasoning_content": "The user is just greeting me. No tool is needed.", "tool_calls": []}, "tags": ["no-call", "reasoning"], "notes": "History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
soft pass qwen3-hermes/parallel-two-calls-thinking expected_match
Checks that did not pass for qwen3-hermes/parallel-two-calls-thinking
CheckStrategyResultDetail
expected_matchnonstream soft passreasoning_content (whitespace only): expected 'I should call the tools.', got '\nI should call the tools.'
expected_matchtoken soft passreasoning_content (whitespace only): expected 'I should call the tools.', got '\nI should call the tools.'

Synthetic strategies (not counted): expected_match@rand:2:8 soft pass, stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/parallel.jsonl --id qwen3-hermes/parallel-two-calls-thinking --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 1 of fixtures/qwen3-hermes/parallel.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, token, rand:2:8

--- expected
+++ observed
@@ -1,5 +1,5 @@
 {
   "content": null,
-  "reasoning_content": "I should call the tools.",
+  "reasoning_content": "\nI should call the tools.",
   "tool_calls": [
     {

Strategies: one, special, rand:1:8, rand:3:8, rand:4:8, rand:5:8

Matches the expected parse.

{
  "content": null,
  "reasoning_content": "I should call the tools.",
  "tool_calls": [
    {
      "arguments": {
        "city": "Zürich",
        "unit": "c"
      },
      "name": "get_weather"
    },
    {
      "arguments": {
        "filters": {
          "max": 3,
          "tags": [
            "a",
            "b"
          ]
        },
        "query": "café \"best\""
      },
      "name": "search"
    }
  ]
}

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: parallel-calls, reasoning, unicode, nested-json, string-escapes.

Raw output

<think>
I should call the tools.
</think>

<tool_call>
{"name": "get_weather", "arguments": {"city": "Zürich", "unit": "c"}}
</tool_call>
<tool_call>
{"name": "search", "arguments": {"query": "café \"best\"", "filters": {"tags": ["a", "b"], "max": 3}}}
</tool_call>

Expected parse

{
  "content": null,
  "reasoning_content": "I should call the tools.",
  "tool_calls": [
    {
      "arguments": {
        "city": "Zürich",
        "unit": "c"
      },
      "name": "get_weather"
    },
    {
      "arguments": {
        "filters": {
          "max": 3,
          "tags": [
            "a",
            "b"
          ]
        },
        "query": "café \"best\""
      },
      "name": "search"
    }
  ]
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/parallel-two-calls-thinking", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["c", "f"]}}, "required": ["city"]}}}, {"type": "function", "function": {"name": "search", "description": "Search the web.", "parameters": {"type": "object", "properties": {"query": {"type": "string"}, "filters": {"type": "object"}}, "required": ["query"]}}}], "raw_output": "<think>\nI should call the tools.\n</think>\n\n<tool_call>\n{\"name\": \"get_weather\", \"arguments\": {\"city\": \"Zürich\", \"unit\": \"c\"}}\n</tool_call>\n<tool_call>\n{\"name\": \"search\", \"arguments\": {\"query\": \"café \\\"best\\\"\", \"filters\": {\"tags\": [\"a\", \"b\"], \"max\": 3}}}\n</tool_call>", "output_token_ids": [151667, 198, 40, 1265, 1618, 279, 7375, 624, 151668, 271, 151657, 198, 4913, 606, 788, 330, 455, 69364, 497, 330, 16370, 788, 5212, 8926, 788, 330, 57, 5186, 713, 497, 330, 3843, 788, 330, 66, 95642, 151658, 198, 151657, 198, 4913, 606, 788, 330, 1836, 497, 330, 16370, 788, 5212, 1631, 788, 330, 924, 58858, 7245, 15862, 55853, 330, 24608, 788, 5212, 14082, 788, 4383, 64, 497, 330, 65, 7914, 330, 2810, 788, 220, 18, 3417, 532, 151658], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected": {"content": null, "reasoning_content": "I should call the tools.", "tool_calls": [{"name": "get_weather", "arguments": {"city": "Zürich", "unit": "c"}}, {"name": "search", "arguments": {"query": "café \"best\"", "filters": {"tags": ["a", "b"], "max": 3}}}]}, "tags": ["parallel-calls", "reasoning", "unicode", "nested-json", "string-escapes"], "notes": "History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
soft pass qwen3-hermes/single-call-thinking expected_match
Checks that did not pass for qwen3-hermes/single-call-thinking
CheckStrategyResultDetail
expected_matchnonstream soft passreasoning_content (whitespace only): expected "The user wants the current weather in Paris. I'll call get_weather.", got "\nThe user wants the current weather in Paris. I'll call get_weather."
expected_matchtoken soft passreasoning_content (whitespace only): expected "The user wants the current weather in Paris. I'll call get_weather.", got "\nThe user wants the current weather in Paris. I'll call get_weather."

Synthetic strategies (not counted): expected_match@rand:2:8 soft pass, stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/basic.jsonl --id qwen3-hermes/single-call-thinking --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 1 of fixtures/qwen3-hermes/basic.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, token, rand:2:8

--- expected
+++ observed
@@ -1,5 +1,5 @@
 {
   "content": null,
-  "reasoning_content": "The user wants the current weather in Paris. I'll call get_weather.",
+  "reasoning_content": "\nThe user wants the current weather in Paris. I'll call get_weather.",
   "tool_calls": [
     {

Strategies: one, special, rand:1:8, rand:3:8, rand:4:8, rand:5:8

Matches the expected parse.

{
  "content": null,
  "reasoning_content": "The user wants the current weather in Paris. I'll call get_weather.",
  "tool_calls": [
    {
      "arguments": {
        "city": "Paris",
        "unit": "c"
      },
      "name": "get_weather"
    }
  ]
}

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: single-call, reasoning.

Raw output

<think>
The user wants the current weather in Paris. I'll call get_weather.
</think>

<tool_call>
{"name": "get_weather", "arguments": {"city": "Paris", "unit": "c"}}
</tool_call>

Expected parse

{
  "content": null,
  "reasoning_content": "The user wants the current weather in Paris. I'll call get_weather.",
  "tool_calls": [
    {
      "arguments": {
        "city": "Paris",
        "unit": "c"
      },
      "name": "get_weather"
    }
  ]
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/single-call-thinking", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["c", "f"]}}, "required": ["city"]}}}], "raw_output": "<think>\nThe user wants the current weather in Paris. I'll call get_weather.\n</think>\n\n<tool_call>\n{\"name\": \"get_weather\", \"arguments\": {\"city\": \"Paris\", \"unit\": \"c\"}}\n</tool_call>", "output_token_ids": [151667, 198, 785, 1196, 6801, 279, 1482, 9104, 304, 12095, 13, 358, 3278, 1618, 633, 69364, 624, 151668, 271, 151657, 198, 4913, 606, 788, 330, 455, 69364, 497, 330, 16370, 788, 5212, 8926, 788, 330, 59604, 497, 330, 3843, 788, 330, 66, 95642, 151658], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected": {"content": null, "reasoning_content": "The user wants the current weather in Paris. I'll call get_weather.", "tool_calls": [{"name": "get_weather", "arguments": {"city": "Paris", "unit": "c"}}]}, "tags": ["single-call", "reasoning"], "notes": "History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
soft pass qwen3-hermes/text-before-call-thinking expected_match
Checks that did not pass for qwen3-hermes/text-before-call-thinking
CheckStrategyResultDetail
expected_matchnonstream soft passreasoning_content (whitespace only): expected 'I need live weather data for London.', got '\nI need live weather data for London.'
expected_matchtoken soft passreasoning_content (whitespace only): expected 'I need live weather data for London.', got '\nI need live weather data for London.'

Synthetic strategies (not counted): expected_match@rand:2:8 soft pass, stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/basic.jsonl --id qwen3-hermes/text-before-call-thinking --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 5 of fixtures/qwen3-hermes/basic.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, token, rand:2:8

--- expected
+++ observed
@@ -1,5 +1,5 @@
 {
   "content": "Let me check the current conditions in London.",
-  "reasoning_content": "I need live weather data for London.",
+  "reasoning_content": "\nI need live weather data for London.",
   "tool_calls": [
     {

Strategies: one, special, rand:1:8, rand:3:8, rand:4:8, rand:5:8

Matches the expected parse.

{
  "content": "Let me check the current conditions in London.",
  "reasoning_content": "I need live weather data for London.",
  "tool_calls": [
    {
      "arguments": {
        "city": "London"
      },
      "name": "get_weather"
    }
  ]
}

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: single-call, text-before-call, reasoning.

Raw output

<think>
I need live weather data for London.
</think>

Let me check the current conditions in London.
<tool_call>
{"name": "get_weather", "arguments": {"city": "London"}}
</tool_call>

Expected parse

{
  "content": "Let me check the current conditions in London.",
  "reasoning_content": "I need live weather data for London.",
  "tool_calls": [
    {
      "arguments": {
        "city": "London"
      },
      "name": "get_weather"
    }
  ]
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/text-before-call-thinking", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["c", "f"]}}, "required": ["city"]}}}], "raw_output": "<think>\nI need live weather data for London.\n</think>\n\nLet me check the current conditions in London.\n<tool_call>\n{\"name\": \"get_weather\", \"arguments\": {\"city\": \"London\"}}\n</tool_call>", "output_token_ids": [151667, 198, 40, 1184, 3887, 9104, 821, 369, 7148, 624, 151668, 271, 10061, 752, 1779, 279, 1482, 4682, 304, 7148, 624, 151657, 198, 4913, 606, 788, 330, 455, 69364, 497, 330, 16370, 788, 5212, 8926, 788, 330, 39572, 95642, 151658], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected": {"content": "Let me check the current conditions in London.", "reasoning_content": "I need live weather data for London.", "tool_calls": [{"name": "get_weather", "arguments": {"city": "London"}}]}, "tags": ["single-call", "text-before-call", "reasoning"], "notes": "History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
soft pass qwen3-hermes/truncated-in-reasoning expected_match
Checks that did not pass for qwen3-hermes/truncated-in-reasoning
CheckStrategyResultDetail
expected_matchnonstream soft passreasoning_content (whitespace only): expected 'The user wants the current weather', got '\nThe user wants the current weather'
expected_matchtoken soft passreasoning_content (whitespace only): expected 'The user wants the current weather', got '\nThe user wants the current weather'

Synthetic strategies (not counted): expected_match@rand:2:8 soft pass, stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/truncated.jsonl --id qwen3-hermes/truncated-in-reasoning --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 5 of fixtures/qwen3-hermes/truncated.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, token, rand:2:8

--- expected
+++ observed
@@ -1,5 +1,5 @@
 {
   "content": null,
-  "reasoning_content": "The user wants the current weather",
+  "reasoning_content": "\nThe user wants the current weather",
   "tool_calls": []
 }

Strategies: one, special, rand:1:8, rand:3:8, rand:4:8, rand:5:8

Matches the expected parse.

{
  "content": null,
  "reasoning_content": "The user wants the current weather",
  "tool_calls": []
}

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: truncated, reasoning.

Raw output

<think>
The user wants the current weather

Expected parse

{
  "content": null,
  "reasoning_content": "The user wants the current weather",
  "tool_calls": []
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/truncated-in-reasoning", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["c", "f"]}}, "required": ["city"]}}}], "raw_output": "<think>\nThe user wants the current weather", "output_token_ids": [151667, 198, 785, 1196, 6801, 279, 1482, 9104], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected": {"content": null, "reasoning_content": "The user wants the current weather", "tool_calls": []}, "tags": ["truncated", "reasoning"], "notes": "Cut inside <think> (no </think> yet): the partial text is reasoning, not content. Token prefix of the full render, cut right after 'current weather' appears. History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
soft pass qwen3-hermes/unicode-emoji-thinking expected_match
Checks that did not pass for qwen3-hermes/unicode-emoji-thinking
CheckStrategyResultDetail
expected_matchnonstream soft passreasoning_content (whitespace only): expected 'Translate the mixed-script greeting (日本語, العربية, emoji 🌸).', got '\nTranslate the mixed-script greeting (日本語, العربية, emoji 🌸).'
expected_matchtoken soft passreasoning_content (whitespace only): expected 'Translate the mixed-script greeting (日本語, العربية, emoji 🌸).', got '\nTranslate the mixed-script greeting (日本語, العربية, emoji 🌸).'

Synthetic strategies (not counted): expected_match@rand:2:8 soft pass, stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/edge.jsonl --id qwen3-hermes/unicode-emoji-thinking --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 2 of fixtures/qwen3-hermes/edge.jsonl.

Observed vs expected

Identical parses are grouped. Empty strings are shown as null, as in strict comparison.

Strategies: nonstream, token, rand:2:8

--- expected
+++ observed
@@ -1,5 +1,5 @@
 {
   "content": null,
-  "reasoning_content": "Translate the mixed-script greeting (日本語, العربية, emoji 🌸).",
+  "reasoning_content": "\nTranslate the mixed-script greeting (日本語, العربية, emoji 🌸).",
   "tool_calls": [
     {

Strategies: one, special, rand:1:8, rand:3:8, rand:4:8, rand:5:8

Matches the expected parse.

{
  "content": null,
  "reasoning_content": "Translate the mixed-script greeting (日本語, العربية, emoji 🌸).",
  "tool_calls": [
    {
      "arguments": {
        "target": "de",
        "text": "こんにちは 🌸 مرحبا — naïve café 👩‍💻"
      },
      "name": "translate"
    }
  ]
}

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: single-call, unicode, reasoning.

Raw output

<think>
Translate the mixed-script greeting (日本語, العربية, emoji 🌸).
</think>

<tool_call>
{"name": "translate", "arguments": {"text": "こんにちは 🌸 مرحبا — naïve café 👩‍💻", "target": "de"}}
</tool_call>

Expected parse

{
  "content": null,
  "reasoning_content": "Translate the mixed-script greeting (日本語, العربية, emoji 🌸).",
  "tool_calls": [
    {
      "arguments": {
        "target": "de",
        "text": "こんにちは 🌸 مرحبا — naïve café 👩‍💻"
      },
      "name": "translate"
    }
  ]
}
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/unicode-emoji-thinking", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "translate", "description": "Translate text.", "parameters": {"type": "object", "properties": {"text": {"type": "string"}, "target": {"type": "string"}}, "required": ["text", "target"]}}}], "raw_output": "<think>\nTranslate the mixed-script greeting (日本語, العربية, emoji 🌸).\n</think>\n\n<tool_call>\n{\"name\": \"translate\", \"arguments\": {\"text\": \"こんにちは 🌸 مرحبا — naïve café 👩‍💻\", \"target\": \"de\"}}\n</tool_call>", "output_token_ids": [151667, 198, 27473, 279, 9519, 65960, 42113, 320, 101059, 102819, 11, 129071, 11, 42365, 11162, 234, 116, 4292, 151668, 271, 151657, 198, 4913, 606, 788, 330, 14045, 497, 330, 16370, 788, 5212, 1318, 788, 330, 89015, 11162, 234, 116, 23364, 126860, 124671, 1959, 94880, 586, 51950, 61804, 102, 378, 235, 145851, 497, 330, 5657, 788, 330, 450, 95642, 151658], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected": {"content": null, "reasoning_content": "Translate the mixed-script greeting (日本語, العربية, emoji 🌸).", "tool_calls": [{"name": "translate", "arguments": {"text": "こんにちは 🌸 مرحبا — naïve café 👩‍💻", "target": "de"}}]}, "tags": ["single-call", "unicode", "reasoning"], "notes": "History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}
pass qwen3-hermes/truncated-mid-arguments-thinking

Synthetic strategies (not counted): stream_equals_nonstream@one soft pass, stream_equals_nonstream@special soft pass, stream_equals_nonstream@rand:1:8 soft pass, stream_equals_nonstream@rand:3:8 soft pass, stream_equals_nonstream@rand:4:8 soft pass, stream_equals_nonstream@rand:5:8 soft pass

Minimal repro

uv run canitoolcall run --engine ollama --fixtures fixtures/qwen3-hermes/truncated.jsonl --id qwen3-hermes/truncated-mid-arguments-thinking --strategy token --observed all

Set up the engine first with scripts/engines/ollama.sh; this run used ollama 7af39318. The fixture is line 2 of fixtures/qwen3-hermes/truncated.jsonl.

The results file does not include the observed parses for this fixture.

Fixture

Provenance: template_render, https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json. Tags: truncated, reasoning.

Expected graceful failure: Output cut by max_tokens inside the tool-call JSON: no complete call exists. (accept: no_tool_calls, content_passthrough, exception).

Raw output

<think>
I should call the tool.
</think>

<tool_call>
{"name": "get_weather", "arguments": {"city
Fixture record (JSONL, ready to vendor into an engine's tests)
{"id": "qwen3-hermes/truncated-mid-arguments-thinking", "family": "qwen3-hermes", "models": ["Qwen/Qwen3-0.6B", "Qwen/Qwen3-8B", "Qwen/Qwen3-32B", "Qwen/Qwen3-30B-A3B", "Qwen/Qwen3-235B-A22B"], "spec_version": "0.1", "provenance": {"kind": "template_render", "source_url": "https://huggingface.co/Qwen/Qwen3-0.6B/blob/c1899de289a04d12100db370d81485cdf75e47ca/tokenizer_config.json", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "license": "Apache-2.0", "generator": "scripts/fixtures/qwen3-hermes/build.py", "template_sha256": "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"}, "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}, "unit": {"type": "string", "enum": ["c", "f"]}}, "required": ["city"]}}}, {"type": "function", "function": {"name": "search", "description": "Search the web.", "parameters": {"type": "object", "properties": {"query": {"type": "string"}, "filters": {"type": "object"}}, "required": ["query"]}}}], "raw_output": "<think>\nI should call the tool.\n</think>\n\n<tool_call>\n{\"name\": \"get_weather\", \"arguments\": {\"city", "output_token_ids": [151667, 198, 40, 1265, 1618, 279, 5392, 624, 151668, 271, 151657, 198, 4913, 606, 788, 330, 455, 69364, 497, 330, 16370, 788, 5212, 8926], "tokenizer": {"repo": "Qwen/Qwen3-0.6B", "revision": "c1899de289a04d12100db370d81485cdf75e47ca", "mode": "hf"}, "generation_prompt": "<|im_start|>assistant\n", "thinking": true, "expected_error": {"reason": "Output cut by max_tokens inside the tool-call JSON: no complete call exists.", "accept": ["no_tool_calls", "content_passthrough", "exception"]}, "tags": ["truncated", "reasoning"], "notes": "Token prefix of the full render, cut right after '\"arguments\": {\"ci' appears. History render of the official template; Qwen3 generates this exact layout."}
Parser configuration
{
  "chunking": "one llama-server event per token; Add(delta, false) per non-empty group, then Add('', true)",
  "detokenizer": "llama-server common_token_to_piece(special = id in preserved_tokens)",
  "eog_stop": "tokens after the first end-of-generation token are dropped (llama-server stops there)",
  "has_thinking_support": true,
  "legacy_template_parser": false,
  "llama_cpp_build": "b11081",
  "nonstream": "per-token events concatenated (routes.go writeChatResponse)",
  "ollama_commit": "7af393188defd52d370464de0d2064649cab9b41",
  "parser": "qwen3-thinking",
  "parser_source": "hybrid Qwen3: qwen3-thinking when thinking is on (the Qwen3 parser that handles <think>), qwen3 when the request disables thinking",
  "preserved_tokens": [
    "<think>",
    "</think>",
    "<tool_call>",
    "</tool_call>"
  ],
  "think_effective": true,
  "think_requested": true,
  "tool_call_serialization": "openai.ToToolCalls",
  "vocab_gguf": "Qwen--Qwen3-0.6B.vocab.gguf",
  "vocab_gguf_converter_llama_cpp_commit": "a25c9865fe03c954c93fd755b5d79ae86ba99750",
  "vocab_gguf_revision": "c1899de289a04d12100db370d81485cdf75e47ca",
  "vocab_gguf_revision_matches_tokenizer": true,
  "vocab_gguf_sha256": "720f095bcaff60b6e960fa8341d2cbb178dcef1bd3ef99e49c8b4e6174022451"
}