chat_template.jinja
7.6 KB · 154 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 {%- 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.index0 %}
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 (preserve_thinking is defined and preserve_thinking is true) or (loop.index0 > 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 | string if args_value is string else args_value | tojson | safe %}
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 %}