wazimondo StableQuant commited on
Commit
1d1faa1
·
0 Parent(s):

Duplicate from StableQuant/Qwen-Templates-Rebuild-Project

Browse files

Co-authored-by: Mr. Stable Quant <StableQuant@users.noreply.huggingface.co>

.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ base_model:
4
+ - Qwen/Qwen3.6-27B
5
+ - Qwen/Qwen3.6-35B-A3B
6
+ - Qwen/Qwen3.5-27B
7
+ - Qwen/Qwen3.5-35B-A3B
8
+ pipeline_tag: text-generation
9
+ library_name: transformers
10
+ tags:
11
+ - chat-template
12
+ - jinja
13
+ - jinja2
14
+ - qwen3
15
+ - template-fix
16
+ - bugfix
17
+ ---
18
+ ### 23.05 22:00: v1.1.5 is released. Agentic and Tool use works 100% for hours now. Try it out!
19
+ ### If you use mainly agentic coding you can also test the "additional-systemprompt" variant out. It adds minimalistic Systempromt Enhancements to 1.1.5 that should work in every environment and actively enhancing correct coding creation and behaviour.
20
+
21
+ TL:DR Download your fixed and finally working Qwen3.5 and Qwen3.6 jinja chat-template here. V1.1.2 with new tool call error bug fixes and additional edge case enhancements is now released.
22
+
23
+ You can use this template for any Qwen3 model (only Qwen3-Coder has another template format)
24
+ Compatible with Qwen3, Qwen3.5 and Qwen3.6 in any OpenWeight size yet released!
25
+
26
+ #### Update: V1.1.5 is out in the wild now!
27
+ Additional fixes to tool calling have been done, verified correct developer role handling and other improvements.
28
+
29
+
30
+ #### Preserve Thinking:
31
+ To enable preserve-thinking (Agent can remember what he thought about in older turns) pass the following parameter to llama.cpp startup:
32
+ On Linux&MacOS (Windows need slightly different which cant be correctly displayed here...): --chat-template-kwargs '{"preserve_thinking": true}'
33
+
34
+ #### OpenCode:
35
+ If using OpenCode do notice they have an open bug for showing thinking content as plain text. Open Bug since 4 months(https://github.com/anomalyco/opencode/issues/11439). Meanwhile you can use the llama.cpp Server switch to hide thinking completely(but still let the model think) using the additonal llama.cpp parameter:
36
+ --reasoning-format deepseek
37
+
38
+
39
+ # Welcome to the Template Rebuild Project
40
+
41
+ How do we got here:
42
+ Annoyed from various bugs with Qwen3.5 and Qwen 3.6 models tool calling etc. I decided to test many different community variants of Qwen Templates, like probably many others out there.
43
+ At the moment there isnt any completly working chat template for the Qwen3.5 and Qwen3.6 models.
44
+
45
+ Community patches were collected and patched together to made a single final version. But it still contained bugs.
46
+
47
+ In the spirit and effort of froggeric https://huggingface.co/froggeric/Qwen-Fixed-Chat-Templates/
48
+ I want to make this awesome model category useful for any case.
49
+
50
+ So I did intense preresearch about the models nature, collected a huge list of commonly used ai-tools and which requests do they make to LLM-Endpoints.
51
+
52
+ All of this was then fed into a new, from the ground up built version of a chat-template.
53
+
54
+ I believe I did that groundwork, currently I didnt covered all off the over 90 common AI-tools but only like 30, I will continue to enhance the information pile and get 99% tool coverage(100% is probably unnecessary and impossible, considering the fast evolving environment, also many tools use the same calling mechanisms).
55
+
56
+ This will lead to a final version of the file that is useable for any usecase you can think of.
57
+
58
+ Currently a third version is released as v1.1.2 which I would call stable
59
+
60
+ 65 specific tests has been run and actual behaviour also manually verified by me.
61
+
62
+ Please feel free to report any bug you encounter with the template, it will be looked in manually from me and be analyzed and fixed.
63
+
64
+ Have fun!
qwen3_5-6-template_v1.1.5.jinja ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {#- ============================================================================
2
+ Qwen Chat Template v1.1 (Stable Agentic Version + JSON Escaping Fix)
3
+
4
+ FIXES APPLIED IN v1.1:
5
+ 1. Added strict JSON escaping instructions to Section 7 to prevent the LLM
6
+ from generating raw newlines and quotes inside JSON tool arguments.
7
+
8
+ FIXES APPLIED IN v1.0:
9
+ 1. Kept v0.9 Audio support and strict iterable type checks.
10
+ 2. REMOVED URL text injection before vision/audio pads.
11
+ 3. REMOVED Tool Call ID injection (violates Qwen's native JSON schema).
12
+ 4. RE-APPLIED Context Breakout Prevention (tojson on tool responses).
13
+ 5. RE-APPLIED Think-Mode Tool Call Prevention (Instruction in Section 7).
14
+ ============================================================================ -#}
15
+
16
+ {%- macro raise_exception(message) -%}
17
+ {{- '\n[ERROR: ' ~ message ~ ']' -}}
18
+ {%- endmacro -%}
19
+
20
+ {#- ===== SECTION 1A: MACRO render_content ===== -#}
21
+ {%- macro render_content(content, count_vision=false, is_system_content=false) -%}
22
+ {%- if is_system_content and content is iterable and content is not mapping and content is not string and content is not none -%}
23
+ {%- for item in content -%}
24
+ {%- if item.type == 'image' or 'image' in item or 'image_url' in item -%}
25
+ {{- raise_exception('System message cannot contain images.') -}}
26
+ {%- endif -%}
27
+ {%- if item.type == 'video' or 'video' in item -%}
28
+ {{- raise_exception('System message cannot contain videos.') -}}
29
+ {%- endif -%}
30
+ {%- endfor -%}
31
+ {%- endif -%}
32
+
33
+ {%- if content is none or content is defined == false -%}
34
+ {{- '' -}}
35
+ {%- elif content is string -%}
36
+ {{- content -}}
37
+ {%- elif content is not mapping and content is iterable and content is not string -%}
38
+ {%- for item in content -%}
39
+ {%- if item.type == 'image' or 'image' in item or 'image_url' in item -%}
40
+ {%- if count_vision -%}{%- set ns.image_count = ns.image_count + 1 -%}{%- endif -%}
41
+ {%- if add_vision_id is defined and add_vision_id -%}
42
+ {{- 'Picture ' ~ ns.image_count ~ ': ' -}}
43
+ {%- endif -%}
44
+ {{- '<|vision_start|><|image_pad|><|vision_end|>' -}}
45
+ {%- elif item.type == 'audio' or 'audio' in item or 'audio_url' in item -%}
46
+ {%- if add_vision_id is defined and add_vision_id -%}
47
+ {{- 'Audio ' ~ ns.image_count ~ ': ' -}}
48
+ {%- endif -%}
49
+ {{- '<|audio_pad|><|audio_end|>' -}}
50
+ {%- elif item.type == 'video' or 'video' in item -%}
51
+ {%- if count_vision -%}{%- set ns.video_count = ns.video_count + 1 -%}{%- endif -%}
52
+ {%- if add_vision_id is defined and add_vision_id -%}
53
+ {{- 'Video ' ~ ns.video_count ~ ': ' -}}
54
+ {%- endif -%}
55
+ {{- '<|vision_start|><|video_pad|><|vision_end|>' -}}
56
+ {%- elif item.type == 'text' or 'text' in item -%}
57
+ {{- item.text -}}
58
+ {%- else -%}
59
+ {{- raise_exception('Unexpected content type in message content.') -}}
60
+ {%- endif -%}
61
+ {%- endfor -%}
62
+ {%- elif content is not none and content is defined -%}
63
+ {{- raise_exception('Unexpected content type.') -}}
64
+ {%- endif -%}
65
+ {%- endmacro -%}
66
+
67
+ {#- ===== SECTION 1B: MACRO detect_tool_error ===== -#}
68
+ {%- macro detect_tool_error(content) -%}
69
+ {%- set content = content if content is string else '' -%}
70
+ {%- set content_lower = content | lower -%}
71
+ {%- set content_length = content | length -%}
72
+
73
+ {%- if content_length < 500
74
+ and '$ ' not in content
75
+ and 'took ' not in content_lower
76
+ and ('"error":' in content_lower or 'error:' in content_lower
77
+ or 'exception:' in content_lower or 'traceback' in content_lower
78
+ or 'command not found' in content_lower or 'invalid syntax' in content_lower
79
+ or 'failed to' in content_lower or 'permission denied' in content_lower) -%}
80
+ {%- set ns.last_tool_failed = true -%}
81
+ {%- set ns.consecutive_failures = ns.consecutive_failures + 1 -%}
82
+ {%- else -%}
83
+ {%- set ns.last_tool_failed = false -%}
84
+ {%- set ns.consecutive_failures = 0 -%}
85
+ {%- endif -%}
86
+ {%- endmacro -%}
87
+
88
+ {#- ===== SECTION 2: NAMESPACE INITIALISATION ===== -#}
89
+ {%- set ns = namespace(
90
+ enable_thinking=true,
91
+ preserve_thinking=true,
92
+ image_count=0,
93
+ video_count=0,
94
+ consecutive_failures=0,
95
+ last_tool_failed=false
96
+ ) -%}
97
+
98
+ {%- if enable_thinking is defined -%}
99
+ {%- if enable_thinking -%}
100
+ {%- set ns.enable_thinking = true -%}
101
+ {%- else -%}
102
+ {%- set ns.enable_thinking = false -%}
103
+ {%- endif -%}
104
+ {%- endif -%}
105
+
106
+ {%- if preserve_thinking is defined -%}
107
+ {%- if not preserve_thinking -%}
108
+ {%- set ns.enable_thinking = false -%}
109
+ {%- set ns.preserve_thinking = false -%}
110
+ {%- else -%}
111
+ {%- set ns.preserve_thinking = true -%}
112
+ {%- endif -%}
113
+ {%- endif -%}
114
+
115
+ {#- ===== SECTION 3: PRE-SCAN ===== -#}
116
+ {%- for i in range(messages | length) -%}
117
+ {%- set _msg = messages[i] -%}
118
+ {%- if _msg.role == 'user' -%}
119
+ {%- set _u = _msg.content if _msg.content is string else '' -%}
120
+ {%- if _u.rstrip().endswith('/no_think') -%}
121
+ {%- set ns.enable_thinking = false -%}
122
+ {%- elif _u.rstrip().endswith('/think') -%}
123
+ {%- set ns.enable_thinking = true -%}
124
+ {%- endif -%}
125
+ {%- elif _msg.role == 'system' or _msg.role == 'developer' -%}
126
+ {%- set _s = _msg.content if _msg.content is string else '' -%}
127
+ {%- if '<|think_off|>' in _s -%}
128
+ {%- set ns.enable_thinking = false -%}
129
+ {%- elif '<|think_on|>' in _s -%}
130
+ {%- set ns.enable_thinking = true -%}
131
+ {%- endif -%}
132
+ {%- endif -%}
133
+ {%- endfor -%}
134
+
135
+ {#- ===== SECTION 4: VALIDATE MESSAGES ===== -#}
136
+ {%- if not messages -%}
137
+ {{- raise_exception('No messages provided.') -}}
138
+ {%- endif -%}
139
+
140
+ {#- ===== SECTION 5: COLLECT SYSTEM CONTENT ===== -#}
141
+ {%- set ns_sys = namespace(content='') -%}
142
+ {%- for msg in messages -%}
143
+ {%- if msg.role == 'system' or msg.role == 'developer' -%}
144
+ {%- set _c = render_content(msg.content | default(''), false, true) | trim -%}
145
+ {%- set _c = _c | replace('<|think_off|>', '') | replace('<|think_on|>', '') | trim -%}
146
+ {%- if _c -%}
147
+ {%- if ns_sys.content == '' -%}
148
+ {%- set ns_sys.content = _c -%}
149
+ {%- else -%}
150
+ {%- set ns_sys.content = ns_sys.content + '\n\n' + _c -%}
151
+ {%- endif -%}
152
+ {%- endif -%}
153
+ {%- endif -%}
154
+ {%- endfor -%}
155
+
156
+ {#- ===== SECTION 6: BUILD TOOLS LIST ===== -#}
157
+ {%- set _has_tools = tools is defined and tools -%}
158
+ {%- if _has_tools -%}
159
+ {%- set ns_tb = namespace(list=[]) -%}
160
+ {%- for tool in tools -%}
161
+ {%- if tool.function is defined -%}
162
+ {%- set ns_tb.list = ns_tb.list + [tool] -%}
163
+ {%- else -%}
164
+ {%- set ns_tb.list = ns_tb.list + [{"type": "function", "function": tool}] -%}
165
+ {%- endif -%}
166
+ {%- endfor -%}
167
+ {%- endif -%}
168
+
169
+ {#- ===== SECTION 7: OUTPUT SYSTEM TURN ===== -#}
170
+ {%- if ns_sys.content or _has_tools -%}
171
+ {{- '<|im_start|>system\n' -}}
172
+ {%- if ns_sys.content -%}
173
+ {{- ns_sys.content -}}
174
+ {%- if _has_tools -%}{{- '\n\n' -}}{%- endif -%}
175
+ {%- endif -%}
176
+ {%- if _has_tools -%}
177
+ {{- '# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>\n' -}}
178
+ {%- for tool in ns_tb.list -%}
179
+ {{- tool | tojson -}}
180
+ {%- if not loop.last -%}{{- '\n' -}}{%- endif -%}
181
+ {%- endfor -%}
182
+ {#- FIX v1.1: Added JSON strict escaping warnings to critical instructions -#}
183
+ {{- '\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{"name": <function-name>, "arguments": <args-json-object>}\n</tool_call>\n\nCRITICAL INSTRUCTIONS:\n1. You MUST NOT output <tool_call> tags while inside <think> tags. Complete your reasoning and output </think> FIRST, then initiate your tool calls.\n2. When passing multi-line code or text containing quotes into a JSON argument, you MUST strictly escape all newlines as \\n and quotes as \\". Failure to escape will invalidate the JSON and crash the system.' -}}
184
+ {%- endif -%}
185
+ {{- '<|im_end|>\n' -}}
186
+ {%- endif -%}
187
+
188
+ {#- ===== SECTION 8: MAIN MESSAGE LOOP ===== -#}
189
+ {%- for message in messages -%}
190
+
191
+ {%- if message.role == 'system' or message.role == 'developer' -%}
192
+
193
+ {%- elif message.role == 'user' -%}
194
+ {%- set _uc = render_content(message.content | default(''), true, false) -%}
195
+ {{- '<|im_start|>user\n' + _uc + '<|im_end|>\n' -}}
196
+
197
+ {%- elif message.role == 'assistant' -%}
198
+ {%- if message.content is defined and message.content is string -%}
199
+ {%- set _ac = message.content -%}
200
+ {%- elif message.content is defined and message.content is iterable and message.content is not mapping and message.content is not string -%}
201
+ {%- set _ac = render_content(message.content, false, false) -%}
202
+ {%- else -%}
203
+ {%- set _ac = '' -%}
204
+ {%- endif -%}
205
+
206
+ {%- if message.reasoning_content is defined and message.reasoning_content is string
207
+ and message.reasoning_content | trim
208
+ and '<think>' not in _ac -%}
209
+ {%- set _ac = '<think>\n' + message.reasoning_content | trim + '\n</think>\n\n' + _ac -%}
210
+ {%- endif -%}
211
+
212
+ {%- set _tc = message.tool_calls if message.tool_calls is defined and message.tool_calls is iterable and message.tool_calls is not string else [] -%}
213
+
214
+ {%- if _tc and '<tool_call>' in _ac -%}
215
+ {%- set _ac = _ac.split('<tool_call>')[0] | trim -%}
216
+ {%- endif -%}
217
+
218
+ {%- set _show_think = false -%}
219
+ {%- if _tc -%}
220
+ {%- set _show_think = true -%}
221
+ {%- elif ns.preserve_thinking -%}
222
+ {%- set _show_think = true -%}
223
+ {%- elif loop.last -%}
224
+ {%- set _show_think = ns.enable_thinking -%}
225
+ {%- endif -%}
226
+
227
+ {%- if not _show_think -%}
228
+ {%- set _think_end = '' -%}
229
+ {%- if '</think>' in _ac -%}
230
+ {%- set _think_end = '</think>' -%}
231
+ {%- elif '</thinking>' in _ac -%}
232
+ {%- set _think_end = '</thinking>' -%}
233
+ {%- elif '</ think>' in _ac -%}
234
+ {%- set _think_end = '</ think>' -%}
235
+ {%- elif '</think >' in _ac -%}
236
+ {%- set _think_end = '</think >' -%}
237
+ {%- endif -%}
238
+ {%- if _think_end -%}
239
+ {%- set _ac = _ac.split(_think_end)[-1].lstrip('\n') -%}
240
+ {%- endif -%}
241
+ {%- elif not _tc and loop.last and '<think>' not in _ac and not ns.enable_thinking -%}
242
+ {%- set _ac = '<think>\n\n</think>\n\n' + _ac -%}
243
+ {%- endif -%}
244
+
245
+ {{- '<|im_start|>assistant\n' -}}
246
+ {%- if _ac -%}
247
+ {{- _ac -}}
248
+ {%- if _tc -%}{{- '\n' -}}{%- endif -%}
249
+ {%- endif -%}
250
+
251
+ {%- if _tc -%}
252
+ {%- for tc in _tc -%}
253
+ {{- '<tool_call>\n' -}}
254
+ {{- '{"name": ' -}}{{- tc.function.name | tojson -}}
255
+ {%- if tc.function.arguments is string -%}
256
+ {{- ', "arguments": ' + tc.function.arguments -}}
257
+ {%- else -%}
258
+ {{- ', "arguments": ' -}}{{- tc.function.arguments | tojson -}}
259
+ {%- endif -%}
260
+ {{- '}' -}}
261
+ {%- if not loop.last -%}
262
+ {{- '\n</tool_call>\n' -}}
263
+ {%- else -%}
264
+ {{- '\n</tool_call>' -}}
265
+ {%- endif -%}
266
+ {%- endfor -%}
267
+ {%- endif -%}
268
+ {{- '<|im_end|>\n' -}}
269
+
270
+ {#- 8d: Tool results -#}
271
+ {%- elif message.role == 'tool' -%}
272
+ {%- set _prev_role = messages[loop.index0 - 1].role if loop.index0 > 0 else '' -%}
273
+ {%- set _next_role = messages[loop.index0 + 1].role if not loop.last else '' -%}
274
+
275
+ {%- set _tool_content = message.content | default('') -%}
276
+ {{- detect_tool_error(_tool_content) -}}
277
+
278
+ {%- if _prev_role != 'tool' -%}
279
+ {{- '<|im_start|>user\n' -}}
280
+ {%- endif -%}
281
+ {{- '<tool_response>\n' -}}
282
+
283
+ {{- '{"result": ' ~ _tool_content | string | tojson ~ '}' -}}
284
+
285
+ {%- if ns.last_tool_failed -%}
286
+ {%- if ns.consecutive_failures >= 2 -%}
287
+ {{- '\n\n[SYSTEM WARNING: ' ~ ns.consecutive_failures ~ ' consecutive tool errors detected. Your previous approach is incorrect.]' -}}
288
+ {%- else -%}
289
+ {{- '\n\n[SYSTEM WARNING: The previous tool call returned an error. Diagnose the failure and retry with corrected arguments.]' -}}
290
+ {%- endif -%}
291
+ {%- endif -%}
292
+
293
+ {%- if _next_role == 'tool' -%}
294
+ {{- '\n</tool_response>\n' -}}
295
+ {%- else -%}
296
+ {{- '\n</tool_response>' -}}
297
+ {{- '<|im_end|>\n' -}}
298
+ {%- endif -%}
299
+
300
+ {%- else -%}
301
+ {{- raise_exception('Unexpected message role: ' + message.role) -}}
302
+ {%- endif -%}
303
+
304
+ {%- endfor -%}
305
+
306
+ {#- ===== SECTION 9: GENERATION PROMPT ===== -#}
307
+ {%- if add_generation_prompt -%}
308
+ {{- '<|im_start|>assistant\n' -}}
309
+ {%- if ns.enable_thinking -%}
310
+ {{- '<think>\n' -}}
311
+ {%- else -%}
312
+ {{- '<think>\n\n</think>\n\n' -}}
313
+ {%- endif -%}
314
+ {%- endif -%}
v1.1.5-variant1_added_agentic-coding-prompt-enhancements.jinja ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {#- ============================================================================
2
+ Qwen Chat Template v1.2 (Ultimate Agentic Version)
3
+
4
+ FIXES APPLIED IN v1.2:
5
+ 1. Added comprehensive Agentic rules to Section 7 (NO LAZINESS, BREAK LOOPS,
6
+ STRICT TOOLS, VERIFY) to prevent common agentic loops and lazy coding
7
+ behaviors in frameworks like Hermes and OpenHands.
8
+
9
+ FIXES APPLIED IN v1.1:
10
+ 1. Added strict JSON escaping instructions to prevent Context Breakouts during
11
+ outbound code generation.
12
+
13
+ FIXES APPLIED IN v1.0:
14
+ 1. Kept v0.9 Audio support and strict iterable type checks.
15
+ 2. REMOVED URL text injection before vision/audio pads.
16
+ 3. REMOVED Tool Call ID injection (violates Qwen's native JSON schema).
17
+ 4. RE-APPLIED Context Breakout Prevention (tojson on tool responses).
18
+ 5. RE-APPLIED Think-Mode Tool Call Prevention (Instruction in Section 7).
19
+ ============================================================================ -#}
20
+
21
+ {%- macro raise_exception(message) -%}
22
+ {{- '\n[ERROR: ' ~ message ~ ']' -}}
23
+ {%- endmacro -%}
24
+
25
+ {#- ===== SECTION 1A: MACRO render_content ===== -#}
26
+ {%- macro render_content(content, count_vision=false, is_system_content=false) -%}
27
+ {%- if is_system_content and content is iterable and content is not mapping and content is not string and content is not none -%}
28
+ {%- for item in content -%}
29
+ {%- if item.type == 'image' or 'image' in item or 'image_url' in item -%}
30
+ {{- raise_exception('System message cannot contain images.') -}}
31
+ {%- endif -%}
32
+ {%- if item.type == 'video' or 'video' in item -%}
33
+ {{- raise_exception('System message cannot contain videos.') -}}
34
+ {%- endif -%}
35
+ {%- endfor -%}
36
+ {%- endif -%}
37
+
38
+ {%- if content is none or content is defined == false -%}
39
+ {{- '' -}}
40
+ {%- elif content is string -%}
41
+ {{- content -}}
42
+ {%- elif content is not mapping and content is iterable and content is not string -%}
43
+ {%- for item in content -%}
44
+ {%- if item.type == 'image' or 'image' in item or 'image_url' in item -%}
45
+ {%- if count_vision -%}{%- set ns.image_count = ns.image_count + 1 -%}{%- endif -%}
46
+ {%- if add_vision_id is defined and add_vision_id -%}
47
+ {{- 'Picture ' ~ ns.image_count ~ ': ' -}}
48
+ {%- endif -%}
49
+ {{- '<|vision_start|><|image_pad|><|vision_end|>' -}}
50
+ {%- elif item.type == 'audio' or 'audio' in item or 'audio_url' in item -%}
51
+ {%- if add_vision_id is defined and add_vision_id -%}
52
+ {{- 'Audio ' ~ ns.image_count ~ ': ' -}}
53
+ {%- endif -%}
54
+ {{- '<|audio_pad|><|audio_end|>' -}}
55
+ {%- elif item.type == 'video' or 'video' in item -%}
56
+ {%- if count_vision -%}{%- set ns.video_count = ns.video_count + 1 -%}{%- endif -%}
57
+ {%- if add_vision_id is defined and add_vision_id -%}
58
+ {{- 'Video ' ~ ns.video_count ~ ': ' -}}
59
+ {%- endif -%}
60
+ {{- '<|vision_start|><|video_pad|><|vision_end|>' -}}
61
+ {%- elif item.type == 'text' or 'text' in item -%}
62
+ {{- item.text -}}
63
+ {%- else -%}
64
+ {{- raise_exception('Unexpected content type in message content.') -}}
65
+ {%- endif -%}
66
+ {%- endfor -%}
67
+ {%- elif content is not none and content is defined -%}
68
+ {{- raise_exception('Unexpected content type.') -}}
69
+ {%- endif -%}
70
+ {%- endmacro -%}
71
+
72
+ {#- ===== SECTION 1B: MACRO detect_tool_error ===== -#}
73
+ {%- macro detect_tool_error(content) -%}
74
+ {%- set content = content if content is string else '' -%}
75
+ {%- set content_lower = content | lower -%}
76
+ {%- set content_length = content | length -%}
77
+
78
+ {%- if content_length < 500
79
+ and '$ ' not in content
80
+ and 'took ' not in content_lower
81
+ and ('"error":' in content_lower or 'error:' in content_lower
82
+ or 'exception:' in content_lower or 'traceback' in content_lower
83
+ or 'command not found' in content_lower or 'invalid syntax' in content_lower
84
+ or 'failed to' in content_lower or 'permission denied' in content_lower) -%}
85
+ {%- set ns.last_tool_failed = true -%}
86
+ {%- set ns.consecutive_failures = ns.consecutive_failures + 1 -%}
87
+ {%- else -%}
88
+ {%- set ns.last_tool_failed = false -%}
89
+ {%- set ns.consecutive_failures = 0 -%}
90
+ {%- endif -%}
91
+ {%- endmacro -%}
92
+
93
+ {#- ===== SECTION 2: NAMESPACE INITIALISATION ===== -#}
94
+ {%- set ns = namespace(
95
+ enable_thinking=true,
96
+ preserve_thinking=true,
97
+ image_count=0,
98
+ video_count=0,
99
+ consecutive_failures=0,
100
+ last_tool_failed=false
101
+ ) -%}
102
+
103
+ {%- if enable_thinking is defined -%}
104
+ {%- if enable_thinking -%}
105
+ {%- set ns.enable_thinking = true -%}
106
+ {%- else -%}
107
+ {%- set ns.enable_thinking = false -%}
108
+ {%- endif -%}
109
+ {%- endif -%}
110
+
111
+ {%- if preserve_thinking is defined -%}
112
+ {%- if not preserve_thinking -%}
113
+ {%- set ns.enable_thinking = false -%}
114
+ {%- set ns.preserve_thinking = false -%}
115
+ {%- else -%}
116
+ {%- set ns.preserve_thinking = true -%}
117
+ {%- endif -%}
118
+ {%- endif -%}
119
+
120
+ {#- ===== SECTION 3: PRE-SCAN ===== -#}
121
+ {%- for i in range(messages | length) -%}
122
+ {%- set _msg = messages[i] -%}
123
+ {%- if _msg.role == 'user' -%}
124
+ {%- set _u = _msg.content if _msg.content is string else '' -%}
125
+ {%- if _u.rstrip().endswith('/no_think') -%}
126
+ {%- set ns.enable_thinking = false -%}
127
+ {%- elif _u.rstrip().endswith('/think') -%}
128
+ {%- set ns.enable_thinking = true -%}
129
+ {%- endif -%}
130
+ {%- elif _msg.role == 'system' or _msg.role == 'developer' -%}
131
+ {%- set _s = _msg.content if _msg.content is string else '' -%}
132
+ {%- if '<|think_off|>' in _s -%}
133
+ {%- set ns.enable_thinking = false -%}
134
+ {%- elif '<|think_on|>' in _s -%}
135
+ {%- set ns.enable_thinking = true -%}
136
+ {%- endif -%}
137
+ {%- endif -%}
138
+ {%- endfor -%}
139
+
140
+ {#- ===== SECTION 4: VALIDATE MESSAGES ===== -#}
141
+ {%- if not messages -%}
142
+ {{- raise_exception('No messages provided.') -}}
143
+ {%- endif -%}
144
+
145
+ {#- ===== SECTION 5: COLLECT SYSTEM CONTENT ===== -#}
146
+ {%- set ns_sys = namespace(content='') -%}
147
+ {%- for msg in messages -%}
148
+ {%- if msg.role == 'system' or msg.role == 'developer' -%}
149
+ {%- set _c = render_content(msg.content | default(''), false, true) | trim -%}
150
+ {%- set _c = _c | replace('<|think_off|>', '') | replace('<|think_on|>', '') | trim -%}
151
+ {%- if _c -%}
152
+ {%- if ns_sys.content == '' -%}
153
+ {%- set ns_sys.content = _c -%}
154
+ {%- else -%}
155
+ {%- set ns_sys.content = ns_sys.content + '\n\n' + _c -%}
156
+ {%- endif -%}
157
+ {%- endif -%}
158
+ {%- endif -%}
159
+ {%- endfor -%}
160
+
161
+ {#- ===== SECTION 6: BUILD TOOLS LIST ===== -#}
162
+ {%- set _has_tools = tools is defined and tools -%}
163
+ {%- if _has_tools -%}
164
+ {%- set ns_tb = namespace(list=[]) -%}
165
+ {%- for tool in tools -%}
166
+ {%- if tool.function is defined -%}
167
+ {%- set ns_tb.list = ns_tb.list + [tool] -%}
168
+ {%- else -%}
169
+ {%- set ns_tb.list = ns_tb.list + [{"type": "function", "function": tool}] -%}
170
+ {%- endif -%}
171
+ {%- endfor -%}
172
+ {%- endif -%}
173
+
174
+ {#- ===== SECTION 7: OUTPUT SYSTEM TURN ===== -#}
175
+ {%- if ns_sys.content or _has_tools -%}
176
+ {{- '<|im_start|>system\n' -}}
177
+ {%- if ns_sys.content -%}
178
+ {{- ns_sys.content -}}
179
+ {%- if _has_tools -%}{{- '\n\n' -}}{%- endif -%}
180
+ {%- endif -%}
181
+ {%- if _has_tools -%}
182
+ {{- '# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>\n' -}}
183
+ {%- for tool in ns_tb.list -%}
184
+ {{- tool | tojson -}}
185
+ {%- if not loop.last -%}{{- '\n' -}}{%- endif -%}
186
+ {%- endfor -%}
187
+ {#- FIX v1.2: Added comprehensive agentic rules to the critical instructions -#}
188
+ {{- '\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{"name": <function-name>, "arguments": <args-json-object>}\n</tool_call>\n\nCRITICAL INSTRUCTIONS:\n1. You MUST NOT output <tool_call> tags while inside <think> tags. Complete your reasoning and output </think> FIRST, then initiate your tool calls.\n2. When passing multi-line code or text containing quotes into a JSON argument, you MUST strictly escape all newlines as \\n and quotes as \\". Failure to escape will invalidate the JSON and crash the system.\n3. NO LAZINESS: Never use placeholders like "// existing code" or "/* rest of function */". You must output the complete, runnable code block required by the tool.\n4. BREAK LOOPS: If a tool call fails with an error, DO NOT repeat the exact same call. Analyze the error, change your approach, use alternative tools, or ask the user for clarification.\n5. STRICT TOOLS: Only use the exact function names listed in the <tools> block. Do not invent new tools or use colon-separated namespaces unless explicitly provided.\n6. VERIFY: After making file changes, always run tests or use terminal commands to verify your code compiles and works before responding to the user.' -}}
189
+ {%- endif -%}
190
+ {{- '<|im_end|>\n' -}}
191
+ {%- endif -%}
192
+
193
+ {#- ===== SECTION 8: MAIN MESSAGE LOOP ===== -#}
194
+ {%- for message in messages -%}
195
+
196
+ {%- if message.role == 'system' or message.role == 'developer' -%}
197
+
198
+ {%- elif message.role == 'user' -%}
199
+ {%- set _uc = render_content(message.content | default(''), true, false) -%}
200
+ {{- '<|im_start|>user\n' + _uc + '<|im_end|>\n' -}}
201
+
202
+ {%- elif message.role == 'assistant' -%}
203
+ {%- if message.content is defined and message.content is string -%}
204
+ {%- set _ac = message.content -%}
205
+ {%- elif message.content is defined and message.content is iterable and message.content is not mapping and message.content is not string -%}
206
+ {%- set _ac = render_content(message.content, false, false) -%}
207
+ {%- else -%}
208
+ {%- set _ac = '' -%}
209
+ {%- endif -%}
210
+
211
+ {%- if message.reasoning_content is defined and message.reasoning_content is string
212
+ and message.reasoning_content | trim
213
+ and '<think>' not in _ac -%}
214
+ {%- set _ac = '<think>\n' + message.reasoning_content | trim + '\n</think>\n\n' + _ac -%}
215
+ {%- endif -%}
216
+
217
+ {%- set _tc = message.tool_calls if message.tool_calls is defined and message.tool_calls is iterable and message.tool_calls is not string else [] -%}
218
+
219
+ {%- if _tc and '<tool_call>' in _ac -%}
220
+ {%- set _ac = _ac.split('<tool_call>')[0] | trim -%}
221
+ {%- endif -%}
222
+
223
+ {%- set _show_think = false -%}
224
+ {%- if _tc -%}
225
+ {%- set _show_think = true -%}
226
+ {%- elif ns.preserve_thinking -%}
227
+ {%- set _show_think = true -%}
228
+ {%- elif loop.last -%}
229
+ {%- set _show_think = ns.enable_thinking -%}
230
+ {%- endif -%}
231
+
232
+ {%- if not _show_think -%}
233
+ {%- set _think_end = '' -%}
234
+ {%- if '</think>' in _ac -%}
235
+ {%- set _think_end = '</think>' -%}
236
+ {%- elif '</thinking>' in _ac -%}
237
+ {%- set _think_end = '</thinking>' -%}
238
+ {%- elif '</ think>' in _ac -%}
239
+ {%- set _think_end = '</ think>' -%}
240
+ {%- elif '</think >' in _ac -%}
241
+ {%- set _think_end = '</think >' -%}
242
+ {%- endif -%}
243
+ {%- if _think_end -%}
244
+ {%- set _ac = _ac.split(_think_end)[-1].lstrip('\n') -%}
245
+ {%- endif -%}
246
+ {%- elif not _tc and loop.last and '<think>' not in _ac and not ns.enable_thinking -%}
247
+ {%- set _ac = '<think>\n\n</think>\n\n' + _ac -%}
248
+ {%- endif -%}
249
+
250
+ {{- '<|im_start|>assistant\n' -}}
251
+ {%- if _ac -%}
252
+ {{- _ac -}}
253
+ {%- if _tc -%}{{- '\n' -}}{%- endif -%}
254
+ {%- endif -%}
255
+
256
+ {%- if _tc -%}
257
+ {%- for tc in _tc -%}
258
+ {{- '<tool_call>\n' -}}
259
+ {{- '{"name": ' -}}{{- tc.function.name | tojson -}}
260
+ {%- if tc.function.arguments is string -%}
261
+ {{- ', "arguments": ' + tc.function.arguments -}}
262
+ {%- else -%}
263
+ {{- ', "arguments": ' -}}{{- tc.function.arguments | tojson -}}
264
+ {%- endif -%}
265
+ {{- '}' -}}
266
+ {%- if not loop.last -%}
267
+ {{- '\n</tool_call>\n' -}}
268
+ {%- else -%}
269
+ {{- '\n</tool_call>' -}}
270
+ {%- endif -%}
271
+ {%- endfor -%}
272
+ {%- endif -%}
273
+ {{- '<|im_end|>\n' -}}
274
+
275
+ {#- 8d: Tool results -#}
276
+ {%- elif message.role == 'tool' -%}
277
+ {%- set _prev_role = messages[loop.index0 - 1].role if loop.index0 > 0 else '' -%}
278
+ {%- set _next_role = messages[loop.index0 + 1].role if not loop.last else '' -%}
279
+
280
+ {%- set _tool_content = message.content | default('') -%}
281
+ {{- detect_tool_error(_tool_content) -}}
282
+
283
+ {%- if _prev_role != 'tool' -%}
284
+ {{- '<|im_start|>user\n' -}}
285
+ {%- endif -%}
286
+ {{- '<tool_response>\n' -}}
287
+
288
+ {{- '{"result": ' ~ _tool_content | string | tojson ~ '}' -}}
289
+
290
+ {%- if ns.last_tool_failed -%}
291
+ {%- if ns.consecutive_failures >= 2 -%}
292
+ {{- '\n\n[SYSTEM WARNING: ' ~ ns.consecutive_failures ~ ' consecutive tool errors detected. Your previous approach is incorrect.]' -}}
293
+ {%- else -%}
294
+ {{- '\n\n[SYSTEM WARNING: The previous tool call returned an error. Diagnose the failure and retry with corrected arguments.]' -}}
295
+ {%- endif -%}
296
+ {%- endif -%}
297
+
298
+ {%- if _next_role == 'tool' -%}
299
+ {{- '\n</tool_response>\n' -}}
300
+ {%- else -%}
301
+ {{- '\n</tool_response>' -}}
302
+ {{- '<|im_end|>\n' -}}
303
+ {%- endif -%}
304
+
305
+ {%- else -%}
306
+ {{- raise_exception('Unexpected message role: ' + message.role) -}}
307
+ {%- endif -%}
308
+
309
+ {%- endfor -%}
310
+
311
+ {#- ===== SECTION 9: GENERATION PROMPT ===== -#}
312
+ {%- if add_generation_prompt -%}
313
+ {{- '<|im_start|>assistant\n' -}}
314
+ {%- if ns.enable_thinking -%}
315
+ {{- '<think>\n' -}}
316
+ {%- else -%}
317
+ {{- '<think>\n\n</think>\n\n' -}}
318
+ {%- endif -%}
319
+ {%- endif -%}