danielhanchen commited on
Commit
ed52522
·
verified ·
1 Parent(s): 0888243

Improved chat template

Browse files
Files changed (2) hide show
  1. chat_template.jinja +152 -16
  2. tokenizer_config.json +2 -2
chat_template.jinja CHANGED
@@ -1,3 +1,4 @@
 
1
  {%- if not add_generation_prompt is defined -%}
2
  {%- set add_generation_prompt = false -%}
3
  {%- endif -%}
@@ -8,11 +9,28 @@
8
  {%- set thinking = false -%}
9
  {%- endif -%}
10
  {%- endif -%}
 
 
 
11
  {%- set dsml_token = '|DSML|' -%}
12
  {%- set thinking_start_token = '<think>' -%}
13
  {%- set thinking_end_token = '</think>' -%}
 
14
  {%- set tools_header = '## Tools\n\nYou have access to a set of tools to help answer the user\'s question. You can invoke tools by writing a "<' + dsml_token + 'tool_calls>" block like the following:\n\n<' + dsml_token + 'tool_calls>\n<' + dsml_token + 'invoke name="$TOOL_NAME">\n<' + dsml_token + 'parameter name="$PARAMETER_NAME" string="true|false">$PARAMETER_VALUE</' + dsml_token + 'parameter>\n...\n</' + dsml_token + 'invoke>\n<' + dsml_token + 'invoke name="$TOOL_NAME2">\n...\n</' + dsml_token + 'invoke>\n</' + dsml_token + 'tool_calls>\n\nString parameters should be specified as is and set `string="true"`. For all other types (numbers, booleans, arrays, objects), pass the value in JSON format and set `string="false"`.\n\nIf thinking_mode is enabled (triggered by ' + thinking_start_token + '), you MUST output your complete reasoning inside ' + thinking_start_token + '...' + thinking_end_token + ' BEFORE any tool calls or final response.\n\nOtherwise, output directly after ' + thinking_end_token + ' with tool calls or final response.\n\n### Available Tool Schemas\n\n' -%}
15
  {%- set tools_footer = '\nYou MUST strictly follow the above defined tool name and parameter schemas to invoke tool calls.\n' -%}
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  {%- set ns = namespace(system_prompt='', is_first_sp=true) -%}
17
  {%- for message in messages -%}
18
  {%- if message['role'] == 'system' -%}
@@ -22,6 +40,9 @@
22
  {%- else -%}
23
  {%- set ns.system_prompt = ns.system_prompt + '\n\n' + (message['content'] or '') -%}
24
  {%- endif -%}
 
 
 
25
  {%- endif -%}
26
  {%- endfor -%}
27
  {%- if tools is defined and tools -%}
@@ -31,14 +52,21 @@
31
  {%- set ts.schemas = ts.schemas + (tool['function'] | tojson) + '\n' -%}
32
  {%- endif -%}
33
  {%- endfor -%}
34
- {%- if ns.system_prompt -%}
 
 
35
  {%- set ns.system_prompt = ns.system_prompt + '\n\n' + tools_header + ts.schemas + tools_footer -%}
36
  {%- else -%}
37
  {%- set ns.system_prompt = tools_header + ts.schemas + tools_footer -%}
38
  {%- endif -%}
39
  {%- endif -%}
40
  {{- bos_token -}}
 
 
 
41
  {{- ns.system_prompt -}}
 
 
42
  {%- set last_user_idx = namespace(value=-1) -%}
43
  {%- for message in messages -%}
44
  {%- if message['role'] == 'user' or message['role'] == 'developer' or message['role'] == 'tool' -%}
@@ -47,7 +75,7 @@
47
  {%- endfor -%}
48
  {%- set state = namespace(in_user=false) -%}
49
  {%- for message in messages -%}
50
- {%- if message['role'] == 'user' or message['role'] == 'developer' -%}
51
  {%- if state.in_user -%}
52
  {{- '\n\n' -}}
53
  {%- else -%}
@@ -55,6 +83,16 @@
55
  {%- set state.in_user = true -%}
56
  {%- endif -%}
57
  {{- message['content'] or '' -}}
 
 
 
 
 
 
 
 
 
 
58
  {%- elif message['role'] == 'tool' -%}
59
  {%- if state.in_user -%}
60
  {{- '\n\n' -}}
@@ -63,18 +101,107 @@
63
  {%- set state.in_user = true -%}
64
  {%- endif -%}
