1+ {% - set image_count = namespace (value =0) %}
2+ {% - set video_count = namespace (value =0) %}
3+ {% - macro render_content (content , do_vision_count , is_system_content =false ) %}
4+ {% - if content is string %}
5+ {{- content }}
6+ {% - elif content is iterable and content is not mapping %}
7+ {% - for item in content %}
8+ {% - if 'image' in item or 'image_url' in item or item .type == 'image' %}
9+ {% - if is_system_content %}
10+ {{- raise_exception('System message cannot contain images.') }}
11+ {% - endif %}
12+ {% - if do_vision_count %}
13+ {% - set image_count .value = image_count .value + 1 %}
14+ {% - endif %}
15+ {% - if add_vision_id %}
16+ {{- 'Picture ' ~ image_count.value ~ ': ' }}
17+ {% - endif %}
18+ {{- '<|vision_start|><|image_pad|><|vision_end|>' }}
19+ {% - elif 'video' in item or item .type == 'video' %}
20+ {% - if is_system_content %}
21+ {{- raise_exception('System message cannot contain videos.') }}
22+ {% - endif %}
23+ {% - if do_vision_count %}
24+ {% - set video_count .value = video_count .value + 1 %}
25+ {% - endif %}
26+ {% - if add_vision_id %}
27+ {{- 'Video ' ~ video_count.value ~ ': ' }}
28+ {% - endif %}
29+ {{- '<|vision_start|><|video_pad|><|vision_end|>' }}
30+ {% - elif 'text' in item %}
31+ {{- item.text }}
32+ {% - else %}
33+ {{- raise_exception('Unexpected item type in content.') }}
34+ {% - endif %}
35+ {% - endfor %}
36+ {% - elif content is none or content is undefined %}
37+ {{- '' }}
38+ {% - else %}
39+ {{- raise_exception('Unexpected content type.') }}
40+ {% - endif %}
41+ {% - endmacro %}
42+ {% - if not messages %}
43+ {{- raise_exception('No messages provided.') }}
44+ {% - endif %}
45+ {% - if tools and tools is iterable and tools is not mapping %}
46+ {{- '<|im_start|>system\n' }}
47+ {{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
48+ {% - for tool in tools %}
49+ {{- "\n" }}
50+ {{- tool | tojson }}
51+ {% - endfor %}
52+ {{- "\n</tools>" }}
53+ {{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
54+ {% - if messages [0].role == 'system' %}
55+ {% - set content = render_content (messages [0].content , false , true )|trim %}
56+ {% - if content %}
57+ {{- '\n\n' + content }}
58+ {% - endif %}
59+ {% - endif %}
60+ {{- '<|im_end|>\n' }}
61+ {% - else %}
62+ {% - if messages [0].role == 'system' %}
63+ {% - set content = render_content (messages [0].content , false , true )|trim %}
64+ {{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
65+ {% - endif %}
66+ {% - endif %}
67+ {% - set ns = namespace (multi_step_tool =true , last_query_index =messages |length - 1) %}
68+ {% - for message in messages [::-1] %}
69+ {% - set index = (messages |length - 1) - loop .index 0 %}
70+ {% - if ns .multi_step_tool and message .role == "user" %}
71+ {% - set content = render_content (message .content , false )|trim %}
72+ {% - if not (content .startswith ('<tool_response>' ) and content .endswith ('</tool_response>' )) %}
73+ {% - set ns .multi_step_tool = false %}
74+ {% - set ns .last_query_index = index %}
75+ {% - endif %}
76+ {% - endif %}
77+ {% - endfor %}
78+ {% - if ns .multi_step_tool %}
79+ {{- raise_exception('No user query found in messages.') }}
80+ {% - endif %}
81+ {% - for message in messages %}
82+ {% - set content = render_content (message .content , true )|trim %}
83+ {% - if message .role == "system" %}
84+ {% - if not loop .first %}
85+ {{- raise_exception('System message must be at the beginning.') }}
86+ {% - endif %}
87+ {% - elif message .role == "user" %}
88+ {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
89+ {% - elif message .role == "assistant" %}
90+ {% - set reasoning_content = '' %}
91+ {% - if message .reasoning_content is string %}
92+ {% - set reasoning_content = message .reasoning_content %}
93+ {% - else %}
94+ {% - if '</think>' in content %}
95+ {% - set reasoning_content = content .split ('</think>' )[0].rstrip ('\n' ).split ('<think>' )[-1].lstrip ('\n' ) %}
96+ {% - set content = content .split ('</think>' )[-1].lstrip ('\n' ) %}
97+ {% - endif %}
98+ {% - endif %}
99+ {% - set reasoning_content = reasoning_content |trim %}
100+ {% - if loop .index 0 > ns .last_query_index %}
101+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
102+ {% - else %}
103+ {{- '<|im_start|>' + message.role + '\n' + content }}
104+ {% - endif %}
105+ {% - if message .tool_calls and message .tool_calls is iterable and message .tool_calls is not mapping %}
106+ {% - for tool_call in message .tool_calls %}
107+ {% - if tool_call .function is defined %}
108+ {% - set tool_call = tool_call .function %}
109+ {% - endif %}
110+ {% - if loop .first %}
111+ {% - if content |trim %}
112+ {{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
113+ {% - else %}
114+ {{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
115+ {% - endif %}
116+ {% - else %}
117+ {{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
118+ {% - endif %}
119+ {% - if tool_call .arguments is defined %}
120+ {% - for args_name , args_value in tool_call .arguments |items %}
121+ {{- '<parameter=' + args_name + '>\n' }}
122+ {% - 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 %}
123+ {{- args_value }}
124+ {{- '\n</parameter>\n' }}
125+ {% - endfor %}
126+ {% - endif %}
127+ {{- '</function>\n</tool_call>' }}
128+ {% - endfor %}
129+ {% - endif %}
130+ {{- '<|im_end|>\n' }}
131+ {% - elif message .role == "tool" %}
132+ {% - if loop .previtem and loop .previtem .role != "tool" %}
133+ {{- '<|im_start|>user' }}
134+ {% - endif %}
135+ {{- '\n<tool_response>\n' }}
136+ {{- content }}
137+ {{- '\n</tool_response>' }}
138+ {% - if not loop .last and loop .nextitem .role != "tool" %}
139+ {{- '<|im_end|>\n' }}
140+ {% - elif loop .last %}
141+ {{- '<|im_end|>\n' }}
142+ {% - endif %}
143+ {% - else %}
144+ {{- raise_exception('Unexpected message role.') }}
145+ {% - endif %}
146+ {% - endfor %}
147+ {% - if add_generation_prompt %}
148+ {{- '<|im_start|>assistant\n' }}
149+ {% - if enable_thinking is defined and enable_thinking is false %}
150+ {{- '<think>\n\n</think>\n\n' }}
151+ {% - else %}
152+ {{- '<think>\n' }}
153+ {% - endif %}
154+ {% - endif %}
0 commit comments