File size: 9,013 Bytes
f3caab0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
{%- macro tool_to_json(tool) -%}
    {%- set ns_tool = namespace(first=true) -%}
    {{- '{' -}}
    {%- for k, v in tool.items() -%}
        {%- if k != 'defer_loading' and k != 'strict' -%}
            {%- if not ns_tool.first -%}{{- ', ' -}}{%- endif -%}
            {%- set ns_tool.first = false -%}
            {{- '"' ~ k ~ '": ' ~ (v | tojson(ensure_ascii=False)) -}}
        {%- endif -%}
    {%- endfor -%}
    {{- '}' -}}
{%- endmacro -%}
{%- set enable_thinking = enable_thinking if enable_thinking is defined else True %}
{%- set low_effort = low_effort if low_effort is defined else False %}
{%- if reasoning_effort is defined and reasoning_effort is not none %}
    {%- set low_effort = reasoning_effort == "low" %}
{%- endif %}
{%- set truncate_history_thinking = truncate_history_thinking if truncate_history_thinking is defined else True %}

{%- set ns = namespace(last_user_idx = -1) %}
{%- set loop_messages = messages %}
{%- for m in loop_messages %}
  {%- if m["role"] == "user" %}
    {%- set ns.last_user_idx = loop.index0 %}
  {%- endif %}
{%- endfor %}

