Instructions to use froggeric/Qwen-Fixed-Chat-Templates with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use froggeric/Qwen-Fixed-Chat-Templates with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] huggingface-cli download --local-dir Qwen-Fixed-Chat-Templates froggeric/Qwen-Fixed-Chat-Templates
- Notebooks
- Google Colab
- Kaggle
- Local Apps
- LM Studio
Upload folder using huggingface_hub
Browse files- README.md +195 -0
- qwen3.5/chat_template.jinja +187 -0
- qwen3.6/chat_template.jinja +210 -0
README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
tags:
|
| 4 |
+
- jinja
|
| 5 |
+
- chat-template
|
| 6 |
+
- qwen
|
| 7 |
+
- qwen3.5
|
| 8 |
+
- qwen3.6
|
| 9 |
+
- lm-studio
|
| 10 |
+
- mlx
|
| 11 |
+
- llama.cpp
|
| 12 |
+
- tool-calling
|
| 13 |
+
- thinking
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# Fixed Chat Templates for Qwen 3.5 & 3.6
|
| 17 |
+
|
| 18 |
+
Drop-in Jinja templates that fix the crashes, token waste, and missing features in the official Qwen chat templates. Works in LM Studio, llama.cpp, vLLM, MLX, oMLX, and any engine that supports HuggingFace Jinja templates.
|
| 19 |
+
|
| 20 |
+
## Why you need this
|
| 21 |
+
|
| 22 |
+
The official Qwen templates have bugs that break real usage:
|
| 23 |
+
|
| 24 |
+
| Problem | Impact |
|
| 25 |
+
|---------|--------|
|
| 26 |
+
| Tool calls crash on C++ engines | `|items` filter doesn't exist outside Python — any tool call dies |
|
| 27 |
+
| `developer` role crashes | Modern APIs send it; the official template throws an exception |
|
| 28 |
+
| Empty thinking blocks spam context | Every past turn gets wrapped in tags, even with nothing inside |
|
| 29 |
+
| No way to toggle thinking | You're stuck with whatever the model defaults to |
|
| 30 |
+
| Qwen 3.6: `</thinking>` hallucination | Model sometimes generates the wrong closing tag; parser fails |
|
| 31 |
+
|
| 32 |
+
All five are fixed here, plus a clean `<|think_on|>` / `<|think_off|>` toggle you can drop into any message.
|
| 33 |
+
|
| 34 |
+
## Quick install
|
| 35 |
+
|
| 36 |
+
### LM Studio
|
| 37 |
+
|
| 38 |
+
1. Open your Qwen model in the right-side panel
|
| 39 |
+
2. Scroll to **Prompt Template**
|
| 40 |
+
3. Replace the template with the contents of `qwen3.5/chat_template.jinja` or `qwen3.6/chat_template.jinja`
|
| 41 |
+
4. Save
|
| 42 |
+
|
| 43 |
+
### llama.cpp / koboldcpp
|
| 44 |
+
|
| 45 |
+
```bash
|
| 46 |
+
--jinja --chat-templateFile qwen3.6/chat_template.jinja
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
### vLLM / TextGen
|
| 50 |
+
|
| 51 |
+
Replace the `chat_template` string in your `tokenizer_config.json` with the file contents.
|
| 52 |
+
|
| 53 |
+
### oMLX
|
| 54 |
+
|
| 55 |
+
Overwrite `chat_template.jinja` in your local model directory. Load with `--jinja`. Remove any `chat_template_kwargs` overrides — the template handles everything internally.
|
| 56 |
+
|
| 57 |
+
## Which file do I use?
|
| 58 |
+
|
| 59 |
+
| File | For models |
|
| 60 |
+
|------|-----------|
|
| 61 |
+
| [`qwen3.5/chat_template.jinja`](qwen3.5/chat_template.jinja) | Qwen3.5-35B-A3B, Qwen3.5-32B, Qwen3.5-14B, and all Qwen 3.5 variants |
|
| 62 |
+
| [`qwen3.6/chat_template.jinja`](qwen3.6/chat_template.jinja) | Qwen3.6-27B and future Qwen 3.6 variants |
|
| 63 |
+
|
| 64 |
+
The 3.6 template is a superset — it additionally handles `preserve_thinking`, `</thinking>` hallucination recovery, and interrupted thought streams. If you're on 3.6, use the 3.6 file.
|
| 65 |
+
|
| 66 |
+
## Thinking toggle
|
| 67 |
+
|
| 68 |
+
Drop `<|think_on|>` or `<|think_off|>` anywhere in your system or user prompt. The template intercepts the tag, removes it from context so the model never sees it, and flips the mode.
|
| 69 |
+
|
| 70 |
+
**Fast answer, no reasoning:**
|
| 71 |
+
```
|
| 72 |
+
System: You are a coding assistant. <|think_off|>
|
| 73 |
+
User: What's 2+2?
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
**Deep reasoning:**
|
| 77 |
+
```
|
| 78 |
+
System: You are a coding assistant. <|think_on|>
|
| 79 |
+
User: Implement a red-black tree in Rust.
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
The tag syntax (`<|think_on|>`, `<|think_off|>`) uses Qwen's control-token delimiters, so it will never collide with real text. Earlier community templates used `/think`, which broke legitimate paths like `cd /mnt/project/think`.
|
| 83 |
+
|
| 84 |
+
## Pre-installed models
|
| 85 |
+
|
| 86 |
+
These templates are already bundled with:
|
| 87 |
+
|
| 88 |
+
- [froggeric/Qwen3.6-27B-MLX-8bit](https://huggingface.co/froggeric/Qwen3.6-27B-MLX-8bit)
|
| 89 |
+
- [froggeric/Qwen3.6-27B-MLX-4bit](https://huggingface.co/froggeric/Qwen3.6-27B-MLX-4bit)
|
| 90 |
+
- [froggeric/Qwen3.5-35B-A3B-Uncensored-FernflowerAI-MLX-8bit](https://huggingface.co/froggeric/Qwen3.5-35B-A3B-Uncensored-FernflowerAI-MLX-8bit)
|
| 91 |
+
- [froggeric/Qwen3.5-35B-A3B-Uncensored-FernflowerAI-MLX-4bit](https://huggingface.co/froggeric/Qwen3.5-35B-A3B-Uncensored-FernflowerAI-MLX-4bit)
|
| 92 |
+
|
| 93 |
+
If you're using one of those, you already have the template. This repo is for everyone else.
|
| 94 |
+
|
| 95 |
+
---
|
| 96 |
+
|
| 97 |
+
<details>
|
| 98 |
+
<summary>Technical details — what exactly was fixed</summary>
|
| 99 |
+
|
| 100 |
+
## Tool calls on C++ engines
|
| 101 |
+
|
| 102 |
+
The official template iterates tool call arguments with `|items`:
|
| 103 |
+
|
| 104 |
+
```jinja
|
| 105 |
+
{%- for key, value in tool_call.arguments|items %}
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
Python's Jinja supports `|items`. C++ runtimes (LM Studio, llama.cpp, MLX) do not. This template uses direct dictionary key lookups instead:
|
| 109 |
+
|
| 110 |
+
```jinja
|
| 111 |
+
{%- for args_name in tool_call.arguments %}
|
| 112 |
+
{%- set args_value = tool_call.arguments[args_name] %}
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
It also replaces `is sequence` with `is iterable` (stricter C++ runtimes require it), removes `|safe` wrappers (also Python-only), and handles arguments returned as raw strings instead of objects.
|
| 116 |
+
|
| 117 |
+
## `developer` role
|
| 118 |
+
|
| 119 |
+
The OpenAI-compatible API spec sends `message.role == "developer"` for system-level instructions. The official Qwen template only checks for `"system"` and throws on anything else. Both templates here accept `"developer"` and map it to the system role.
|
| 120 |
+
|
| 121 |
+
## Empty thinking blocks
|
| 122 |
+
|
| 123 |
+
The official template wraps every past assistant turn in thinking tags:
|
| 124 |
+
|
| 125 |
+
```
|
| 126 |
+
<|im_start|>assistant
|
| 127 |
+
<think/>
|
| 128 |
+
</think >
|
| 129 |
+
|
| 130 |
+
Here is the answer...
|
| 131 |
+
```
|
| 132 |
+
|
| 133 |
+
When there's no reasoning content, those tags are dead weight — they waste context tokens and break prefix caching. The Qwen 3.5 template checks `reasoning_content` before emitting. The Qwen 3.6 template goes further: it respects the `preserve_thinking` kwarg, checks `reasoning_content|trim|length > 0`, and ties history visibility to the `<|think_off|>` override.
|
| 134 |
+
|
| 135 |
+
## `</thinking>` hallucination (Qwen 3.6 only)
|
| 136 |
+
|
| 137 |
+
The Qwen 3.6 model sometimes generates `</thinking>` instead of the expected closing tag. The official parser splits on `</think >` only and fails. The 3.6 template detects which closing tag was actually used and splits on that:
|
| 138 |
+
|
| 139 |
+
```jinja
|
| 140 |
+
{%- if '</think >' in content %}
|
| 141 |
+
{%- set think_end_token = '</think >' %}
|
| 142 |
+
{%- elif '</thinking>' in content %}
|
| 143 |
+
{%- set think_end_token = '</thinking>' %}
|
| 144 |
+
```
|
| 145 |
+
|
| 146 |
+
It also handles interrupted generation (max tokens hit mid-thought) by rescuing incomplete streams instead of injecting broken tag pairs.
|
| 147 |
+
|
| 148 |
+
## Arguments serialization
|
| 149 |
+
|
| 150 |
+
The official template serializes argument values with `|tojson` unconditionally, which turns Python `True` into JSON `true` correctly but crashes when the value is already a string. The fixed templates check the type first — strings pass through as-is, everything else goes through `|tojson`.
|
| 151 |
+
|
| 152 |
+
</details>
|
| 153 |
+
|
| 154 |
+
<details>
|
| 155 |
+
<summary>Comparison — Qwen 3.5 templates</summary>
|
| 156 |
+
|
| 157 |
+
| Feature | Official | LuffyTheFox | mod-ellary | Pneuny | **This** |
|
| 158 |
+
|---------|----------|-------------|------------|--------|----------|
|
| 159 |
+
| Tool arguments | Crashes | Fixed | Missing | Fixed | **Fixed** |
|
| 160 |
+
| `\|safe` removed | Crashes | Fixed | Missing | Fixed | **Fixed** |
|
| 161 |
+
| `developer` role | Missing | Missing | Missing | Missing | **Added** |
|
| 162 |
+
| Thinking toggle | None | None | `/think` (system only) | None | **`<\|think_off\|>` anywhere** |
|
| 163 |
+
| Empty think in history | Broken | Broken | Tags omitted | Broken | **Fixed** |
|
| 164 |
+
| Text safety | N/A | N/A | Breaks on `/think` in paths | N/A | **Safe** |
|
| 165 |
+
| Clean instructions | Yes | Yes | Yes | Injects "I cannot call a tool" | **Yes** |
|
| 166 |
+
|
| 167 |
+
</details>
|
| 168 |
+
|
| 169 |
+
<details>
|
| 170 |
+
<summary>Comparison — Qwen 3.6 template</summary>
|
| 171 |
+
|
| 172 |
+
| Feature | Official | **This** |
|
| 173 |
+
|---------|----------|----------|
|
| 174 |
+
| Tool arguments | Crashes (`\|items`) | **Fixed** |
|
| 175 |
+
| `\|safe` removed | Crashes | **Fixed** |
|
| 176 |
+
| `developer` role | Missing | **Added** |
|
| 177 |
+
| Thinking toggle | None | **`<\|think_off\|>` anywhere** |
|
| 178 |
+
| `preserve_thinking` | Spams empty blocks | **Dynamic length checks** |
|
| 179 |
+
| `</thinking>` hallucination | Fails | **Detected and handled** |
|
| 180 |
+
| Interrupted streams | Broken tags | **Rescued** |
|
| 181 |
+
|
| 182 |
+
</details>
|
| 183 |
+
|
| 184 |
+
---
|
| 185 |
+
|
| 186 |
+
## Authorship
|
| 187 |
+
|
| 188 |
+
| Role | Author |
|
| 189 |
+
|------|--------|
|
| 190 |
+
| Original models | Alibaba Cloud (Qwen team) |
|
| 191 |
+
| Template fixes | [froggeric](https://huggingface.co/froggeric) |
|
| 192 |
+
|
| 193 |
+
## License
|
| 194 |
+
|
| 195 |
+
Apache-2.0, inherited from Qwen.
|
qwen3.5/chat_template.jinja
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set image_count = namespace(value=0) %}
|
| 2 |
+
{%- set video_count = namespace(value=0) %}
|
| 3 |
+
{%- macro render_content(content, do_vision_count, is_system_content=false) %}
|
| 4 |
+
{%- if content is string %}
|
| 5 |
+
{{- content }}
|
| 6 |
+
{%- elif content is iterable and content is not mapping %}
|
| 7 |
+
{%- for item in content %}
|
| 8 |
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 9 |
+
{%- if is_system_content %}
|
| 10 |
+
{{- raise_exception('System message cannot contain images.') }}
|
| 11 |
+
{%- endif %}
|
| 12 |
+
{%- if do_vision_count %}
|
| 13 |
+
{%- set image_count.value = image_count.value + 1 %}
|
| 14 |
+
{%- endif %}
|
| 15 |
+
{%- if add_vision_id is defined and add_vision_id %}
|
| 16 |
+
{{- 'Picture ' ~ image_count.value ~ ': ' }}
|
| 17 |
+
{%- endif %}
|
| 18 |
+
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
|
| 19 |
+
{%- elif 'video' in item or item.type == 'video' %}
|
| 20 |
+
{%- if is_system_content %}
|
| 21 |
+
{{- raise_exception('System message cannot contain videos.') }}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{%- if do_vision_count %}
|
| 24 |
+
{%- set video_count.value = video_count.value + 1 %}
|
| 25 |
+
{%- endif %}
|
| 26 |
+
{%- if add_vision_id is defined and add_vision_id %}
|
| 27 |
+
{{- 'Video ' ~ video_count.value ~ ': ' }}
|
| 28 |
+
{%- endif %}
|
| 29 |
+
{{- '<|vision_start|><|video_pad|><|vision_end|>' }}
|
| 30 |
+
{%- elif 'text' in item %}
|
| 31 |
+
{{- item.text }}
|
| 32 |
+
{%- else %}
|
| 33 |
+
{{- raise_exception('Unexpected item type in content.') }}
|
| 34 |
+
{%- endif %}
|
| 35 |
+
{%- endfor %}
|
| 36 |
+
{%- elif content is none or content is undefined %}
|
| 37 |
+
{{- '' }}
|
| 38 |
+
{%- else %}
|
| 39 |
+
{{- raise_exception('Unexpected content type.') }}
|
| 40 |
+
{%- endif %}
|
| 41 |
+
{%- endmacro %}
|
| 42 |
+
{%- set ns_flags = namespace(enable_thinking=true) %}
|
| 43 |
+
{%- if enable_thinking is defined %}
|
| 44 |
+
{%- set ns_flags.enable_thinking = enable_thinking %}
|
| 45 |
+
{%- endif %}
|
| 46 |
+
{%- if not messages %}
|
| 47 |
+
{{- raise_exception('No messages provided.') }}
|
| 48 |
+
{%- endif %}
|
| 49 |
+
{%- if tools and tools is iterable and tools is not mapping %}
|
| 50 |
+
{{- '<|im_start|>system\n' }}
|
| 51 |
+
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
| 52 |
+
{%- for tool in tools %}
|
| 53 |
+
{{- "\n" }}
|
| 54 |
+
{{- tool | tojson }}
|
| 55 |
+
{%- endfor %}
|
| 56 |
+
{{- "\n</tools>" }}
|
| 57 |
+
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
| 58 |
+
{%- if messages[0].role == 'system' or messages[0].role == 'developer' %}
|
| 59 |
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 60 |
+
{%- if '<|think_off|>' in content %}
|
| 61 |
+
{%- set ns_flags.enable_thinking = false %}
|
| 62 |
+
{%- set content = content.replace('<|think_off|>', '').strip() %}
|
| 63 |
+
{%- elif '<|think_on|>' in content %}
|
| 64 |
+
{%- set ns_flags.enable_thinking = true %}
|
| 65 |
+
{%- set content = content.replace('<|think_on|>', '').strip() %}
|
| 66 |
+
{%- endif %}
|
| 67 |
+
{%- if content %}
|
| 68 |
+
{{- '\n\n' + content }}
|
| 69 |
+
{%- endif %}
|
| 70 |
+
{%- endif %}
|
| 71 |
+
{{- '<|im_end|>\n' }}
|
| 72 |
+
{%- else %}
|
| 73 |
+
{%- if messages[0].role == 'system' or messages[0].role == 'developer' %}
|
| 74 |
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 75 |
+
{%- if '<|think_off|>' in content %}
|
| 76 |
+
{%- set ns_flags.enable_thinking = false %}
|
| 77 |
+
{%- set content = content.replace('<|think_off|>', '').strip() %}
|
| 78 |
+
{%- elif '<|think_on|>' in content %}
|
| 79 |
+
{%- set ns_flags.enable_thinking = true %}
|
| 80 |
+
{%- set content = content.replace('<|think_on|>', '').strip() %}
|
| 81 |
+
{%- endif %}
|
| 82 |
+
{{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
|
| 83 |
+
{%- endif %}
|
| 84 |
+
{%- endif %}
|
| 85 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 86 |
+
{%- for message in messages[::-1] %}
|
| 87 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 88 |
+
{%- if ns.multi_step_tool and message.role == "user" %}
|
| 89 |
+
{%- set content = render_content(message.content, false)|trim %}
|
| 90 |
+
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
| 91 |
+
{%- set ns.multi_step_tool = false %}
|
| 92 |
+
{%- set ns.last_query_index = index %}
|
| 93 |
+
{%- endif %}
|
| 94 |
+
{%- endif %}
|
| 95 |
+
{%- endfor %}
|
| 96 |
+
{%- if ns.multi_step_tool %}
|
| 97 |
+
{{- raise_exception('No user query found in messages.') }}
|
| 98 |
+
{%- endif %}
|
| 99 |
+
{%- for message in messages %}
|
| 100 |
+
{%- set content = render_content(message.content, true)|trim %}
|
| 101 |
+
{%- if '<|think_off|>' in content %}
|
| 102 |
+
{%- set ns_flags.enable_thinking = false %}
|
| 103 |
+
{%- set content = content.replace('<|think_off|>', '').strip() %}
|
| 104 |
+
{%- elif '<|think_on|>' in content %}
|
| 105 |
+
{%- set ns_flags.enable_thinking = true %}
|
| 106 |
+
{%- set content = content.replace('<|think_on|>', '').strip() %}
|
| 107 |
+
{%- endif %}
|
| 108 |
+
{%- if message.role == "system" or message.role == "developer" %}
|
| 109 |
+
{%- if not loop.first %}
|
| 110 |
+
{{- raise_exception('System message must be at the beginning.') }}
|
| 111 |
+
{%- endif %}
|
| 112 |
+
{%- elif message.role == "user" %}
|
| 113 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 114 |
+
{%- elif message.role == "assistant" %}
|
| 115 |
+
{%- set reasoning_content = '' %}
|
| 116 |
+
{%- if message.reasoning_content is string %}
|
| 117 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 118 |
+
{%- else %}
|
| 119 |
+
{%- if '</think>' in content %}
|
| 120 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 121 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 122 |
+
{%- endif %}
|
| 123 |
+
{%- endif %}
|
| 124 |
+
{%- set reasoning_content = reasoning_content|trim %}
|
| 125 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 126 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
|
| 127 |
+
{%- else %}
|
| 128 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 129 |
+
{%- endif %}
|
| 130 |
+
{%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
|
| 131 |
+
{%- for tool_call in message.tool_calls %}
|
| 132 |
+
{%- if tool_call.function is defined %}
|
| 133 |
+
{%- set tool_call = tool_call.function %}
|
| 134 |
+
{%- endif %}
|
| 135 |
+
{%- if loop.first %}
|
| 136 |
+
{%- if content|trim %}
|
| 137 |
+
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 138 |
+
{%- else %}
|
| 139 |
+
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 140 |
+
{%- endif %}
|
| 141 |
+
{%- else %}
|
| 142 |
+
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 143 |
+
{%- endif %}
|
| 144 |
+
{%- if tool_call.arguments is defined and tool_call.arguments is mapping %}
|
| 145 |
+
{%- if tool_call.arguments|length > 0 %}
|
| 146 |
+
{%- for args_name in tool_call.arguments %}
|
| 147 |
+
{%- set args_value = tool_call.arguments[args_name] %}
|
| 148 |
+
{{- '<parameter=' + args_name + '>\n' }}
|
| 149 |
+
{%- set args_value = args_value | tojson if args_value is mapping or (args_value is iterable and args_value is not string) else args_value | string %}
|
| 150 |
+
{{- args_value }}
|
| 151 |
+
{{- '\n</parameter>\n' }}
|
| 152 |
+
{%- endfor %}
|
| 153 |
+
{%- endif %}
|
| 154 |
+
{%- elif tool_call.arguments is defined and tool_call.arguments is string %}
|
| 155 |
+
{%- if tool_call.arguments|trim|length > 0 %}
|
| 156 |
+
{{- tool_call.arguments }}
|
| 157 |
+
{{- '\n' }}
|
| 158 |
+
{%- endif %}
|
| 159 |
+
{%- endif %}
|
| 160 |
+
{{- '</function>\n</tool_call>' }}
|
| 161 |
+
{%- endfor %}
|
| 162 |
+
{%- endif %}
|
| 163 |
+
{{- '<|im_end|>\n' }}
|
| 164 |
+
{%- elif message.role == "tool" %}
|
| 165 |
+
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
| 166 |
+
{{- '<|im_start|>user' }}
|
| 167 |
+
{%- endif %}
|
| 168 |
+
{{- '\n<tool_response>\n' }}
|
| 169 |
+
{{- content }}
|
| 170 |
+
{{- '\n</tool_response>' }}
|
| 171 |
+
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
| 172 |
+
{{- '<|im_end|>\n' }}
|
| 173 |
+
{%- elif loop.last %}
|
| 174 |
+
{{- '<|im_end|>\n' }}
|
| 175 |
+
{%- endif %}
|
| 176 |
+
{%- else %}
|
| 177 |
+
{{- raise_exception('Unexpected message role.') }}
|
| 178 |
+
{%- endif %}
|
| 179 |
+
{%- endfor %}
|
| 180 |
+
{%- if add_generation_prompt %}
|
| 181 |
+
{{- '<|im_start|>assistant\n' }}
|
| 182 |
+
{%- if ns_flags.enable_thinking is false %}
|
| 183 |
+
{{- '<think>\n\n</think>\n\n' }}
|
| 184 |
+
{%- else %}
|
| 185 |
+
{{- '<think>\n' }}
|
| 186 |
+
{%- endif %}
|
| 187 |
+
{%- endif %}
|
qwen3.6/chat_template.jinja
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set image_count = namespace(value=0) %}
|
| 2 |
+
{%- set video_count = namespace(value=0) %}
|
| 3 |
+
{%- macro render_content(content, do_vision_count, is_system_content=false) %}
|
| 4 |
+
{%- if content is string %}
|
| 5 |
+
{{- content }}
|
| 6 |
+
{%- elif content is iterable and content is not mapping %}
|
| 7 |
+
{%- for item in content %}
|
| 8 |
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 9 |
+
{%- if is_system_content %}
|
| 10 |
+
{{- raise_exception('System message cannot contain images.') }}
|
| 11 |
+
{%- endif %}
|
| 12 |
+
{%- if do_vision_count %}
|
| 13 |
+
{%- set image_count.value = image_count.value + 1 %}
|
| 14 |
+
{%- endif %}
|
| 15 |
+
{%- if add_vision_id is defined and add_vision_id %}
|
| 16 |
+
{{- 'Picture ' ~ image_count.value ~ ': ' }}
|
| 17 |
+
{%- endif %}
|
| 18 |
+
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
|
| 19 |
+
{%- elif 'video' in item or item.type == 'video' %}
|
| 20 |
+
{%- if is_system_content %}
|
| 21 |
+
{{- raise_exception('System message cannot contain videos.') }}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{%- if do_vision_count %}
|
| 24 |
+
{%- set video_count.value = video_count.value + 1 %}
|
| 25 |
+
{%- endif %}
|
| 26 |
+
{%- if add_vision_id is defined and add_vision_id %}
|
| 27 |
+
{{- 'Video ' ~ video_count.value ~ ': ' }}
|
| 28 |
+
{%- endif %}
|
| 29 |
+
{{- '<|vision_start|><|video_pad|><|vision_end|>' }}
|
| 30 |
+
{%- elif 'text' in item %}
|
| 31 |
+
{{- item.text }}
|
| 32 |
+
{%- else %}
|
| 33 |
+
{{- raise_exception('Unexpected item type in content.') }}
|
| 34 |
+
{%- endif %}
|
| 35 |
+
{%- endfor %}
|
| 36 |
+
{%- elif content is none or content is undefined %}
|
| 37 |
+
{{- '' }}
|
| 38 |
+
{%- else %}
|
| 39 |
+
{{- raise_exception('Unexpected content type.') }}
|
| 40 |
+
{%- endif %}
|
| 41 |
+
{%- endmacro %}
|
| 42 |
+
{%- set ns_flags = namespace(enable_thinking=true) %}
|
| 43 |
+
{%- if enable_thinking is defined %}
|
| 44 |
+
{%- set ns_flags.enable_thinking = enable_thinking %}
|
| 45 |
+
{%- endif %}
|
| 46 |
+
{%- if not messages %}
|
| 47 |
+
{{- raise_exception('No messages provided.') }}
|
| 48 |
+
{%- endif %}
|
| 49 |
+
{%- if tools and tools is iterable and tools is not mapping %}
|
| 50 |
+
{{- '<|im_start|>system\n' }}
|
| 51 |
+
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
| 52 |
+
{%- for tool in tools %}
|
| 53 |
+
{{- "\n" }}
|
| 54 |
+
{{- tool | tojson }}
|
| 55 |
+
{%- endfor %}
|
| 56 |
+
{{- "\n</tools>" }}
|
| 57 |
+
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
| 58 |
+
{%- if messages[0].role == 'system' or messages[0].role == 'developer' %}
|
| 59 |
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 60 |
+
{%- if '<|think_off|>' in content %}
|
| 61 |
+
{%- set ns_flags.enable_thinking = false %}
|
| 62 |
+
{%- set content = content.replace('<|think_off|>', '') %}
|
| 63 |
+
{%- endif %}
|
| 64 |
+
{%- if '<|think_on|>' in content %}
|
| 65 |
+
{%- set ns_flags.enable_thinking = true %}
|
| 66 |
+
{%- set content = content.replace('<|think_on|>', '') %}
|
| 67 |
+
{%- endif %}
|
| 68 |
+
{%- set content = content.strip() %}
|
| 69 |
+
{%- if content %}
|
| 70 |
+
{{- '\n\n' + content }}
|
| 71 |
+
{%- endif %}
|
| 72 |
+
{%- endif %}
|
| 73 |
+
{{- '<|im_end|>\n' }}
|
| 74 |
+
{%- else %}
|
| 75 |
+
{%- if messages[0].role == 'system' or messages[0].role == 'developer' %}
|
| 76 |
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 77 |
+
{%- if '<|think_off|>' in content %}
|
| 78 |
+
{%- set ns_flags.enable_thinking = false %}
|
| 79 |
+
{%- set content = content.replace('<|think_off|>', '') %}
|
| 80 |
+
{%- endif %}
|
| 81 |
+
{%- if '<|think_on|>' in content %}
|
| 82 |
+
{%- set ns_flags.enable_thinking = true %}
|
| 83 |
+
{%- set content = content.replace('<|think_on|>', '') %}
|
| 84 |
+
{%- endif %}
|
| 85 |
+
{%- set content = content.strip() %}
|
| 86 |
+
{{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
|
| 87 |
+
{%- endif %}
|
| 88 |
+
{%- endif %}
|
| 89 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 90 |
+
{%- for message in messages[::-1] %}
|
| 91 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 92 |
+
{%- if ns.multi_step_tool and message.role == "user" %}
|
| 93 |
+
{%- set content = render_content(message.content, false)|trim %}
|
| 94 |
+
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
| 95 |
+
{%- set ns.multi_step_tool = false %}
|
| 96 |
+
{%- set ns.last_query_index = index %}
|
| 97 |
+
{%- endif %}
|
| 98 |
+
{%- endif %}
|
| 99 |
+
{%- endfor %}
|
| 100 |
+
{%- if ns.multi_step_tool %}
|
| 101 |
+
{{- raise_exception('No user query found in messages.') }}
|
| 102 |
+
{%- endif %}
|
| 103 |
+
{%- for message in messages %}
|
| 104 |
+
{%- set content = render_content(message.content, true)|trim %}
|
| 105 |
+
{%- if '<|think_off|>' in content %}
|
| 106 |
+
{%- set ns_flags.enable_thinking = false %}
|
| 107 |
+
{%- set content = content.replace('<|think_off|>', '') %}
|
| 108 |
+
{%- endif %}
|
| 109 |
+
{%- if '<|think_on|>' in content %}
|
| 110 |
+
{%- set ns_flags.enable_thinking = true %}
|
| 111 |
+
{%- set content = content.replace('<|think_on|>', '') %}
|
| 112 |
+
{%- endif %}
|
| 113 |
+
{%- set content = content.strip() %}
|
| 114 |
+
{%- if message.role == "system" or message.role == "developer" %}
|
| 115 |
+
{%- if not loop.first %}
|
| 116 |
+
{{- raise_exception('System message must be at the beginning.') }}
|
| 117 |
+
{%- endif %}
|
| 118 |
+
{%- elif message.role == "user" %}
|
| 119 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 120 |
+
{%- elif message.role == "assistant" %}
|
| 121 |
+
{%- set reasoning_content = '' %}
|
| 122 |
+
{%- if message.reasoning_content is string %}
|
| 123 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 124 |
+
{%- else %}
|
| 125 |
+
{%- set has_think_tag = false %}
|
| 126 |
+
{%- set think_end_token = '</think>' %}
|
| 127 |
+
{%- if '</think>' in content %}
|
| 128 |
+
{%- set has_think_tag = true %}
|
| 129 |
+
{%- elif '</thinking>' in content %}
|
| 130 |
+
{%- set has_think_tag = true %}
|
| 131 |
+
{%- set think_end_token = '</thinking>' %}
|
| 132 |
+
{%- elif '<think>' in content %}
|
| 133 |
+
{%- set reasoning_content = content.split('<think>')[-1].lstrip('\n') %}
|
| 134 |
+
{%- set content = '' %}
|
| 135 |
+
{%- endif %}
|
| 136 |
+
{%- if has_think_tag %}
|
| 137 |
+
{%- set reasoning_content = content.split(think_end_token)[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 138 |
+
{%- set content = content.split(think_end_token)[-1].lstrip('\n') %}
|
| 139 |
+
{%- endif %}
|
| 140 |
+
{%- endif %}
|
| 141 |
+
{%- set reasoning_content = reasoning_content|trim %}
|
| 142 |
+
{%- set show_think = false %}
|
| 143 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 144 |
+
{%- set show_think = true %}
|
| 145 |
+
{%- elif ns_flags.enable_thinking and (preserve_thinking is undefined or preserve_thinking is true) and reasoning_content|length > 0 %}
|
| 146 |
+
{%- set show_think = true %}
|
| 147 |
+
{%- endif %}
|
| 148 |
+
{%- if show_think %}
|
| 149 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
|
| 150 |
+
{%- else %}
|
| 151 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 152 |
+
{%- endif %}
|
| 153 |
+
{%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
|
| 154 |
+
{%- for tool_call in message.tool_calls %}
|
| 155 |
+
{%- if tool_call.function is defined %}
|
| 156 |
+
{%- set tool_call = tool_call.function %}
|
| 157 |
+
{%- endif %}
|
| 158 |
+
{%- if loop.first %}
|
| 159 |
+
{%- if content|trim %}
|
| 160 |
+
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 161 |
+
{%- else %}
|
| 162 |
+
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 163 |
+
{%- endif %}
|
| 164 |
+
{%- else %}
|
| 165 |
+
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 166 |
+
{%- endif %}
|
| 167 |
+
{%- if tool_call.arguments is defined and tool_call.arguments is mapping %}
|
| 168 |
+
{%- if tool_call.arguments|length > 0 %}
|
| 169 |
+
{%- for args_name in tool_call.arguments %}
|
| 170 |
+
{%- set args_value = tool_call.arguments[args_name] %}
|
| 171 |
+
{{- '<parameter=' + args_name + '>\n' }}
|
| 172 |
+
{%- set args_value = args_value | string if args_value is string else args_value | tojson %}
|
| 173 |
+
{{- args_value }}
|
| 174 |
+
{{- '\n</parameter>\n' }}
|
| 175 |
+
{%- endfor %}
|
| 176 |
+
{%- endif %}
|
| 177 |
+
{%- elif tool_call.arguments is defined and tool_call.arguments is string %}
|
| 178 |
+
{%- if tool_call.arguments|trim|length > 0 %}
|
| 179 |
+
{{- tool_call.arguments }}
|
| 180 |
+
{{- '\n' }}
|
| 181 |
+
{%- endif %}
|
| 182 |
+
{%- endif %}
|
| 183 |
+
{{- '</function>\n</tool_call>' }}
|
| 184 |
+
{%- endfor %}
|
| 185 |
+
{%- endif %}
|
| 186 |
+
{{- '<|im_end|>\n' }}
|
| 187 |
+
{%- elif message.role == "tool" %}
|
| 188 |
+
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
| 189 |
+
{{- '<|im_start|>user' }}
|
| 190 |
+
{%- endif %}
|
| 191 |
+
{{- '\n<tool_response>\n' }}
|
| 192 |
+
{{- content }}
|
| 193 |
+
{{- '\n</tool_response>' }}
|
| 194 |
+
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
| 195 |
+
{{- '<|im_end|>\n' }}
|
| 196 |
+
{%- elif loop.last %}
|
| 197 |
+
{{- '<|im_end|>\n' }}
|
| 198 |
+
{%- endif %}
|
| 199 |
+
{%- else %}
|
| 200 |
+
{{- raise_exception('Unexpected message role.') }}
|
| 201 |
+
{%- endif %}
|
| 202 |
+
{%- endfor %}
|
| 203 |
+
{%- if add_generation_prompt %}
|
| 204 |
+
{{- '<|im_start|>assistant\n' }}
|
| 205 |
+
{%- if ns_flags.enable_thinking is false %}
|
| 206 |
+
{{- '<think>\n\n</think>\n\n' }}
|
| 207 |
+
{%- else %}
|
| 208 |
+
{{- '<think>\n' }}
|
| 209 |
+
{%- endif %}
|
| 210 |
+
{%- endif %}
|