Skip to content

Commit 15175c8

Browse files
committed
Add fix of the apertus chat template
1 parent aac4953 commit 15175c8

File tree

1 file changed

+370
-0
lines changed

1 file changed

+370
-0
lines changed
Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
{%- macro render_typescript_type(param_spec, required_params, is_nullable=false) -%}
2+
{%- if param_spec.type == "array" -%}
3+
{%- if param_spec['items'] -%}
4+
{%- if param_spec['items']['type'] == "string" -%}
5+
{{- "string[]" }}
6+
{%- elif param_spec['items']['type'] == "number" -%}
7+
{{- "number[]" }}
8+
{%- elif param_spec['items']['type'] == "integer" -%}
9+
{{- "number[]" }}
10+
{%- elif param_spec['items']['type'] == "boolean" -%}
11+
{{- "boolean[]" }}
12+
{%- else -%}
13+
{%- set inner_type = render_typescript_type(param_spec['items'], required_params) -%}
14+
{%- if inner_type == "object | object" or inner_type|length > 50 -%}
15+
{{- "any[]" }}
16+
{%- else -%}
17+
{{- inner_type + "[]" }}
18+
{%- endif -%}
19+
{%- endif -%}
20+
{%- if param_spec.nullable -%}
21+
{{- " | null" }}
22+
{%- endif -%}
23+
{%- else -%}
24+
{{- "any[]" }}
25+
{%- if param_spec.nullable -%}
26+
{{- " | null" }}
27+
{%- endif -%}
28+
{%- endif -%}
29+
{%- elif param_spec.type is defined and param_spec.type is iterable and param_spec.type is not string and param_spec.type is not mapping and param_spec.type[0] is defined -%}
30+
{#- Handle array of types like ["object", "object"] from Union[dict, list] #}
31+
{%- if param_spec.type | length > 1 -%}
32+
{{- param_spec.type | join(" | ") }}
33+
{%- else -%}
34+
{{- param_spec.type[0] }}
35+
{%- endif -%}
36+
{%- elif param_spec.oneOf -%}
37+
{#- Handle oneOf schemas - check for complex unions and fallback to any #}
38+
{%- set has_object_variants = false -%}
39+
{%- for variant in param_spec.oneOf -%}
40+
{%- if variant.type == "object" -%}
41+
{%- set has_object_variants = true -%}
42+
{%- endif -%}
43+
{%- endfor -%}
44+
{%- if has_object_variants and param_spec.oneOf|length > 1 -%}
45+
{{- "any" }}
46+
{%- else -%}
47+
{%- for variant in param_spec.oneOf -%}
48+
{{- render_typescript_type(variant, required_params) -}}
49+
{%- if variant.description %}
50+
{{- "// " + variant.description }}
51+
{%- endif -%}
52+
{%- if variant.default is defined %}
53+
{{ "// default: " + variant.default|tojson }}
54+
{%- endif -%}
55+
{%- if not loop.last %}
56+
{{- " | " }}
57+
{% endif -%}
58+
{%- endfor -%}
59+
{%- endif -%}
60+
{%- elif param_spec.type == "string" -%}
61+
{%- if param_spec.enum -%}
62+
{{- '"' + param_spec.enum|join('" | "') + '"' -}}
63+
{%- else -%}
64+
{{- "string" }}
65+
{%- if param_spec.nullable %}
66+
{{- " | null" }}
67+
{%- endif -%}
68+
{%- endif -%}
69+
{%- elif param_spec.type == "number" -%}
70+
{{- "number" }}
71+
{%- elif param_spec.type == "integer" -%}
72+
{{- "number" }}
73+
{%- elif param_spec.type == "boolean" -%}
74+
{{- "boolean" }}
75+
{%- elif param_spec.type == "object" -%}
76+
{%- if param_spec.properties -%}
77+
{{- "{\n" }}
78+
{%- for prop_name, prop_spec in param_spec.properties.items() -%}
79+
{{- prop_name -}}
80+
{%- if prop_name not in (param_spec.required or []) -%}
81+
{{- "?" }}
82+
{%- endif -%}
83+
{{- ": " }}
84+
{{ render_typescript_type(prop_spec, param_spec.required or []) }}
85+
{%- if not loop.last -%}
86+
{{-", " }}
87+
{%- endif -%}
88+
{%- endfor -%}
89+
{{- "}" }}
90+
{%- else -%}
91+
{{- "object" }}
92+
{%- endif -%}
93+
{%- else -%}
94+
{{- "any" }}
95+
{%- endif -%}
96+
{%- endmacro -%}
97+
98+
{%- macro render_tools(tools) -%}
99+
{%- for tool in tools %}
100+
{%- if tool.function is defined -%}
101+
{#- Chat Completions format: {"type": "function", "function": {...}} #}
102+
{%- set func = tool.function -%}
103+
{%- if func.description is defined -%}
104+
{{- "// " + func.description + "\n" }}
105+
{%- endif -%}
106+
{{- "type "+ func.name + " = " }}
107+
{%- if func.parameters and func.parameters.properties %}
108+
{{- "(_: {\n" }}
109+
{%- for param_name, param_spec in func.parameters.properties.items() %}
110+
{%- if param_spec.description is defined %}
111+
{{- "// " + param_spec.description + "\n" }}
112+
{%- endif %}
113+
{{- param_name }}
114+
{%- if param_name not in (func.parameters.required or []) -%}
115+
{{- "?" }}
116+
{%- endif -%}
117+
{{- ": " }}
118+
{{- render_typescript_type(param_spec, func.parameters.required or []) }}
119+
{%- if param_spec.default is defined -%}
120+
{%- if param_spec.enum %}
121+
{{- ", // default: " + param_spec.default }}
122+
{%- elif param_spec.oneOf %}
123+
{{- "// default: " + param_spec.default }}
124+
{%- else %}
125+
{{- ", // default: " + param_spec.default|tojson }}
126+
{%- endif -%}
127+
{%- endif -%}
128+
{%- if not loop.last %}
129+
{{- ",\n" }}
130+
{%- else %}
131+
{{- "\n" }}
132+
{%- endif -%}
133+
{%- endfor %}
134+
{{- "}) => any;" }}
135+
{%- else -%}
136+
{{- "() => any;" }}
137+
{%- endif -%}
138+
{%- else -%}
139+
{#- Responses format: {"type": "function", "name": "...", ...} #}
140+
{%- if tool.description is defined -%}
141+
{{- "// " + tool.description + "\n" }}
142+
{%- endif -%}
143+
{{- "type "+ tool.name + " = " }}
144+
{%- if tool.parameters and tool.parameters.properties %}
145+
{{- "(_: {\n" }}
146+
{%- for param_name, param_spec in tool.parameters.properties.items() %}
147+
{%- if param_spec.description is defined %}
148+
{{- "// " + param_spec.description + "\n" }}
149+
{%- endif %}
150+
{{- param_name }}
151+
{%- if param_name not in (tool.parameters.required or []) -%}
152+
{{- "?" }}
153+
{%- endif -%}
154+
{{- ": " }}
155+
{{- render_typescript_type(param_spec, tool.parameters.required or []) }}
156+
{%- if param_spec.default is defined -%}
157+
{%- if param_spec.enum %}
158+
{{- ", // default: " + param_spec.default }}
159+
{%- elif param_spec.oneOf %}
160+
{{- "// default: " + param_spec.default }}
161+
{%- else %}
162+
{{- ", // default: " + param_spec.default|tojson }}
163+
{%- endif -%}
164+
{%- endif -%}
165+
{%- if not loop.last %}
166+
{{- ",\n" }}
167+
{%- else %}
168+
{{- "\n" }}
169+
{%- endif -%}
170+
{%- endfor %}
171+
{{- "}) => any;" }}
172+
{%- else -%}
173+
{{- "() => any;" }}
174+
{%- endif -%}
175+
{%- endif -%}
176+
{%- if not loop.last -%}
177+
{{- "\n" }}
178+
{%- endif -%}
179+
{%- endfor %}
180+
{%- endmacro -%}
181+
182+
{{ bos_token }}
183+
184+
{%- set system_token = '<|system_start|>' -%}
185+
{%- set end_system_token = '<|system_end|>' -%}
186+
{%- set developer_token = '<|developer_start|>' -%}
187+
{%- set end_developer_token = '<|developer_end|>' -%}
188+
{%- set user_token = '<|user_start|>' -%}
189+
{%- set end_user_token = '<|user_end|>' -%}
190+
{%- set assistant_token = '<|assistant_start|>' -%}
191+
{%- set end_assistant_token = '<|assistant_end|>' -%}
192+
{%- set inner_token = '<|inner_prefix|>' -%}
193+
{%- set outer_token = '<|inner_suffix|>' -%}
194+
{%- set tool_calls_token = '<|tools_prefix|>' -%}
195+
{%- set end_tool_calls_token = '<|tools_suffix|>' -%}
196+
197+
{%- set ns = namespace(in_assistant=false, in_tool=false, in_inner=false, assistant_format=none) -%}
198+
199+
{%- if messages and messages[0].role == 'system' -%}
200+
{%- if "content" in messages[0] -%}
201+
{%- if messages[0].content is string -%}
202+
{{ system_token + messages[0].content + end_system_token }}
203+
{%- elif messages[0].content is mapping and "text" in messages[0].content -%}
204+
{{ system_token + messages[0].content.text + end_system_token }}
205+
{%- else -%}
206+
{{- raise_exception("Invalid system message") -}}
207+
{%- endif -%}
208+
{%- else -%}
209+
{{- raise_exception("Invalid system message") -}}
210+
{%- endif -%}
211+
{%- set loop_messages = messages[1:] -%}
212+
{%- else -%}
213+
{{ system_token + 'You are Apertus, a helpful assistant created by the SwissAI initiative.\nKnowledge cutoff: 2024-04\nCurrent date: ' + strftime_now('%Y-%m-%d') + end_system_token }}
214+
{%- set loop_messages = messages -%}
215+
{%- endif -%}
216+
217+
{{ developer_token + 'Deliberation: ' }}
218+
{%- if enable_thinking is defined and enable_thinking -%}
219+
{{ 'enabled\n' }}
220+
{%- else -%}
221+
{{ 'disabled\n' }}
222+
{%- endif -%}
223+
{%- if tools is defined and tools -%}
224+
{{ 'Tool Capabilities:\n' + render_tools(tools) }}
225+
{%- else -%}
226+
{{ 'Tool Capabilities: disabled' }}
227+
{%- endif -%}
228+
{{ end_developer_token }}
229+
230+
{%- for message in loop_messages -%}
231+
{%- if message.role == 'user' -%}
232+
{%- set ns.in_inner = false -%}
233+
{%- if ns.in_tool -%}
234+
{{ ']' }}
235+
{%- set ns.in_tool = false -%}
236+
{%- endif -%}
237+
{%- if ns.in_assistant -%}
238+
{{ end_assistant_token }}
239+
{%- set ns.in_assistant = false -%}
240+
{%- endif -%}
241+
{%- if "content" in message -%}
242+
{{ user_token }}
243+
{%- if message.content is string -%}
244+
{{ message.content }}
245+
{%- elif message.content is mapping and "parts" in message.content -%}
246+
{%- set parts = message.content.parts -%}
247+
{%- for part in parts -%}
248+
{%- if part.type == "text" -%}
249+
{{ part.text }}
250+
{%- else -%}
251+
{{- raise_exception("Invalid user part: " + part.type) -}}
252+
{%- endif -%}
253+
{%- endfor -%}
254+
{%- else -%}
255+
{{- raise_exception("Invalid user message: " + message.role) -}}
256+
{%- endif -%}
257+
{{ end_user_token }}
258+
{%- endif -%}
259+
{%- elif message.role == 'assistant' -%}
260+
{%- if not ns.in_assistant -%}
261+
{{ assistant_token }}
262+
{%- set ns.in_assistant = true -%}
263+
{%- endif -%}
264+
{%- if "content" in message -%}
265+
{%- if message.content is string and (ns.assistant_format is none or ns.assistant_format == "string") -%}
266+
{%- if ns.in_tool -%}
267+
{{ ']' }}
268+
{%- set ns.in_tool = false -%}
269+
{%- endif -%}
270+
{%- set ns.assistant_format = "string" -%}
271+
{{ message.content }}
272+
{%- elif message.content is mapping and "blocks" in message.content and (ns.assistant_format is none or ns.assistant_format == "mapping") -%}
273+
{%- set ns.assistant_format = "mapping" -%}
274+
{%- set blocks = message.content.blocks -%}
275+
{%- for block in blocks -%}
276+
{%- if block.type == 'thoughts' -%}
277+
{%- if ns.in_tool -%}
278+
{{ ']' }}
279+
{%- set ns.in_tool = false -%}
280+
{%- endif -%}
281+
{%- if not ns.in_inner -%}
282+
{%- set ns.in_inner = true -%}
283+
{{ inner_token }}
284+
{%- endif -%}
285+
{{ block.text }}
286+
{%- elif block.type == 'tool_calls' -%}
287+
{%- if ns.in_tool -%}
288+
{{ ']' }}
289+
{%- set ns.in_tool = false -%}
290+
{%- endif -%}
291+
{%- if ns.in_inner and not loop.first and block.calls|length == 1 and block.calls[0].name == 'display_answers' -%}
292+
{%- set ns.in_inner = false -%}
293+
{{ outer_token }}
294+
{%- endif -%}
295+
{{ tool_calls_token + '[' }}
296+
{%- for tool_call in block.calls -%}
297+
{{- '{"' + tool_call.name + '": ' + tool_call.arguments + '}' }}
298+
{%- if not loop.last -%}
299+
{{- ", " }}
300+
{%- endif -%}
301+
{%- endfor -%}
302+
{{ ']' + end_tool_calls_token }}
303+
{%- elif block.type == 'tool_outputs' -%}
304+
{%- if ns.in_tool -%}
305+
{{- raise_exception("Cannot have both tool outputs as separate messages and tool outputs as blocks") -}}
306+
{%- endif -%}
307+
{{ '[' }}
308+
{%- for tool_output in block.outputs -%}
309+
{{- tool_output.output }}
310+
{%- if not loop.last -%}
311+
{{- ", " }}
312+
{%- endif -%}
313+
{%- endfor -%}
314+
{{- ']' }}
315+
{%- elif block.type == 'response' -%}
316+
{%- if ns.in_tool -%}
317+
{{ ']' }}
318+
{%- set ns.in_tool = false -%}
319+
{%- endif -%}
320+
{%- if (not loop.first and ns.in_inner) or (ns.in_assistant and ns.in_inner) -%}
321+
{%- set ns.in_inner = false -%}
322+
{{ outer_token }}
323+
{%- endif -%}
324+
{{ block.text }}
325+
{%- else -%}
326+
{{- raise_exception("Invalid assistant block type: " + block.type) -}}
327+
{%- endif -%}
328+
{%- endfor -%}
329+
{%- else -%}
330+
{{- raise_exception("Invalid assistant content") -}}
331+
{%- endif -%}
332+
{%- else -%}
333+
{{- raise_exception("Invalid assistant message") -}}
334+
{%- endif -%}
335+
{%- if "tool_calls" in message and message.tool_calls -%}
336+
{{ tool_calls_token + '[' }}
337+
{%- for tool_call in message.tool_calls -%}
338+
{%- if tool_call.type == 'function' -%}
339+
{%- set function = tool_call.function -%}
340+
{{- '{"' + function.name + '": ' + function.arguments + '}' }}
341+
{%- if not loop.last -%}
342+
{{- ", " }}
343+
{%- endif -%}
344+
{%- else -%}
345+
{{- raise_exception("Invalid tool call type: " + tool_call.type) -}}
346+
{%- endif -%}
347+
{%- endfor -%}
348+
{{ ']' + end_tool_calls_token }}
349+
{%- endif -%}
350+
{%- elif message.role == 'tool' -%}
351+
{%- if not ns.in_assistant -%}
352+
{{- raise_exception("Tool message outside of assistant") -}}
353+
{%- endif -%}
354+
{%- if not ns.in_tool -%}
355+
{{ '[' }}
356+
{%- set ns.in_tool = true -%}
357+
{%- else -%}
358+
{{ ", "}}
359+
{%- endif -%}
360+
{{ message.content }}
361+
{%- else -%}
362+
{{- raise_exception("Invalid message role") -}}
363+
{%- endif -%}
364+
{%- endfor -%}
365+
{%- if ns.in_tool -%}
366+
{{ ']' }}
367+
{%- endif -%}
368+
{%- if add_generation_prompt -%}
369+
{{ assistant_token }}
370+
{%- endif -%}

0 commit comments

Comments
 (0)