{%- if messages[0]["role"] == "system" %}
    {%- set system_message = messages[0]["content"] %}
    {%- set loop_messages = messages[1:] %}
{%- else %}
    {%- set system_message = "" %}
    {%- set loop_messages = messages %}
{%- endif %}
{%- if not tools is defined %}
    {%- set tools = [] %}
{%- endif %}
{# Recompute last_user_idx relative to loop_messages after handling system #}
{%- set ns = namespace(last_user_idx = -1) %}
{%- for m in loop_messages %}
  {%- if m["role"] == "user" %}
    {%- set ns.last_user_idx = loop.index0 %}
  {%- endif %}
{%- endfor %}
{%- if system_message is defined %}
    {{- "<|im_start|>system\n" + system_message }}
{%- else %}
    {%- if tools is iterable and tools | length > 0 %}
        {{- "<|im_start|>system\n" }}
    {%- endif %}
{%- endif %}
{%- if tools is iterable and tools | length > 0 %}
    {%- if system_message is defined and system_message | length > 0 %}
        {{- "\n\n" }}
    {%- endif %}
    {{- "# Tools\n\nYou have access to the following functions:\n\n" }}
    {{- "<tools>" }}
    {%- for tool in tools %}
        {%- if tool.function is defined %}
            {%- set tool = tool.function %}
        {%- endif %}
        {%- if tool.defer_loading is not defined or not tool.defer_loading %}
            {{- '\n' ~ tool_to_json(tool) }}
        {%- endif %}
    {%- endfor %}
    {{- "\n</tools>" }}

    {{- '\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>' }}
{%- endif %}


{%- if system_message is defined %}
    {{- '<|im_end|>\n' }}
{%- else %}
    {%- if tools is iterable and tools | length > 0 %}
        {{- '<|im_end|>\n' }}
    {%- endif %}
{%- endif %}

{%- for message in loop_messages %}
    {%- if message.role == "assistant" %}
        {# Add reasoning content in to content field for unified processing below. #}
        {%- if message.reasoning_content is defined and message.reasoning_content is string and message.reasoning_content | trim | length > 0 %}
            {%- set content = "<think>\n" ~ message.reasoning_content ~ "\n</think>\n" ~ (message.content | default('', true)) %}
        {%- else %}
            {%- set content = message.content | default('', true) %}
            {%- if content is string -%}
                {# Allow downstream logic to to take care of broken thought, only handle coherent reasoning here. #}
                {%- if '<think>' not in content and '</think>' not in content -%}
                    {%- set content = "<think></think>" ~ content -%}
                {%- endif -%}
            {%- else -%}
                {%- set content = content -%}
            {%- endif -%}
        {%- endif %}
        {%- if message.tool_calls is defined and message.tool_calls is iterable and message.tool_calls | length > 0 %}
            {# Assistant message has tool calls. #}
            {{- '<|im_start|>assistant\n' }}
                {%- set include_content = not (truncate_history_thinking and loop.index0 < ns.last_user_idx) %}
                {%- if content is string and content | trim | length > 0 %}
                    {%- if include_content %}
                        {{- (content | trim) ~ '\n' -}}
                    {%- else %}
                        {%- set c = (content | string) %}
                        {%- if '</think>' in c %}
                            {# Keep only content after the last closing think. Also generation prompt causes this. #}
                            {%- set c = c.split('</think>')[-1] %}
                        {%- elif '<think>' in c %}
                            {# If <think> was opened but never closed, drop the trailing think segment #}
                            {%- set c = c.split('<think>')[0] %}
                        {%- endif %}
                        {%- set c = "<think></think>" ~ c | trim %}
                        {%- if c | length > 0 %}
                            {{- c ~ '\n' -}}
                        {%- endif %}
                    {%- endif %}
                {%- else %}
                    {{- "<think></think>" -}}
                {%- endif %}
                {%- for tool_call in message.tool_calls %}
                    {%- if tool_call.function is defined %}
                        {%- set tool_call = tool_call.function %}
                    {%- endif %}
                    {{- '<tool_call>\n<function=' ~ tool_call.name ~ '>\n' -}}
                        {%- if tool_call.arguments is defined %}
                            {%- for args_name, args_value in tool_call.arguments|items %}
                                {{- '<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>\n' -}}
                {%- endfor %}
                {{- '<|im_end|>\n' }}
        {%- else %}
            {# Assistant message doesn't have tool calls. #}
            {%- if not (truncate_history_thinking and loop.index0 < ns.last_user_idx) %}
                {{- '<|im_start|>assistant\n' ~ (content | default('', true) | string | trim) ~ '<|im_end|>\n' }}
            {%- else %}
                {%- set c = (content | default('', true) | string) %}
                {%- if '<think>' in c and '</think>' in c %}
                    {%- set c = "<think></think>" ~ c.split('</think>')[-1] %}
                {%- endif %}
                {%- set c = c | trim %}
                {%- if c | length > 0 %}
                    {{- '<|im_start|>assistant\n' ~ c ~ '<|im_end|>\n' }}
                {%- else %}
                    {{- '<|im_start|>assistant\n<|im_end|>\n' }}
                {%- endif %}
            {%- endif %}
        {%- endif %}
    {%- elif message.role == "user" or message.role == "system" %}
        {{- '<|im_start|>' + message.role + '\n' }}
        {%- set content = message.content | string %}
        {%- if message.role == "user" and loop.index0 == ns.last_user_idx and low_effort %}
            {{- content + '\n\n{reasoning effort: low}' }}
        {%- else %}
            {{- content }}
        {%- endif %}
        {{- '<|im_end|>\n' }}
    {%- elif message.role == "tool" %}
        {%- if loop.previtem and loop.previtem.role != "tool" %}
            {{- '<|im_start|>user\n' }}
        {%- endif %}
        {{- '<tool_response>\n' }}
        {{- message.content }}
        {{- '\n</tool_response>\n' }}
        {%- if not loop.last and loop.nextitem.role != "tool" %}
            {{- '<|im_end|>\n' }}
        {%- elif loop.last %}
            {{- '<|im_end|>\n' }}
        {%- endif %}
    {%- else %}
        {{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>\n' }}
    {%- endif %}
{%- endfor %}

{%- if add_generation_prompt %}
    {%- if enable_thinking %}
        {{- '<|im_start|>assistant\n<think>\n' }}
    {%- else %}
        {{- '<|im_start|>assistant\n<think></think>' }}
    {%- endif %}
{%- endif %}