- Qwen3.8-27B β quimmedes Chat Template
- Files
- Quick usage
- Changes vs. the stock Qwen3.8-27B template
- 1. Default reasoning effort:
xhighβlow - 2. Robust
render_content(no more crashes on odd inputs) - 3. Cleaner assistant turn (no phantom whitespace)
- 4. Tool-call arguments: mapping or string
- 5. Tool-result messages as the first message
- 6. Smaller, safer "last user query" scan
- 7. Explicit generation-prompt branches
- 8. Attribution markers
- 1. Default reasoning effort:
- Why this is better
- Attribution
- Files
Qwen3.8-27B β quimmedes Chat Template
A drop-in replacement Jinja chat template for the Qwen3.8-27B model. It is a
hardened, more efficient fork of the stock Qwen3.8-27B template: same message
format, same tool-calling protocol, same think block β but with a faster
default reasoning mode, a finer-grained reasoning-effort dial, and robustness
fixes that make it stop crashing on real-world (messy) message histories.
No weights, no tokenizer here. This repo ships only the template. Point it at your existing Qwen3.8-27B weights / tokenizer.
Files
| File | What it is |
|---|---|
chat_template.jinja |
The template source (human-readable, editable). |
Quick usage
All three major serving engines accept a chat-template file directly.
llama.cpp (llama-server / llama-cli):
llama-server -m Qwen3.8-27B.gguf --chat-template-file chat_template.jinja
--jinja(the engine toggle) is on by default, so no extra flag is needed. To pass a reasoning effort at the CLI: add--reasoning-effort low.
More flags to force the total limit of tokens spent, you can use it as a safeguard in case any other limitation fails, it's helpful with Hermes
--reasoning-budget 2048
--chat-template-kwargs "{\"preserve_thinking\": true, \"reasoning_effort\": \"low\"}"
--reasoning-budget-message "... I am thinking for too long -- let me gather more info about the task."
--chat-template-file chat_template_budget.jinja
--reasoning-budgetThe max size of tokens spent in thinking--reasoning-budget-messageMessage sent after the thinking budget cut--chat-template-kwargsCan also set to low, medium or xhigh by default in case the harness can't do that
vLLM (vllm serve):
vllm serve Qwen3.8-27B --chat-template chat_template.jinja
--chat-templateaccepts a path to a template file (or a built-in name). Per-request effort can be set viachat_template_kwargsin the API call.
SGLang (sglang.launch_server):
python -m sglang.launch_server --model-path Qwen3.8-27B \
--chat-template chat_template.jinja
--chat-templateaccepts "the path of the chat template file" (per the official server args). This only applies to the OpenAI-compatible server.
Option D β apply to a tokenizer (transformers / any Jinja pipeline):
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("path/to/Qwen3.8-27B")
with open("chat_template.jinja", encoding="utf-8") as f:
tok.chat_template = f.read()
tok.save_pretrained("path/to/Qwen3.8-27B")
Reasoning effort
The template reads an optional reasoning_effort variable. Pass it through
your chat pipeline to dial the model's thinking budget:
prompt = tok.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=True,
reasoning_effort="medium", # minimal | low | medium | high | xhigh | ultra | max
)
low is the default when you don't pass anything (see below).
Changes vs. the stock Qwen3.8-27B template
Everything below is a superset of the stock template β the core
user/assistant/system/tool message layout and the
tool_call/function XML protocol are unchanged, so the model behaves
identically on well-formed inputs. The differences are in the defaults and in
edge-case handling.
1. Default reasoning effort: xhigh β low
| Stock | quimmedes | |
|---|---|---|
Default reasoning_effort |
xhigh |
low |
| Levels supported | xhigh, medium, low (3) |
minimal, low, medium, high, xhigh, ultra, max (7) |
The stock template forces maximum reasoning (xhigh) unless you explicitly
override it. That means every single turn β even a trivial one β burns the
model's full thinking budget. This fork flips the default to low and expands
the dial to 7 levels so you can spend tokens only when a task actually needs
them.
2. Robust render_content (no more crashes on odd inputs)
The stock macro assumes every content item is a well-formed dict. This fork accepts the shapes that real frameworks actually send:
- Plain strings inside a content list β
["hello", {"type":"text","text":"hi"}]now renders instead of raisingUnexpected item type in content. item.get('type')instead ofitem.typeβ safe when the item has notypekey (noUndefinedError).add_vision_id|default(false)β the stock template referencesadd_vision_idwith no default, which errors if the variable is never set.{"type":"text","content":...}items β supports the alternatecontent-keyed text shape, not just thetext-keyed one.nullitems β silently skipped instead of raising.
3. Cleaner assistant turn (no phantom whitespace)
Stock always emitted think\n\n + content, even when there was no
reasoning and no content β producing empty think blocks and stray
double newlines. This fork:
- only opens a
thinkblock when there is actualreasoning_content; - only adds the
\n\nseparator when there is real content to follow; - guards
message.reasoning_content is definedbefore reading it.
Result: byte-clean output with no empty thinking blocks and no dangling newlines β important for KV-cache reuse and for downstream parsers.
4. Tool-call arguments: mapping or string
The stock template assumed tool_call.arguments is always a dict and called
|items on it. Many frameworks (and some model outputs) send arguments as a
JSON string. This fork handles both:
argumentsis a mapping β rendered as<parameter>blocks (as before);argumentsis a string β emitted verbatim (with a trailing newline added if missing).
This is the single biggest compatibility win: it stops hard-failing on string-arg tool calls.
5. Tool-result messages as the first message
Stock: loop.previtem and loop.previtem.role != "tool" β if the very first
message in the history is a tool result, loop.previtem is undefined and the
template errors. This fork adds the loop.first or (...) guard, so a
conversation that starts with a tool result renders correctly.
6. Smaller, safer "last user query" scan
The stock version carried a multi_step_tool flag and raised an exception
if it couldn't find a user query. This fork replaces it with a simpler
last_query_index scan (default -1) and drops the hard exception β so a
history with no plain user message degrades gracefully instead of crashing.
7. Explicit generation-prompt branches
The think opening at the generation prompt is now spelled out for
enable_thinking = true / false / unset (same output as before, just
explicit and easier to audit).
8. Attribution markers
{#- quimmedes -#} comment markers at the top and bottom, plus an inline note
at the xhigh branch, so the fork is identifiable in the wild.
Why this is better
Cheaper & faster by default. The stock template's
xhighdefault makes every turn think at maximum effort. Defaulting tolow(with 7 levels to scale up) means routine queries use a fraction of the reasoning tokens, and you opt into heavy thinking only when a task warrants it.It doesn't crash on real data. The stock template is written for perfectly-formed inputs. In practice, frameworks send string content items, string tool arguments, missing
typekeys,nullentries, and tool-first histories β all of which the stock template rejects. This fork handles all of them, so it's safe to drop into a production serving stack.Cleaner output, better cache reuse. No empty
thinkblocks and no phantom newlines means shorter, more predictable prompts β which improves prefix/KV-cache hit rates and keeps downstream parsers from choking on whitespace.Drop-in, no retraining, no behavior change on good inputs. Because the message layout and tool protocol are untouched, a model that works with the stock template works identically here for well-formed conversations β you only gain the faster default and the robustness.
Attribution
Forked from the stock Qwen3.8-27B chat template. Modified by quimmedes.
Base model: Qwen/Qwen3-27B.