65
  {{- '<tool_result>' + (message['content'] or '') + '</tool_result>' -}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  {%- elif message['role'] == 'assistant' -%}
67
  {%- set state.in_user = false -%}
68
- {{- '<|Assistant|>' -}}
69
- {%- set is_after_last_user = loop.index0 > last_user_idx.value -%}
70
- {%- if is_after_last_user and thinking -%}
71
- {{- thinking_start_token -}}
72
- {%- if message['reasoning_content'] is defined and message['reasoning_content'] -%}
73
- {{- message['reasoning_content'] -}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  {%- endif -%}
75
- {{- thinking_end_token -}}
76
  {%- else -%}
77
- {{- thinking_end_token -}}
 
 
 
 
 
 
 
 
78
  {%- endif -%}
79
  {%- if message['content'] is defined and message['content'] -%}
80
  {{- message['content'] -}}
@@ -100,13 +227,22 @@
100
  {{- '</' + dsml_token + 'tool_calls>' -}}
101
  {%- endif -%}
102
  {{- '<|end▁of▁sentence|>' -}}
 
 
 
103
  {%- endif -%}
104
  {%- endfor -%}
105
  {%- if add_generation_prompt -%}
106
- {{- '<|Assistant|>' -}}
107
- {%- if thinking -%}
108
- {{- thinking_start_token -}}
109
- {%- else -%}
110
- {{- thinking_end_token -}}
 
 
 
 
 
111
  {%- endif -%}
112
- {%- endif -%}
 
 
1
+ {#- Unsloth template fixes #}
2
  {%- if not add_generation_prompt is defined -%}
3
  {%- set add_generation_prompt = false -%}
4
  {%- endif -%}
 
9
  {%- set thinking = false -%}
10
  {%- endif -%}
11
  {%- endif -%}
12
+ {%- if not reasoning_effort is defined -%}
13
+ {%- set reasoning_effort = none -%}
14
+ {%- endif -%}
15
  {%- set dsml_token = '|DSML|' -%}
16
  {%- set thinking_start_token = '<think>' -%}
17
  {%- set thinking_end_token = '</think>' -%}
18
+ {%- set reasoning_effort_max = 'Reasoning Effort: Absolute maximum with no shortcuts permitted.\nYou MUST be very thorough in your thinking and comprehensively decompose the problem to resolve the root cause, rigorously stress-testing your logic against all potential paths, edge cases, and adversarial scenarios.\nExplicitly write out your entire deliberation process, documenting every intermediate step, considered alternative, and rejected hypothesis to ensure absolutely no assumption is left unchecked.\n\n' -%}
19
  {%- set tools_header = '## Tools\n\nYou have access to a set of tools to help answer the user\'s question. You can invoke tools by writing a "<' + dsml_token + 'tool_calls>" block like the following:\n\n<' + dsml_token + 'tool_calls>\n<' + dsml_token + 'invoke name="$TOOL_NAME">\n<' + dsml_token + 'parameter name="$PARAMETER_NAME" string="true|false">$PARAMETER_VALUE</' + dsml_token + 'parameter>\n...\n</' + dsml_token + 'invoke>\n<' + dsml_token + 'invoke name="$TOOL_NAME2">\n...\n</' + dsml_token + 'invoke>\n</' + dsml_token + 'tool_calls>\n\nString parameters should be specified as is and set `string="true"`. For all other types (numbers, booleans, arrays, objects), pass the value in JSON format and set `string="false"`.\n\nIf thinking_mode is enabled (triggered by ' + thinking_start_token + '), you MUST output your complete reasoning inside ' + thinking_start_token + '...' + thinking_end_token + ' BEFORE any tool calls or final response.\n\nOtherwise, output directly after ' + thinking_end_token + ' with tool calls or final response.\n\n### Available Tool Schemas\n\n' -%}
20
  {%- set tools_footer = '\nYou MUST strictly follow the above defined tool name and parameter schemas to invoke tool calls.\n' -%}
21
+ {%- set response_format_header = '## Response Format:\n\nYou MUST strictly adhere to the following schema to reply:\n' -%}
22
+ {#- Detect tools anywhere (top-level param or attached to any message): DeepSeek-V4's
23
+ encoder disables reasoning-dropping entirely when tools are present. -#}
24
+ {%- set tp = namespace(has=false) -%}
25
+ {%- if tools is defined and tools -%}
26
+ {%- set tp.has = true -%}
27
+ {%- endif -%}
28
+ {%- for message in messages -%}
29
+ {%- if message['tools'] is defined and message['tools'] -%}
30
+ {%- set tp.has = true -%}
31
+ {%- endif -%}
32
+ {%- endfor -%}
33
+ {#- Build system prompt from all system messages (+ optional per-message response_format). -#}
34
  {%- set ns = namespace(system_prompt='', is_first_sp=true) -%}
35
  {%- for message in messages -%}
36
  {%- if message['role'] == 'system' -%}
 
40
  {%- else -%}
41
  {%- set ns.system_prompt = ns.system_prompt + '\n\n' + (message['content'] or '') -%}
42
  {%- endif -%}
43
+ {%- if message['response_format'] is defined and message['response_format'] -%}
44
+ {%- set ns.system_prompt = ns.system_prompt + '\n\n' + response_format_header + (message['response_format'] | tojson) -%}
45
+ {%- endif -%}
46
  {%- endif -%}
47
  {%- endfor -%}
48
  {%- if tools is defined and tools -%}
 
52
  {%- set ts.schemas = ts.schemas + (tool['function'] | tojson) + '\n' -%}
53
  {%- endif -%}
54
  {%- endfor -%}
55
+ {#- Match the reference: tools attach to the system message, so a system message
56
+ that exists but is empty still contributes its "" + "\n\n" separator. -#}
57
+ {%- if not ns.is_first_sp -%}
58
  {%- set ns.system_prompt = ns.system_prompt + '\n\n' + tools_header + ts.schemas + tools_footer -%}
59
  {%- else -%}
60
  {%- set ns.system_prompt = tools_header + ts.schemas + tools_footer -%}
61
  {%- endif -%}
62
  {%- endif -%}
63
  {{- bos_token -}}
64
+ {%- if thinking and reasoning_effort == 'max' -%}
65
+ {{- reasoning_effort_max -}}
66
+ {%- endif -%}
67
  {{- ns.system_prompt -}}
68
+ {#- Last user-like index: controls which turns keep reasoning. The encoder merges tool
69
+ results into user messages before computing this, so tool turns count as user here. -#}
70
  {%- set last_user_idx = namespace(value=-1) -%}
71
  {%- for message in messages -%}
72
  {%- if message['role'] == 'user' or message['role'] == 'developer' or message['role'] == 'tool' -%}
 
75
  {%- endfor -%}
76
  {%- set state = namespace(in_user=false) -%}
77
  {%- for message in messages -%}
78
+ {%- if message['role'] == 'user' -%}
79
  {%- if state.in_user -%}
80
  {{- '\n\n' -}}
81
  {%- else -%}
 
83
  {%- set state.in_user = true -%}
84
  {%- endif -%}
85
  {{- message['content'] or '' -}}
86
+ {%- if message['task'] is defined and message['task'] and (loop.last or messages[loop.index0 + 1]['role'] in ['assistant', 'latest_reminder']) -%}
87
+ {%- set state.in_user = false -%}
88
+ {%- if message['task'] == 'action' -%}
89
+ {{- '<|Assistant|>' -}}
90
+ {%- if thinking -%}{{- thinking_start_token -}}{%- else -%}{{- thinking_end_token -}}{%- endif -%}
91
+ {{- '<|action|>' -}}
92
+ {%- else -%}
93
+ {{- '<|' + message['task'] + '|>' -}}
94
+ {%- endif -%}
95
+ {%- endif -%}
96
  {%- elif message['role'] == 'tool' -%}
97
  {%- if state.in_user -%}
98
  {{- '\n\n' -}}
 
101
  {%- set state.in_user = true -%}
102
  {%- endif -%}
103
  {{- '<tool_result>' + (message['content'] or '') + '</tool_result>' -}}
104
+ {%- elif message['role'] == 'developer' -%}
105
+ {#- The encoder's _drop_thinking_messages drops developer turns that sit before the
106
+ last user turn when in thinking mode with reasoning-dropping active (no tools).
107
+ (No-op under llama.cpp, which remaps developer -> system before templating.) -#}
108
+ {%- if thinking and not tp.has and loop.index0 < last_user_idx.value -%}
109
+ {%- else -%}
110
+ {%- set state.in_user = false -%}
111
+ {{- '<|User|>' + (message['content'] or '') -}}
112
+ {%- if message['tools'] is defined and message['tools'] -%}
113
+ {%- set ds = namespace(schemas='') -%}
114
+ {%- for tool in message['tools'] -%}
115
+ {%- if tool['type'] == 'function' -%}
116
+ {%- set ds.schemas = ds.schemas + (tool['function'] | tojson) + '\n' -%}
117
+ {%- endif -%}
118
+ {%- endfor -%}
119
+ {{- '\n\n' + tools_header + ds.schemas + tools_footer -}}
120
+ {%- endif -%}
121
+ {%- if message['response_format'] is defined and message['response_format'] -%}
122
+ {{- '\n\n' + response_format_header + (message['response_format'] | tojson) -}}
123
+ {%- endif -%}
124
+ {%- if message['task'] is defined and message['task'] and (loop.last or messages[loop.index0 + 1]['role'] in ['assistant', 'latest_reminder']) -%}
125
+ {%- if message['task'] == 'action' -%}
126
+ {{- '<|Assistant|>' -}}
127
+ {%- if thinking -%}{{- thinking_start_token -}}{%- else -%}{{- thinking_end_token -}}{%- endif -%}
128
+ {{- '<|action|>' -}}
129
+ {%- else -%}
130
+ {{- '<|' + message['task'] + '|>' -}}
131
+ {%- endif -%}
132
+ {%- endif -%}
133
+ {%- endif -%}
134
+ {%- elif message['role'] == 'latest_reminder' -%}
135
+ {%- set state.in_user = false -%}
136
+ {#- The encoder emits the user/developer -> assistant transition before a
137
+ latest_reminder as well (the opening think stays open across the reminder and is
138
+ closed by the following assistant). Same effective-predecessor rule as assistant. -#}
139
+ {%- set ep = namespace(idx=(loop.index0 - 1), done=false, is_ud=false, has_task=false) -%}
140
+ {%- for _i in range(loop.index0) -%}
141
+ {%- if not ep.done and ep.idx >= 0 -%}
142
+ {%- set _pm = messages[ep.idx] -%}
143
+ {%- if (_pm['role'] == 'developer') and thinking and (not tp.has) and (ep.idx < last_user_idx.value) -%}
144
+ {%- set ep.idx = ep.idx - 1 -%}
145
+ {%- else -%}
146
+ {%- set ep.done = true -%}
147
+ {%- set ep.is_ud = _pm['role'] in ['user', 'developer', 'tool'] -%}
148
+ {%- set ep.has_task = _pm['task'] is defined and _pm['task'] -%}
149
+ {%- endif -%}
150
+ {%- endif -%}
151
+ {%- endfor -%}
152
+ {%- if ep.has_task -%}
153
+ {%- elif ep.is_ud -%}
154
+ {{- '<|Assistant|>' -}}
155
+ {%- if thinking and (tp.has or (loop.index0 > last_user_idx.value)) -%}
156
+ {{- thinking_start_token -}}
157
+ {%- else -%}
158
+ {{- thinking_end_token -}}
159
+ {%- endif -%}
160
+ {%- endif -%}
161
+ {{- '<|latest_reminder|>' + (message['content'] or '') -}}
162
  {%- elif message['role'] == 'assistant' -%}
163
  {%- set state.in_user = false -%}
164
+ {#- The encoder emits the "<|Assistant|>" + opening think/end token as a trailing
165
+ transition on a user/developer predecessor (tool results merge into user), never
166
+ on the assistant message itself. Find the effective predecessor, skipping any
167
+ developer the encoder drops (developer before the last user in thinking+drop). -#}
168
+ {%- set ep = namespace(idx=(loop.index0 - 1), done=false, is_ud=false, has_task=false) -%}
169
+ {%- for _i in range(loop.index0) -%}
170
+ {%- if not ep.done and ep.idx >= 0 -%}
171
+ {%- set _pm = messages[ep.idx] -%}
172
+ {%- if (_pm['role'] == 'developer') and thinking and (not tp.has) and (ep.idx < last_user_idx.value) -%}
173
+ {%- set ep.idx = ep.idx - 1 -%}
174
+ {%- else -%}
175
+ {%- set ep.done = true -%}
176
+ {%- set ep.is_ud = _pm['role'] in ['user', 'developer', 'tool'] -%}
177
+ {%- set ep.has_task = _pm['task'] is defined and _pm['task'] -%}
178
+ {%- endif -%}
179
+ {%- endif -%}
180
+ {%- endfor -%}
181
+ {%- set keep_reasoning = tp.has or (loop.index0 > last_user_idx.value) -%}
182
+ {%- if ep.has_task -%}
183
+ {#- The predecessor's task token already emitted the assistant prompt. -#}
184
+ {%- elif ep.is_ud -%}
185
+ {{- '<|Assistant|>' -}}
186
+ {%- if keep_reasoning and thinking -%}
187
+ {{- thinking_start_token -}}
188
+ {%- if message['reasoning_content'] is defined and message['reasoning_content'] -%}
189
+ {{- message['reasoning_content'] -}}
190
+ {%- endif -%}
191
+ {{- thinking_end_token -}}
192
+ {%- else -%}
193
+ {{- thinking_end_token -}}
194
  {%- endif -%}
 
195
  {%- else -%}
196
+ {#- Predecessor is not user/developer/tool (first turn, latest_reminder, another
197
+ assistant, or an all-dropped-developer run): the encoder emits no prefix and
198
+ no opening think; a kept reasoning turn still closes with its content + </think>. -#}
199
+ {%- if keep_reasoning and thinking -%}
200
+ {%- if message['reasoning_content'] is defined and message['reasoning_content'] -%}
201
+ {{- message['reasoning_content'] -}}
202
+ {%- endif -%}
203
+ {{- thinking_end_token -}}
204
+ {%- endif -%}
205
  {%- endif -%}
206
  {%- if message['content'] is defined and message['content'] -%}
207
  {{- message['content'] -}}
 
227
  {{- '</' + dsml_token + 'tool_calls>' -}}
228
  {%- endif -%}
229
  {{- '<|end▁of▁sentence|>' -}}
230
+ {%- if message['task'] is defined and message['task'] == 'title' and (loop.last or messages[loop.index0 + 1]['role'] in ['assistant', 'latest_reminder']) -%}
231
+ {{- '<|title|>' -}}
232
+ {%- endif -%}
233
  {%- endif -%}
234
  {%- endfor -%}
235
  {%- if add_generation_prompt -%}
236
+ {#- Suppress the standard generation prefix when the final message already
237
+ emitted a task token (which serves as the generation prompt). -#}
238
+ {%- set _last = messages[messages | length - 1] -%}
239
+ {%- if not (_last['task'] is defined and _last['task']) -%}
240
+ {{- '<|Assistant|>' -}}
241
+ {%- if thinking -%}
242
+ {{- thinking_start_token -}}
243
+ {%- else -%}
244
+ {{- thinking_end_token -}}
245
+ {%- endif -%}
246
  {%- endif -%}
247
+ {%- endif -%}
248
+ {#- Copyright 2026-present Unsloth. Apache 2.0 License. #}
tokenizer_config.json CHANGED
@@ -10278,5 +10278,5 @@
10278
  "sp_model_kwargs": {},
10279
  "tokenizer_class": "PreTrainedTokenizerFast",
10280
  "unk_token": null,
10281
- "chat_template": "{%- if not add_generation_prompt is defined -%}\n {%- set add_generation_prompt = false -%}\n{%- endif -%}\n{%- if not thinking is defined -%}\n {%- if enable_thinking is defined -%}\n {%- set thinking = enable_thinking -%}\n {%- else -%}\n {%- set thinking = false -%}\n {%- endif -%}\n{%- endif -%}\n{%- set dsml_token = '|DSML|' -%}\n{%- set thinking_start_token = '<think>' -%}\n{%- set thinking_end_token = '</think>' -%}\n{%- set tools_header = '## Tools\\n\\nYou have access to a set of tools to help answer the user\\'s question. You can invoke tools by writing a \"<' + dsml_token + 'tool_calls>\" block like the following:\\n\\n<' + dsml_token + 'tool_calls>\\n<' + dsml_token + 'invoke name=\"$TOOL_NAME\">\\n<' + dsml_token + 'parameter name=\"$PARAMETER_NAME\" string=\"true|false\">$PARAMETER_VALUE</' + dsml_token + 'parameter>\\n...\\n</' + dsml_token + 'invoke>\\n<' + dsml_token + 'invoke name=\"$TOOL_NAME2\">\\n...\\n</' + dsml_token + 'invoke>\\n</' + dsml_token + 'tool_calls>\\n\\nString parameters should be specified as is and set `string=\"true\"`. For all other types (numbers, booleans, arrays, objects), pass the value in JSON format and set `string=\"false\"`.\\n\\nIf thinking_mode is enabled (triggered by ' + thinking_start_token + '), you MUST output your complete reasoning inside ' + thinking_start_token + '...' + thinking_end_token + ' BEFORE any tool calls or final response.\\n\\nOtherwise, output directly after ' + thinking_end_token + ' with tool calls or final response.\\n\\n### Available Tool Schemas\\n\\n' -%}\n{%- set tools_footer = '\\nYou MUST strictly follow the above defined tool name and parameter schemas to invoke tool calls.\\n' -%}\n{%- set ns = namespace(system_prompt='', is_first_sp=true) -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'system' -%}\n {%- if ns.is_first_sp -%}\n {%- set ns.system_prompt = ns.system_prompt + (message['content'] or '') -%}\n {%- set ns.is_first_sp = false -%}\n {%- else -%}\n {%- set ns.system_prompt = ns.system_prompt + '\\n\\n' + (message['content'] or '') -%}\n {%- endif -%}\n {%- endif -%}\n{%- endfor -%}\n{%- if tools is defined and tools -%}\n {%- set ts = namespace(schemas='') -%}\n {%- for tool in tools -%}\n {%- if tool['type'] == 'function' -%}\n {%- set ts.schemas = ts.schemas + (tool['function'] | tojson) + '\\n' -%}\n {%- endif -%}\n {%- endfor -%}\n {%- if ns.system_prompt -%}\n {%- set ns.system_prompt = ns.system_prompt + '\\n\\n' + tools_header + ts.schemas + tools_footer -%}\n {%- else -%}\n {%- set ns.system_prompt = tools_header + ts.schemas + tools_footer -%}\n {%- endif -%}\n{%- endif -%}\n{{- bos_token -}}\n{{- ns.system_prompt -}}\n{%- set last_user_idx = namespace(value=-1) -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'user' or message['role'] == 'developer' or message['role'] == 'tool' -%}\n {%- set last_user_idx.value = loop.index0 -%}\n {%- endif -%}\n{%- endfor -%}\n{%- set state = namespace(in_user=false) -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'user' or message['role'] == 'developer' -%}\n {%- if state.in_user -%}\n {{- '\\n\\n' -}}\n {%- else -%}\n {{- '<|User|>' -}}\n {%- set state.in_user = true -%}\n {%- endif -%}\n {{- message['content'] or '' -}}\n {%- elif message['role'] == 'tool' -%}\n {%- if state.in_user -%}\n {{- '\\n\\n' -}}\n {%- else -%}\n {{- '<|User|>' -}}\n {%- set state.in_user = true -%}\n {%- endif -%}\n {{- '<tool_result>' + (message['content'] or '') + '</tool_result>' -}}\n {%- elif message['role'] == 'assistant' -%}\n {%- set state.in_user = false -%}\n {{- '<|Assistant|>' -}}\n {%- set is_after_last_user = loop.index0 > last_user_idx.value -%}\n {%- if is_after_last_user and thinking -%}\n {{- thinking_start_token -}}\n {%- if message['reasoning_content'] is defined and message['reasoning_content'] -%}\n {{- message['reasoning_content'] -}}\n {%- endif -%}\n {{- thinking_end_token -}}\n {%- else -%}\n {{- thinking_end_token -}}\n {%- endif -%}\n {%- if message['content'] is defined and message['content'] -%}\n {{- message['content'] -}}\n {%- endif -%}\n {%- if message['tool_calls'] -%}\n {{- '\\n\\n<' + dsml_token + 'tool_calls>\\n' -}}\n {%- for tool in message['tool_calls'] -%}\n {%- set func = tool['function'] -%}\n {{- '<' + dsml_token + 'invoke name=\"' + func['name'] + '\">\\n' -}}\n {%- set args = func['arguments'] -%}\n {%- if args is string -%}\n {%- set args = args | from_json -%}\n {%- endif -%}\n {%- for key, val in args.items() -%}\n {%- if val is string -%}\n {{- '<' + dsml_token + 'parameter name=\"' + key + '\" string=\"true\">' + val + '</' + dsml_token + 'parameter>\\n' -}}\n {%- else -%}\n {{- '<' + dsml_token + 'parameter name=\"' + key + '\" string=\"false\">' + (val | tojson) + '</' + dsml_token + 'parameter>\\n' -}}\n {%- endif -%}\n {%- endfor -%}\n {{- '</' + dsml_token + 'invoke>\\n' -}}\n {%- endfor -%}\n {{- '</' + dsml_token + 'tool_calls>' -}}\n {%- endif -%}\n {{- '<|end▁of▁sentence|>' -}}\n {%- endif -%}\n{%- endfor -%}\n{%- if add_generation_prompt -%}\n {{- '<|Assistant|>' -}}\n {%- if thinking -%}\n {{- thinking_start_token -}}\n {%- else -%}\n {{- thinking_end_token -}}\n {%- endif -%}\n{%- endif -%}"
10282
- }
 
10278
  "sp_model_kwargs": {},
10279
  "tokenizer_class": "PreTrainedTokenizerFast",
10280
  "unk_token": null,
10281
+ "chat_template": "{#- Unsloth template fixes #}\n{%- if not add_generation_prompt is defined -%}\n {%- set add_generation_prompt = false -%}\n{%- endif -%}\n{%- if not thinking is defined -%}\n {%- if enable_thinking is defined -%}\n {%- set thinking = enable_thinking -%}\n {%- else -%}\n {%- set thinking = false -%}\n {%- endif -%}\n{%- endif -%}\n{%- if not reasoning_effort is defined -%}\n {%- set reasoning_effort = none -%}\n{%- endif -%}\n{%- set dsml_token = '|DSML|' -%}\n{%- set thinking_start_token = '<think>' -%}\n{%- set thinking_end_token = '</think>' -%}\n{%- set reasoning_effort_max = 'Reasoning Effort: Absolute maximum with no shortcuts permitted.\\nYou MUST be very thorough in your thinking and comprehensively decompose the problem to resolve the root cause, rigorously stress-testing your logic against all potential paths, edge cases, and adversarial scenarios.\\nExplicitly write out your entire deliberation process, documenting every intermediate step, considered alternative, and rejected hypothesis to ensure absolutely no assumption is left unchecked.\\n\\n' -%}\n{%- set tools_header = '## Tools\\n\\nYou have access to a set of tools to help answer the user\\'s question. You can invoke tools by writing a \"<' + dsml_token + 'tool_calls>\" block like the following:\\n\\n<' + dsml_token + 'tool_calls>\\n<' + dsml_token + 'invoke name=\"$TOOL_NAME\">\\n<' + dsml_token + 'parameter name=\"$PARAMETER_NAME\" string=\"true|false\">$PARAMETER_VALUE</' + dsml_token + 'parameter>\\n...\\n</' + dsml_token + 'invoke>\\n<' + dsml_token + 'invoke name=\"$TOOL_NAME2\">\\n...\\n</' + dsml_token + 'invoke>\\n</' + dsml_token + 'tool_calls>\\n\\nString parameters should be specified as is and set `string=\"true\"`. For all other types (numbers, booleans, arrays, objects), pass the value in JSON format and set `string=\"false\"`.\\n\\nIf thinking_mode is enabled (triggered by ' + thinking_start_token + '), you MUST output your complete reasoning inside ' + thinking_start_token + '...' + thinking_end_token + ' BEFORE any tool calls or final response.\\n\\nOtherwise, output directly after ' + thinking_end_token + ' with tool calls or final response.\\n\\n### Available Tool Schemas\\n\\n' -%}\n{%- set tools_footer = '\\nYou MUST strictly follow the above defined tool name and parameter schemas to invoke tool calls.\\n' -%}\n{%- set response_format_header = '## Response Format:\\n\\nYou MUST strictly adhere to the following schema to reply:\\n' -%}\n{#- Detect tools anywhere (top-level param or attached to any message): DeepSeek-V4's\n encoder disables reasoning-dropping entirely when tools are present. -#}\n{%- set tp = namespace(has=false) -%}\n{%- if tools is defined and tools -%}\n {%- set tp.has = true -%}\n{%- endif -%}\n{%- for message in messages -%}\n {%- if message['tools'] is defined and message['tools'] -%}\n {%- set tp.has = true -%}\n {%- endif -%}\n{%- endfor -%}\n{#- Build system prompt from all system messages (+ optional per-message response_format). -#}\n{%- set ns = namespace(system_prompt='', is_first_sp=true) -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'system' -%}\n {%- if ns.is_first_sp -%}\n {%- set ns.system_prompt = ns.system_prompt + (message['content'] or '') -%}\n {%- set ns.is_first_sp = false -%}\n {%- else -%}\n {%- set ns.system_prompt = ns.system_prompt + '\\n\\n' + (message['content'] or '') -%}\n {%- endif -%}\n {%- if message['response_format'] is defined and message['response_format'] -%}\n {%- set ns.system_prompt = ns.system_prompt + '\\n\\n' + response_format_header + (message['response_format'] | tojson) -%}\n {%- endif -%}\n {%- endif -%}\n{%- endfor -%}\n{%- if tools is defined and tools -%}\n {%- set ts = namespace(schemas='') -%}\n {%- for tool in tools -%}\n {%- if tool['type'] == 'function' -%}\n {%- set ts.schemas = ts.schemas + (tool['function'] | tojson) + '\\n' -%}\n {%- endif -%}\n {%- endfor -%}\n {#- Match the reference: tools attach to the system message, so a system message\n that exists but is empty still contributes its \"\" + \"\\n\\n\" separator. -#}\n {%- if not ns.is_first_sp -%}\n {%- set ns.system_prompt = ns.system_prompt + '\\n\\n' + tools_header + ts.schemas + tools_footer -%}\n {%- else -%}\n {%- set ns.system_prompt = tools_header + ts.schemas + tools_footer -%}\n {%- endif -%}\n{%- endif -%}\n{{- bos_token -}}\n{%- if thinking and reasoning_effort == 'max' -%}\n {{- reasoning_effort_max -}}\n{%- endif -%}\n{{- ns.system_prompt -}}\n{#- Last user-like index: controls which turns keep reasoning. The encoder merges tool\n results into user messages before computing this, so tool turns count as user here. -#}\n{%- set last_user_idx = namespace(value=-1) -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'user' or message['role'] == 'developer' or message['role'] == 'tool' -%}\n {%- set last_user_idx.value = loop.index0 -%}\n {%- endif -%}\n{%- endfor -%}\n{%- set state = namespace(in_user=false) -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'user' -%}\n {%- if state.in_user -%}\n {{- '\\n\\n' -}}\n {%- else -%}\n {{- '<|User|>' -}}\n {%- set state.in_user = true -%}\n {%- endif -%}\n {{- message['content'] or '' -}}\n {%- if message['task'] is defined and message['task'] and (loop.last or messages[loop.index0 + 1]['role'] in ['assistant', 'latest_reminder']) -%}\n {%- set state.in_user = false -%}\n {%- if message['task'] == 'action' -%}\n {{- '<|Assistant|>' -}}\n {%- if thinking -%}{{- thinking_start_token -}}{%- else -%}{{- thinking_end_token -}}{%- endif -%}\n {{- '<|action|>' -}}\n {%- else -%}\n {{- '<|' + message['task'] + '|>' -}}\n {%- endif -%}\n {%- endif -%}\n {%- elif message['role'] == 'tool' -%}\n {%- if state.in_user -%}\n {{- '\\n\\n' -}}\n {%- else -%}\n {{- '<|User|>' -}}\n {%- set state.in_user = true -%}\n {%- endif -%}\n {{- '<tool_result>' + (message['content'] or '') + '</tool_result>' -}}\n {%- elif message['role'] == 'developer' -%}\n {#- The encoder's _drop_thinking_messages drops developer turns that sit before the\n last user turn when in thinking mode with reasoning-dropping active (no tools).\n (No-op under llama.cpp, which remaps developer -> system before templating.) -#}\n {%- if thinking and not tp.has and loop.index0 < last_user_idx.value -%}\n {%- else -%}\n {%- set state.in_user = false -%}\n {{- '<|User|>' + (message['content'] or '') -}}\n {%- if message['tools'] is defined and message['tools'] -%}\n {%- set ds = namespace(schemas='') -%}\n {%- for tool in message['tools'] -%}\n {%- if tool['type'] == 'function' -%}\n {%- set ds.schemas = ds.schemas + (tool['function'] | tojson) + '\\n' -%}\n {%- endif -%}\n {%- endfor -%}\n {{- '\\n\\n' + tools_header + ds.schemas + tools_footer -}}\n {%- endif -%}\n {%- if message['response_format'] is defined and message['response_format'] -%}\n {{- '\\n\\n' + response_format_header + (message['response_format'] | tojson) -}}\n {%- endif -%}\n {%- if message['task'] is defined and message['task'] and (loop.last or messages[loop.index0 + 1]['role'] in ['assistant', 'latest_reminder']) -%}\n {%- if message['task'] == 'action' -%}\n {{- '<|Assistant|>' -}}\n {%- if thinking -%}{{- thinking_start_token -}}{%- else -%}{{- thinking_end_token -}}{%- endif -%}\n {{- '<|action|>' -}}\n {%- else -%}\n {{- '<|' + message['task'] + '|>' -}}\n {%- endif -%}\n {%- endif -%}\n {%- endif -%}\n {%- elif message['role'] == 'latest_reminder' -%}\n {%- set state.in_user = false -%}\n {#- The encoder emits the user/developer -> assistant transition before a\n latest_reminder as well (the opening think stays open across the reminder and is\n closed by the following assistant). Same effective-predecessor rule as assistant. -#}\n {%- set ep = namespace(idx=(loop.index0 - 1), done=false, is_ud=false, has_task=false) -%}\n {%- for _i in range(loop.index0) -%}\n {%- if not ep.done and ep.idx >= 0 -%}\n {%- set _pm = messages[ep.idx] -%}\n {%- if (_pm['role'] == 'developer') and thinking and (not tp.has) and (ep.idx < last_user_idx.value) -%}\n {%- set ep.idx = ep.idx - 1 -%}\n {%- else -%}\n {%- set ep.done = true -%}\n {%- set ep.is_ud = _pm['role'] in ['user', 'developer', 'tool'] -%}\n {%- set ep.has_task = _pm['task'] is defined and _pm['task'] -%}\n {%- endif -%}\n {%- endif -%}\n {%- endfor -%}\n {%- if ep.has_task -%}\n {%- elif ep.is_ud -%}\n {{- '<|Assistant|>' -}}\n {%- if thinking and (tp.has or (loop.index0 > last_user_idx.value)) -%}\n {{- thinking_start_token -}}\n {%- else -%}\n {{- thinking_end_token -}}\n {%- endif -%}\n {%- endif -%}\n {{- '<|latest_reminder|>' + (message['content'] or '') -}}\n {%- elif message['role'] == 'assistant' -%}\n {%- set state.in_user = false -%}\n {#- The encoder emits the \"<|Assistant|>\" + opening think/end token as a trailing\n transition on a user/developer predecessor (tool results merge into user), never\n on the assistant message itself. Find the effective predecessor, skipping any\n developer the encoder drops (developer before the last user in thinking+drop). -#}\n {%- set ep = namespace(idx=(loop.index0 - 1), done=false, is_ud=false, has_task=false) -%}\n {%- for _i in range(loop.index0) -%}\n {%- if not ep.done and ep.idx >= 0 -%}\n {%- set _pm = messages[ep.idx] -%}\n {%- if (_pm['role'] == 'developer') and thinking and (not tp.has) and (ep.idx < last_user_idx.value) -%}\n {%- set ep.idx = ep.idx - 1 -%}\n {%- else -%}\n {%- set ep.done = true -%}\n {%- set ep.is_ud = _pm['role'] in ['user', 'developer', 'tool'] -%}\n {%- set ep.has_task = _pm['task'] is defined and _pm['task'] -%}\n {%- endif -%}\n {%- endif -%}\n {%- endfor -%}\n {%- set keep_reasoning = tp.has or (loop.index0 > last_user_idx.value) -%}\n {%- if ep.has_task -%}\n {#- The predecessor's task token already emitted the assistant prompt. -#}\n {%- elif ep.is_ud -%}\n {{- '<|Assistant|>' -}}\n {%- if keep_reasoning and thinking -%}\n {{- thinking_start_token -}}\n {%- if message['reasoning_content'] is defined and message['reasoning_content'] -%}\n {{- message['reasoning_content'] -}}\n {%- endif -%}\n {{- thinking_end_token -}}\n {%- else -%}\n {{- thinking_end_token -}}\n {%- endif -%}\n {%- else -%}\n {#- Predecessor is not user/developer/tool (first turn, latest_reminder, another\n assistant, or an all-dropped-developer run): the encoder emits no prefix and\n no opening think; a kept reasoning turn still closes with its content + </think>. -#}\n {%- if keep_reasoning and thinking -%}\n {%- if message['reasoning_content'] is defined and message['reasoning_content'] -%}\n {{- message['reasoning_content'] -}}\n {%- endif -%}\n {{- thinking_end_token -}}\n {%- endif -%}\n {%- endif -%}\n {%- if message['content'] is defined and message['content'] -%}\n {{- message['content'] -}}\n {%- endif -%}\n {%- if message['tool_calls'] -%}\n {{- '\\n\\n<' + dsml_token + 'tool_calls>\\n' -}}\n {%- for tool in message['tool_calls'] -%}\n {%- set func = tool['function'] -%}\n {{- '<' + dsml_token + 'invoke name=\"' + func['name'] + '\">\\n' -}}\n {%- set args = func['arguments'] -%}\n {%- if args is string -%}\n {%- set args = args | from_json -%}\n {%- endif -%}\n {%- for key, val in args.items() -%}\n {%- if val is string -%}\n {{- '<' + dsml_token + 'parameter name=\"' + key + '\" string=\"true\">' + val + '</' + dsml_token + 'parameter>\\n' -}}\n {%- else -%}\n {{- '<' + dsml_token + 'parameter name=\"' + key + '\" string=\"false\">' + (val | tojson) + '</' + dsml_token + 'parameter>\\n' -}}\n {%- endif -%}\n {%- endfor -%}\n {{- '</' + dsml_token + 'invoke>\\n' -}}\n {%- endfor -%}\n {{- '</' + dsml_token + 'tool_calls>' -}}\n {%- endif -%}\n {{- '<|end▁of▁sentence|>' -}}\n {%- if message['task'] is defined and message['task'] == 'title' and (loop.last or messages[loop.index0 + 1]['role'] in ['assistant', 'latest_reminder']) -%}\n {{- '<|title|>' -}}\n {%- endif -%}\n {%- endif -%}\n{%- endfor -%}\n{%- if add_generation_prompt -%}\n {#- Suppress the standard generation prefix when the final message already\n emitted a task token (which serves as the generation prompt). -#}\n {%- set _last = messages[messages | length - 1] -%}\n {%- if not (_last['task'] is defined and _last['task']) -%}\n {{- '<|Assistant|>' -}}\n {%- if thinking -%}\n {{- thinking_start_token -}}\n {%- else -%}\n {{- thinking_end_token -}}\n {%- endif -%}\n {%- endif -%}\n{%- endif -%}\n{#- Copyright 2026-present Unsloth. Apache 2.0 License. #}"
10282
+ }