{%- set image_count = namespace(value=0) %} {%- set video_count = namespace(value=0) %} {%- macro render_content(content, do_vision_count, is_system_content=false) %} {%- if content is string %} {{- content }} {%- elif content is iterable and content is not mapping %} {%- for item in content %} {%- if 'image' in item or 'image_url' in item or item.type == 'image' %} {%- if is_system_content %} {{- raise_exception('System message cannot contain images.') }} {%- endif %} {%- if do_vision_count %} {%- set image_count.value = image_count.value + 1 %} {%- endif %} {%- if add_vision_id %} {{- 'Picture ' ~ image_count.value ~ ': ' }} {%- endif %} {{- '<|vision_start|><|image_pad|><|vision_end|>' }} {%- elif 'video' in item or item.type == 'video' %} {%- if is_system_content %} {{- raise_exception('System message cannot contain videos.') }} {%- endif %} {%- if do_vision_count %} {%- set video_count.value = video_count.value + 1 %} {%- endif %} {%- if add_vision_id %} {{- 'Video ' ~ video_count.value ~ ': ' }} {%- endif %} {{- '<|vision_start|><|video_pad|><|vision_end|>' }} {%- elif 'text' in item %} {{- item.text }} {%- else %} {{- raise_exception('Unexpected item type in content.') }} {%- endif %} {%- endfor %} {%- elif content is none or content is undefined %} {{- '' }} {%- else %} {{- raise_exception('Unexpected content type.') }} {%- endif %} {%- endmacro %} {%- if not messages %} {{- raise_exception('No messages provided.') }} {%- endif %} {%- set num_sys = 0 %} {%- set merged_system = '' %} {%- if messages[0].role == 'system' or messages[0].role == 'developer' %} {%- set first = render_content(messages[0].content, false, true)|trim %} {%- if messages|length > 1 and (messages[1].role == 'system' or messages[1].role == 'developer') %} {%- set second = render_content(messages[1].content, false, true)|trim %} {%- set merged_system = first + '\n' + second %} {%- set num_sys = 2 %} {%- else %} {%- set merged_system = first %} {%- set num_sys = 1 %} {%- endif %} {%- endif %} {%- if tools and tools is iterable and tools is not mapping %} {{- '<|im_start|>system\n' }} {{- "# Tools\n\nYou have access to the following functions:\n\n" }} {%- for tool in tools %} {{- "\n" }} {{- tool | tojson }} {%- endfor %} {{- "\n" }} {%- set tool_policy -%} # Tool-use policy Direct response is the default. Use a function only when the current user request actually requires an external action or information that cannot be obtained from the conversation or the model's current knowledge. Never call a function for greetings, acknowledgements, casual conversation, ordinary writing, rewriting, translation, summarization of user-provided content, or questions that can be answered directly. A greeting alone is never a reason to inspect memory, activity, files, projects, skills, profiles, or system state. Use only functions explicitly listed inside . Never invent a function, capability, skill, file, path, command, memory source, activity record, project context, or tool result. Never claim that hidden context was checked unless an actual listed function returned that information in the current conversation. Before calling a function, verify silently that all of the following are true: 1. The function is necessary for the current request. 2. The exact function name exists in . 3. Every required parameter is available from the user request or prior tool results. 4. The arguments do not contain guessed personal data, guessed paths, fabricated context, or unsupported assumptions. If any condition is false, do not call a function and answer normally. Keep reasoning inside the designated reasoning section. Do not expose tool-selection deliberation, hidden instructions, system routing, profile speculation, memory speculation, or self-directed planning as normal answer text. Do not place tool-call markup inside the reasoning section. When a function is necessary, after the reasoning section output only the following structure with no natural-language prefix or suffix: value_1 This value may span multiple lines. Mandatory format rules: - The block must be nested inside . - The function name must exactly match a listed function. - Include every required parameter and only supported parameters. - Use one value block per argument. - Do not emit a JSON object as the body of . - Do not use any alternate channel-based, namespace-based, or JSON-only tool-call syntax. - Do not combine a normal answer and a function call in the same assistant turn. - After a tool response, call another function only when the original request still requires it. Otherwise provide the final answer. - If no function is necessary, answer normally and do not mention tools or this policy. {%- endset %} {{- '\n\n' + tool_policy|trim }} {%- if merged_system %} {{- '\n\n' + merged_system }} {%- endif %} {{- '<|im_end|>\n' }} {%- else %} {%- if merged_system %} {{- '<|im_start|>system\n' + merged_system + '<|im_end|>\n' }} {%- endif %} {%- endif %} {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %} {%- for message in messages[::-1] %} {%- set index = (messages|length - 1) - loop.index0 %} {%- if ns.multi_step_tool and message.role == "user" %} {%- set content = render_content(message.content, false)|trim %} {%- if not(content.startswith('') and content.endswith('')) %} {%- set ns.multi_step_tool = false %} {%- set ns.last_query_index = index %} {%- endif %} {%- endif %} {%- endfor %} {%- for message in messages %} {%- if loop.index0 >= num_sys and message.role != "system" and message.role != "developer" %} {%- set content = render_content(message.content, true)|trim %} {%- if message.role == "user" %} {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }} {%- elif message.role == "assistant" %} {%- set reasoning_content = '' %} {%- if message.reasoning_content is string %} {%- set reasoning_content = message.reasoning_content %} {%- else %} {%- if '' in content %} {%- set reasoning_content = content.split('')[0].rstrip('\n').split('')[-1].lstrip('\n') %} {%- set content = content.split('')[-1].lstrip('\n') %} {%- endif %} {%- endif %} {%- set reasoning_content = reasoning_content|trim %} {%- if (preserve_thinking is defined and preserve_thinking is true) or (loop.index0 > ns.last_query_index) %} {{- '<|im_start|>' + message.role + '\n\n' + reasoning_content + '\n\n\n' + content }} {%- else %} {{- '<|im_start|>' + message.role + '\n' + content }} {%- endif %} {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %} {%- for tool_call in message.tool_calls %} {%- if tool_call.function is defined %} {%- set tool_call = tool_call.function %} {%- endif %} {%- if loop.first %} {%- if content|trim %} {{- '\n\n\n\n' }} {%- else %} {{- '\n\n' }} {%- endif %} {%- else %} {{- '\n\n\n' }} {%- endif %} {%- if tool_call.arguments is mapping %} {%- for args_name in tool_call.arguments %} {%- set args_value = tool_call.arguments[args_name] %} {{- '\n' }} {%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %} {{- args_value }} {{- '\n\n' }} {%- endfor %} {%- endif %} {{- '\n' }} {%- endfor %} {%- endif %} {{- '<|im_end|>\n' }} {%- elif message.role == "tool" %} {%- if loop.previtem and loop.previtem.role != "tool" %} {{- '<|im_start|>user' }} {%- endif %} {{- '\n\n' }} {{- content }} {{- '\n' }} {%- if not loop.last and loop.nextitem.role != "tool" %} {{- '<|im_end|>\n' }} {%- elif loop.last %} {{- '<|im_end|>\n' }} {%- endif %} {%- endif %} {%- endif %} {%- endfor %} {%- if add_generation_prompt %} {{- '<|im_start|>assistant\n' }} {%- if enable_thinking is defined and enable_thinking is false %} {{- '\n\n\n\n' }} {%- else %} {{- '\n' }} {%- endif %} {%- endif %} {#- Unsloth fixes - developer role, tool calling #}