| {%- 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<tools>" }}
|
| {%- for tool in tools %}
|
| {{- "\n" }}
|
| {{- tool | tojson }}
|
| {%- endfor %}
|
| {{- "\n</tools>" }}
|
| {%- 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 <tools>. 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 <tools>.
|
| 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:
|
|
|
| <tool_call>
|
| <function=example_function_name>
|
| <parameter=example_parameter_1>
|
| value_1
|
| </parameter>
|
| <parameter=example_parameter_2>
|
| This value may span multiple lines.
|
| </parameter>
|
| </function>
|
| </tool_call>
|
|
|
| Mandatory format rules:
|
| - The <function=...></function> block must be nested inside <tool_call></tool_call>.
|
| - The function name must exactly match a listed function.
|
| - Include every required parameter and only supported parameters.
|
| - Use one <parameter=name>value</parameter> block per argument.
|
| - Do not emit a JSON object as the body of <tool_call>.
|
| - 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('<tool_response>') and content.endswith('</tool_response>')) %}
|
| {%- 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 '</think>' in content %}
|
| {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| {%- set content = content.split('</think>')[-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<think>\n' + reasoning_content + '\n</think>\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<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| {%- else %}
|
| {{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| {%- endif %}
|
| {%- else %}
|
| {{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| {%- endif %}
|
| {%- if tool_call.arguments is mapping %}
|
| {%- for args_name in tool_call.arguments %}
|
| {%- set args_value = tool_call.arguments[args_name] %}
|
| {{- '<parameter=' + 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</parameter>\n' }}
|
| {%- endfor %}
|
| {%- endif %}
|
| {{- '</function>\n</tool_call>' }}
|
| {%- endfor %}
|
| {%- endif %}
|
| {{- '<|im_end|>\n' }}
|
| {%- elif message.role == "tool" %}
|
| {%- if loop.previtem and loop.previtem.role != "tool" %}
|
| {{- '<|im_start|>user' }}
|
| {%- endif %}
|
| {{- '\n<tool_response>\n' }}
|
| {{- content }}
|
| {{- '\n</tool_response>' }}
|
| {%- 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 %}
|
| {{- '<think>\n\n</think>\n\n' }}
|
| {%- else %}
|
| {{- '<think>\n' }}
|
| {%- endif %}
|
| {%- endif %}
|
|
|
| |