chat_template.jinja
7.9 KB · 158 lines · plaintext Raw
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 {%- set num_sys = 0 %}
46 {%- set merged_system = '' %}
47 {%- if messages[0].role == 'system' or messages[0].role == 'developer' %}
48 {%- set first = render_content(messages[0].content, false, true)|trim %}
49 {%- if messages|length > 1 and (messages[1].role == 'system' or messages[1].role == 'developer') %}
50 {%- set second = render_content(messages[1].content, false, true)|trim %}
51 {%- set merged_system = first + '\n' + second %}
52 {%- set num_sys = 2 %}
53 {%- else %}
54 {%- set merged_system = first %}
55 {%- set num_sys = 1 %}
56 {%- endif %}
57 {%- endif %}
58 {%- if tools and tools is iterable and tools is not mapping %}
59 {{- '<|im_start|>system\n' }}
60 {{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
61 {%- for tool in tools %}
62 {{- "\n" }}
63 {{- tool | tojson }}
64 {%- endfor %}
65 {{- "\n</tools>" }}
66 {{- '\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>' }}
67 {%- if merged_system %}
68 {{- '\n\n' + merged_system }}
69 {%- endif %}
70 {{- '<|im_end|>\n' }}
71 {%- else %}
72 {%- if merged_system %}
73 {{- '<|im_start|>system\n' + merged_system + '<|im_end|>\n' }}
74 {%- endif %}
75 {%- endif %}
76 {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
77 {%- for message in messages[::-1] %}
78 {%- set index = (messages|length - 1) - loop.index0 %}
79 {%- if ns.multi_step_tool and message.role == "user" %}
80 {%- set content = render_content(message.content, false)|trim %}
81 {%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
82 {%- set ns.multi_step_tool = false %}
83 {%- set ns.last_query_index = index %}
84 {%- endif %}
85 {%- endif %}
86 {%- endfor %}
87 {%- for message in messages %}
88 {%- if loop.index0 >= num_sys and message.role != "system" and message.role != "developer" %}
89 {%- set content = render_content(message.content, true)|trim %}
90 {%- if message.role == "user" %}
91 {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
92 {%- elif message.role == "assistant" %}
93 {%- set reasoning_content = '' %}
94 {%- if message.reasoning_content is string %}
95 {%- set reasoning_content = message.reasoning_content %}
96 {%- else %}
97 {%- if '</think>' in content %}
98 {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
99 {%- set content = content.split('</think>')[-1].lstrip('\n') %}
100 {%- endif %}
101 {%- endif %}
102 {%- set reasoning_content = reasoning_content|trim %}
103 {%- if (preserve_thinking is defined and preserve_thinking is true) or (loop.index0 > ns.last_query_index) %}
104 {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
105 {%- else %}
106 {{- '<|im_start|>' + message.role + '\n' + content }}
107 {%- endif %}
108 {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
109 {%- for tool_call in message.tool_calls %}
110 {%- if tool_call.function is defined %}
111 {%- set tool_call = tool_call.function %}
112 {%- endif %}
113 {%- if loop.first %}
114 {%- if content|trim %}
115 {{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
116 {%- else %}
117 {{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
118 {%- endif %}
119 {%- else %}
120 {{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
121 {%- endif %}
122 {%- if tool_call.arguments is mapping %}
123 {%- for args_name in tool_call.arguments %}
124 {%- set args_value = tool_call.arguments[args_name] %}
125 {{- '<parameter=' + args_name + '>\n' }}
126 {%- 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 %}
127 {{- args_value }}
128 {{- '\n</parameter>\n' }}
129 {%- endfor %}
130 {%- endif %}
131 {{- '</function>\n</tool_call>' }}
132 {%- endfor %}
133 {%- endif %}
134 {{- '<|im_end|>\n' }}
135 {%- elif message.role == "tool" %}
136 {%- if loop.previtem and loop.previtem.role != "tool" %}
137 {{- '<|im_start|>user' }}
138 {%- endif %}
139 {{- '\n<tool_response>\n' }}
140 {{- content }}
141 {{- '\n</tool_response>' }}
142 {%- if not loop.last and loop.nextitem.role != "tool" %}
143 {{- '<|im_end|>\n' }}
144 {%- elif loop.last %}
145 {{- '<|im_end|>\n' }}
146 {%- endif %}
147 {%- endif %}
148 {%- endif %}
149 {%- endfor %}
150 {%- if add_generation_prompt %}
151 {{- '<|im_start|>assistant\n' }}
152 {%- if enable_thinking is defined and enable_thinking is false %}
153 {{- '<think>\n\n</think>\n\n' }}
154 {%- else %}
155 {{- '<think>\n' }}
156 {%- endif %}
157 {%- endif %}
158 {#- Unsloth fixes - developer role, tool calling #}