StableQuant commited on
Commit
093dae2
·
verified ·
1 Parent(s): 32d33fa

Upload qwen3-5_6-template_v1.1.jinja

Browse files
Files changed (1) hide show
  1. qwen3-5_6-template_v1.1.jinja +290 -0
qwen3-5_6-template_v1.1.jinja ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {#- ===== SECTION 1: MACRO render_content =====
2
+ Handles string, list (image/video/text items), or None/undefined.
3
+ count_vision=true: increments ns.image_count / ns.video_count.
4
+ -#}
5
+ {%- macro render_content(content, count_vision=false) -%}
6
+ {%- if content is string -%}
7
+ {{- content -}}
8
+ {%- elif content is iterable and content is not mapping -%}
9
+ {%- for item in content -%}
10
+ {%- if item.type == 'image' or 'image' in item or 'image_url' in item -%}
11
+ {%- if count_vision -%}{%- set ns.image_count = ns.image_count + 1 -%}{%- endif -%}
12
+ {%- if add_vision_id is defined and add_vision_id -%}
13
+ {{- 'Picture ' ~ ns.image_count ~ ': ' -}}
14
+ {%- endif -%}
15
+ {{- '<|vision_start|><|image_pad|><|vision_end|>' -}}
16
+ {%- elif item.type == 'video' or 'video' in item -%}
17
+ {%- if count_vision -%}{%- set ns.video_count = ns.video_count + 1 -%}{%- endif -%}
18
+ {%- if add_vision_id is defined and add_vision_id -%}
19
+ {{- 'Video ' ~ ns.video_count ~ ': ' -}}
20
+ {%- endif -%}
21
+ {{- '<|vision_start|><|video_pad|><|vision_end|>' -}}
22
+ {%- elif item.type == 'text' or 'text' in item -%}
23
+ {{- item.text -}}
24
+ {%- endif -%}
25
+ {%- endfor -%}
26
+ {%- endif -%}
27
+ {%- endmacro -%}
28
+
29
+ {#- ===== SECTION 2: NAMESPACE INITIALISATION =====
30
+ Single ns object for all mutable state.
31
+ enable_thinking default=true (BUG-003 fix)
32
+ preserve_thinking default=true: when false, suppresses think-block output in
33
+ generation prompt and overrides enable_thinking to false.
34
+ Passed via --chat-template-kwargs {"preserve_thinking":false}.
35
+ -#}
36
+ {%- set ns = namespace(
37
+ enable_thinking=true,
38
+ image_count=0,
39
+ video_count=0
40
+ ) -%}
41
+
42
+ {#- Resolve enable_thinking kwarg -#}
43
+ {%- if enable_thinking is defined -%}
44
+ {%- if enable_thinking -%}
45
+ {%- set ns.enable_thinking = true -%}
46
+ {%- else -%}
47
+ {%- set ns.enable_thinking = false -%}
48
+ {%- endif -%}
49
+ {%- endif -%}
50
+
51
+ {#- Resolve preserve_thinking kwarg.
52
+ preserve_thinking=false => force non-thinking mode (same as enable_thinking=false).
53
+ preserve_thinking=true => default, no override (thinking controlled by enable_thinking).
54
+ When not defined => default, no override.
55
+ -#}
56
+ {%- if preserve_thinking is defined and not preserve_thinking -%}
57
+ {%- set ns.enable_thinking = false -%}
58
+ {%- endif -%}
59
+
60
+ {#- ===== SECTION 3: PRE-SCAN =====
61
+ Track last /no_think or /think flag in user messages.
62
+ Also scan system messages for <|think_off|> / <|think_on|> markers
63
+ (allows apps to control thinking mode via system prompt injection).
64
+ The model follows the last flag encountered in multi-turn conversations.
65
+ -#}
66
+ {%- for i in range(messages | length) -%}
67
+ {%- set _msg = messages[i] -%}
68
+ {%- if _msg.role == 'user' -%}
69
+ {%- set _u = _msg.content if _msg.content is string else '' -%}
70
+ {%- if _u.rstrip().endswith('/no_think') -%}
71
+ {%- set ns.enable_thinking = false -%}
72
+ {%- elif _u.rstrip().endswith('/think') -%}
73
+ {%- set ns.enable_thinking = true -%}
74
+ {%- endif -%}
75
+ {%- elif _msg.role == 'system' or _msg.role == 'developer' -%}
76
+ {%- set _s = _msg.content if _msg.content is string else '' -%}
77
+ {%- if '<|think_off|>' in _s -%}
78
+ {%- set ns.enable_thinking = false -%}
79
+ {%- elif '<|think_on|>' in _s -%}
80
+ {%- set ns.enable_thinking = true -%}
81
+ {%- endif -%}
82
+ {%- endif -%}
83
+ {%- endfor -%}
84
+
85
+ {#- ===== SECTION 4: COLLECT SYSTEM CONTENT =====
86
+ Merge all system/developer messages with \n\n separator (BUG-004 fix).
87
+ <|think_off|> / <|think_on|> markers are stripped from output.
88
+ -#}
89
+ {%- set ns_sys = namespace(content='') -%}
90
+ {%- for msg in messages -%}
91
+ {%- if msg.role == 'system' or msg.role == 'developer' -%}
92
+ {%- set _c = render_content(msg.content | default('')) | trim -%}
93
+ {%- set _c = _c | replace('<|think_off|>', '') | replace('<|think_on|>', '') | trim -%}
94
+ {%- if _c -%}
95
+ {%- if ns_sys.content == '' -%}
96
+ {%- set ns_sys.content = _c -%}
97
+ {%- else -%}
98
+ {%- set ns_sys.content = ns_sys.content + '\n\n' + _c -%}
99
+ {%- endif -%}
100
+ {%- endif -%}
101
+ {%- endif -%}
102
+ {%- endfor -%}
103
+
104
+ {#- ===== SECTION 5: BUILD TOOLS LIST =====
105
+ Normalise each tool to {"type":"function","function":{...}} format.
106
+ Serialisation happens later at output time (avoids Markup + str escaping bugs).
107
+ -#}
108
+ {%- set _has_tools = tools is defined and tools -%}
109
+ {%- if _has_tools -%}
110
+ {%- set ns_tb = namespace(list=[]) -%}
111
+ {%- for tool in tools -%}
112
+ {%- if tool.function is defined -%}
113
+ {%- set ns_tb.list = ns_tb.list + [tool] -%}
114
+ {%- else -%}
115
+ {%- set ns_tb.list = ns_tb.list + [{"type": "function", "function": tool}] -%}
116
+ {%- endif -%}
117
+ {%- endfor -%}
118
+ {%- endif -%}
119
+
120
+ {#- ===== SECTION 6: OUTPUT SYSTEM TURN =====
121
+ Each fragment output via its own {{ }} block so tojson Markup objects are
122
+ never Python-concatenated with plain strings (would trigger HTML-escaping).
123
+ User system content appears BEFORE the tools block (correct ordering).
124
+ No default system prompt injected.
125
+ -#}
126
+ {%- if ns_sys.content or _has_tools -%}
127
+ {{- '<|im_start|>system\n' -}}
128
+ {%- if ns_sys.content -%}
129
+ {{- ns_sys.content -}}
130
+ {%- if _has_tools -%}{{- '\n\n' -}}{%- endif -%}
131
+ {%- endif -%}
132
+ {%- if _has_tools -%}
133
+ {{- '# 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' -}}
134
+ {%- for tool in ns_tb.list -%}
135
+ {{- tool | tojson -}}
136
+ {%- if not loop.last -%}{{- '\n' -}}{%- endif -%}
137
+ {%- endfor -%}
138
+ {{- '\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>' -}}
139
+ {%- endif -%}
140
+ {{- '<|im_end|>\n' -}}
141
+ {%- endif -%}
142
+
143
+ {#- ===== SECTION 7: MAIN MESSAGE LOOP ===== -#}
144
+ {%- for message in messages -%}
145
+
146
+ {#- 7a: System / Developer — already rendered above, skip -#}
147
+ {%- if message.role == 'system' or message.role == 'developer' -%}
148
+
149
+ {#- 7b: User messages -#}
150
+ {%- elif message.role == 'user' -%}
151
+ {%- set _uc = render_content(message.content | default(''), true) -%}
152
+ {{- '<|im_start|>user\n' + _uc + '<|im_end|>\n' -}}
153
+
154
+ {#- 7c: Assistant messages -#}
155
+ {%- elif message.role == 'assistant' -%}
156
+ {#- Safely extract content as string — guard against absent key (BUG-002 fix).
157
+ Also support message.reasoning_content as an explicit think-block source
158
+ (used by some frameworks that store thinking separately from content). -#}
159
+ {%- if message.content is defined and message.content is string -%}
160
+ {%- set _ac = message.content -%}
161
+ {%- elif message.content is defined and message.content is iterable and message.content is not mapping -%}
162
+ {%- set _ac = render_content(message.content) -%}
163
+ {%- else -%}
164
+ {%- set _ac = '' -%}
165
+ {%- endif -%}
166
+
167
+ {#- Reconstruct content from reasoning_content + content when the framework
168
+ stores thinking separately (e.g. OpenAI-style reasoning_content field).
169
+ Only apply when no think-block already present in _ac. -#}
170
+ {%- if message.reasoning_content is defined and message.reasoning_content is string
171
+ and message.reasoning_content | trim
172
+ and '<think>' not in _ac -%}
173
+ {%- set _ac = '<think>\n' + message.reasoning_content | trim + '\n</think>\n\n' + _ac -%}
174
+ {%- endif -%}
175
+
176
+ {#- Collect tool_calls if present -#}
177
+ {%- set _tc = message.tool_calls if message.tool_calls is defined and message.tool_calls else [] -%}
178
+
179
+ {#- Strip <tool_call> prefix from content when tool_calls also present
180
+ (some frameworks duplicate the data in both fields) -#}
181
+ {%- if _tc and '<tool_call>' in _ac -%}
182
+ {%- set _ac = _ac.split('<tool_call>')[0] | trim -%}
183
+ {%- endif -%}
184
+
185
+ {#- Determine if this is the last-in-history assistant turn.
186
+ When add_generation_prompt=False and this is the last message, think blocks
187
+ must be preserved (and non-thinking prefill applied if needed).
188
+ All other turns have their think blocks stripped. -#}
189
+ {%- set _is_last_hist = loop.last and not (add_generation_prompt | default(false)) -%}
190
+
191
+ {#- Think-block handling (BUG-001 fix + last-turn preservation):
192
+ - Tool-call turns : never strip (think block is part of the tool-call format)
193
+ - Last-history turn : preserve; inject non-thinking prefill when absent
194
+ - Historical turns : strip using fuzzy end-tag matching to handle
195
+ </think>, </thinking>, </ think>, </think > variants -#}
196
+ {%- if not _tc -%}
197
+ {%- if _is_last_hist -%}
198
+ {%- if '<think>' not in _ac and not ns.enable_thinking -%}
199
+ {%- set _ac = '<think>\n\n</think>\n\n' + _ac -%}
200
+ {%- endif -%}
201
+ {%- else -%}
202
+ {#- Fuzzy end-tag detection for historical turn stripping -#}
203
+ {%- set _think_end = '' -%}
204
+ {%- if '</think>' in _ac -%}
205
+ {%- set _think_end = '</think>' -%}
206
+ {%- elif '</thinking>' in _ac -%}
207
+ {%- set _think_end = '</thinking>' -%}
208
+ {%- elif '</ think>' in _ac -%}
209
+ {%- set _think_end = '</ think>' -%}
210
+ {%- elif '</think >' in _ac -%}
211
+ {%- set _think_end = '</think >' -%}
212
+ {%- endif -%}
213
+ {%- if _think_end -%}
214
+ {%- set _ac = _ac.split(_think_end)[-1].lstrip('\n') -%}
215
+ {%- endif -%}
216
+ {%- endif -%}
217
+ {%- endif -%}
218
+
219
+ {#- Emit the assistant turn -#}
220
+ {{- '<|im_start|>assistant\n' -}}
221
+ {%- if _ac -%}
222
+ {{- _ac -}}
223
+ {%- if _tc -%}{{- '\n' -}}{%- endif -%}
224
+ {%- endif -%}
225
+
226
+ {#- Render tool calls in Hermes format (BUG-006 fix: arguments as-is or tojson).
227
+ Each value output via its own {{ }} block — never concatenated with plain strings
228
+ in Python, which would trigger Markup HTML-escaping (BUG-003/markup fix). -#}
229
+ {%- if _tc -%}
230
+ {%- for tc in _tc -%}
231
+ {{- '<tool_call>\n' -}}
232
+ {{- '{"name": ' -}}{{- tc.function.name | tojson -}}
233
+ {%- if tc.function.arguments is string -%}
234
+ {{- ', "arguments": ' + tc.function.arguments -}}
235
+ {%- else -%}
236
+ {{- ', "arguments": ' -}}{{- tc.function.arguments | tojson -}}
237
+ {%- endif -%}
238
+ {{- '}' -}}
239
+ {%- if not loop.last -%}
240
+ {{- '\n</tool_call>\n' -}}
241
+ {%- else -%}
242
+ {{- '\n</tool_call>' -}}
243
+ {%- endif -%}
244
+ {%- endfor -%}
245
+ {%- endif -%}
246
+ {{- '<|im_end|>\n' -}}
247
+
248
+ {#- 7d: Tool results — group consecutive tool messages into one user turn -#}
249
+ {%- elif message.role == 'tool' -%}
250
+ {%- set _prev_role = messages[loop.index0 - 1].role if loop.index0 > 0 else '' -%}
251
+ {%- set _next_role = messages[loop.index0 + 1].role if not loop.last else '' -%}
252
+ {%- if _prev_role != 'tool' -%}
253
+ {{- '<|im_start|>user\n' -}}
254
+ {%- endif -%}
255
+ {{- '<tool_response>\n' -}}
256
+ {{- message.content | default('') -}}
257
+ {%- if _next_role == 'tool' -%}
258
+ {{- '\n</tool_response>\n' -}}
259
+ {%- else -%}
260
+ {{- '\n</tool_response>' -}}
261
+ {{- '<|im_end|>\n' -}}
262
+ {%- endif -%}
263
+
264
+ {#- 7e: Unknown role -#}
265
+ {%- else -%}
266
+ {{- raise_exception('Unexpected message role: ' + message.role) -}}
267
+ {%- endif -%}
268
+
269
+ {%- endfor -%}
270
+
271
+ {#- ===== SECTION 8: GENERATION PROMPT =====
272
+ enable_thinking=True → open <think>\n prefill so llama.cpp reasoning-budget
273
+ and other inference engines can hook into the think-stream.
274
+ The model continues generating inside the open block.
275
+ enable_thinking=False → exact non-thinking prefill: <think>\n\n</think>\n\n
276
+ (19-char closed block, BUG-005 fix)
277
+
278
+ NOTE: The <think>\n opener is EPHEMERAL — it lives only in the generation
279
+ prompt, never in chat history. Historical think-block stripping (BUG-001)
280
+ is handled in Section 7c and is entirely unaffected by this change.
281
+ No context poisoning risk.
282
+ -#}
283
+ {%- if add_generation_prompt -%}
284
+ {{- '<|im_start|>assistant\n' -}}
285
+ {%- if ns.enable_thinking -%}
286
+ {{- '<think>\n' -}}
287
+ {%- else -%}
288
+ {{- '<think>\n\n</think>\n\n' -}}
289
+ {%- endif -%}
290
+ {%- endif -%}