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        {{- "// " + tool.description + "\n" }}
101        {{- "type "+ tool.name + " = " }}
102        {%- if tool.parameters and tool.parameters.properties %}
103            {{- "(_: {\n" }}
104            {%- for param_name, param_spec in tool.parameters.properties.items() %}
105                {%- if param_spec.description %}
106                    {{- "// " + param_spec.description + "\n" }}
107                {%- endif %}
108                {{- param_name }}
109                {%- if param_name not in (tool.parameters.required or []) -%}
110                    {{- "?" }}
111                {%- endif -%}
112                {{- ": " }}
113                {{- render_typescript_type(param_spec, tool.parameters.required or []) }}
114                {%- if param_spec.default is defined -%}
115                    {%- if param_spec.enum %}
116                        {{- ", // default: " + param_spec.default }}
117                    {%- elif param_spec.oneOf %}
118                        {{- "// default: " + param_spec.default }}
119                    {%- else %}
120                        {{- ", // default: " + param_spec.default|tojson }}
121                    {%- endif -%}
122                {%- endif -%}
123                {%- if not loop.last %}
124                    {{- ",\n" }}
125                {%- else %}
126                    {{- "\n" }}
127                {%- endif -%}
128            {%- endfor %}
129            {{- "}) => any;" }}
130        {%- else -%}
131            {{- "() => any;" }}
132        {%- endif -%}
133        {%- if not loop.last -%}
134            {{- "\n" }}
135        {%- endif -%}
136    {%- endfor %}
137{%- endmacro -%}
138
139{{ bos_token }}
140
141{%- set system_token = '<|system_start|>' -%}
142{%- set end_system_token = '<|system_end|>' -%}
143{%- set developer_token = '<|developer_start|>' -%}
144{%- set end_developer_token = '<|developer_end|>' -%}
145{%- set user_token = '<|user_start|>' -%}
146{%- set end_user_token = '<|user_end|>' -%}
147{%- set assistant_token = '<|assistant_start|>' -%}
148{%- set end_assistant_token = '<|assistant_end|>' -%}
149{%- set inner_token = '<|inner_prefix|>' -%}
150{%- set outer_token = '<|inner_suffix|>' -%}
151{%- set tool_calls_token = '<|tools_prefix|>' -%}
152{%- set end_tool_calls_token = '<|tools_suffix|>' -%}
153
154{%- set ns = namespace(in_assistant=false, in_tool=false, in_inner=false, assistant_format=none) -%}
155
156{%- if messages and messages[0].role == 'system' -%}
157    {%- if "content" in messages[0] -%}
158        {%- if messages[0].content is string -%}
159            {{ system_token + messages[0].content + end_system_token }}
160        {%- elif messages[0].content is mapping and "text" in messages[0].content -%}
161            {{ system_token + messages[0].content.text + end_system_token }}
162        {%- else -%}
163            {{- raise_exception("Invalid system message") -}}
164        {%- endif -%}
165    {%- else -%}
166        {{- raise_exception("Invalid system message") -}}
167    {%- endif -%}
168    {%- set loop_messages = messages[1:] -%}
169{%- else -%}
170    {{ 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 }}
171    {%- set loop_messages = messages -%}
172{%- endif -%}
173
174{{ developer_token + 'Deliberation: ' }}
175{%- if enable_thinking is defined and enable_thinking -%}
176    {{ 'enabled\n' }}
177{%- else -%}
178    {{ 'disabled\n' }}
179{%- endif -%}
180{%- if tools is defined and tools -%}
181    {{ 'Tool Capabilities:\n' + render_tools(tools) }}
182{%- else -%}
183    {{ 'Tool Capabilities: disabled' }}
184{%- endif -%}
185{{ end_developer_token }}
186
187{%- for message in loop_messages -%}
188    {%- if message.role == 'user' -%}
189        {%- set ns.in_inner = false -%}
190        {%- if ns.in_tool -%}
191            {{ ']' }}
192            {%- set ns.in_tool = false -%}
193        {%- endif -%}
194        {%- if ns.in_assistant -%}
195            {{ end_assistant_token }}
196            {%- set ns.in_assistant = false -%}
197        {%- endif -%}
198        {%- if "content" in message -%}
199            {{ user_token }}
200            {%- if message.content is string -%}
201                {{ message.content }}
202            {%- elif message.content is mapping and "parts" in message.content -%}
203                {%- set parts = message.content.parts -%}
204                {%- for part in parts -%}
205                    {%- if part.type == "text" -%}
206                        {{ part.text }}
207                    {%- else -%}
208                        {{- raise_exception("Invalid user part: " + part.type) -}}
209                    {%- endif -%}
210                {%- endfor -%}
211            {%- else -%}
212                {{- raise_exception("Invalid user message: " + message.role) -}}
213            {%- endif -%}
214            {{ end_user_token }}
215        {%- endif -%}
216    {%- elif message.role == 'assistant' -%}
217        {%- if not ns.in_assistant -%}
218            {{ assistant_token }}
219            {%- set ns.in_assistant = true -%}
220        {%- endif -%}
221        {%- if "content" in message and message.content is not none -%}
222            {%- if message.content is string and (ns.assistant_format is none or ns.assistant_format == "string") -%}
223                {%- if ns.in_tool -%}
224                    {{ ']' }}
225                    {%- set ns.in_tool = false -%}
226                {%- endif -%}
227                {%- set ns.assistant_format = "string" -%}
228                {{ message.content }}
229            {%- elif message.content is mapping and "blocks" in message.content and (ns.assistant_format is none or ns.assistant_format == "mapping") -%}
230                {%- set ns.assistant_format = "mapping" -%}
231                {%- set blocks = message.content.blocks -%}
232                {%- for block in blocks -%}
233                    {%- if block.type == 'thoughts' -%}
234                        {%- if ns.in_tool -%}
235                            {{ ']' }}
236                            {%- set ns.in_tool = false -%}
237                        {%- endif -%}
238                        {%- if not ns.in_inner -%}
239                            {%- set ns.in_inner = true -%}
240                            {{ inner_token }}
241                        {%- endif -%}
242                        {{ block.text }}
243                    {%- elif block.type == 'tool_calls' -%}
244                        {%- if ns.in_tool -%}
245                            {{ ']' }}
246                            {%- set ns.in_tool = false -%}
247                        {%- endif -%}
248                        {%- if ns.in_inner and not loop.first and block.calls|length == 1 and block.calls[0].name == 'display_answers' -%}
249                            {%- set ns.in_inner = false -%}
250                            {{ outer_token }}
251                        {%- endif -%}
252                        {{ tool_calls_token + '[' }}
253                        {%- for tool_call in block.calls -%}
254                            {{- '{"' + tool_call.name + '": ' + tool_call.arguments + '}' }}
255                            {%- if not loop.last -%}
256                                {{- ", " }}
257                            {%- endif -%}
258                        {%- endfor -%}
259                        {{ ']' + end_tool_calls_token }}
260                    {%- elif block.type == 'tool_outputs' -%}
261                        {%- if ns.in_tool -%}
262                            {{- raise_exception("Cannot have both tool outputs as separate messages and tool outputs as blocks") -}}
263                        {%- endif -%}
264                        {{ '[' }}
265                        {%- for tool_output in block.outputs -%}
266                            {{- tool_output.output }}
267                            {%- if not loop.last -%}
268                                {{- ", " }}
269                            {%- endif -%}
270                        {%- endfor -%}
271                        {{- ']' }}
272                    {%- elif block.type == 'response' -%}
273                        {%- if ns.in_tool -%}
274                            {{ ']' }}
275                            {%- set ns.in_tool = false -%}
276                        {%- endif -%}
277                        {%- if (not loop.first and ns.in_inner) or (ns.in_assistant and ns.in_inner) -%}
278                            {%- set ns.in_inner = false -%}
279                            {{ outer_token }}
280                        {%- endif -%}
281                        {{ block.text }}
282                    {%- else -%}
283                        {{- raise_exception("Invalid assistant block type: " + block.type) -}}
284                    {%- endif -%}
285                {%- endfor -%}
286            {%- else -%}
287                {{- raise_exception("Invalid assistant content '" + message.content + "', expected " + ns.assistant_format) -}}
288            {%- endif -%}
289        {%- elif "tool_calls" not in message -%}
290            {{- raise_exception("Invalid assistant message " + message) -}}
291        {%- endif -%}
292        {%- if "tool_calls" in message and message.tool_calls -%}
293            {{ tool_calls_token + '[' }}
294            {%- for tool_call in message.tool_calls -%}
295                {%- if tool_call.type == 'function' -%}
296                    {%- set function = tool_call.function -%}
297                    {{- '{"' + function.name + '": ' + function.arguments + '}' }}
298                    {%- if not loop.last -%}
299                        {{- ", " }}
300                    {%- endif -%}
301                {%- else -%}
302                    {{- raise_exception("Invalid tool call type: " + tool_call.type) -}}
303                {%- endif -%}
304            {%- endfor -%}
305            {{ ']' + end_tool_calls_token }}
306        {%- endif -%}
307    {%- elif message.role == 'tool' -%}
308        {%- if not ns.in_assistant -%}
309            {{- raise_exception("Tool message outside of assistant") -}}
310        {%- endif -%}
311        {%- if not ns.in_tool -%}
312            {{ '[' }}
313            {%- set ns.in_tool = true -%}
314        {%- else -%}
315            {{ ", "}}
316        {%- endif -%}
317        {{ message.content }}
318    {%- else -%}
319        {{- raise_exception("Invalid message role") -}}
320    {%- endif -%}
321{%- endfor -%}
322{%- if ns.in_tool -%}
323    {{ ']' }}
324{%- endif -%}
325{%- if add_generation_prompt -%}
326    {{ assistant_token }}
327{%- endif -%}