Godwind
/

File size: 14,674 Bytes
7986ae3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
{#- ----------‑‑‑ special token variables ‑‑‑---------- -#}
{%- set HYTK = ':opensource' %}
{%- set hy_start_token = '<|hy_start{}|>'.format(HYTK) %}
{%- set hy_middle_token = '<|hy_middle{}|>'.format(HYTK) %}
{%- set hy_end_token = '<|hy_end{}|>'.format(HYTK) %}
{%- set think_begin_token = '<think{}>'.format(HYTK) %}
{%- set think_end_token = '</think{}>'.format(HYTK) %}
{%- set toolcalls_begin_token = '<tool_calls{}>'.format(HYTK) %}
{%- set toolcalls_end_token = '</tool_calls{}>'.format(HYTK) %}
{%- set toolcall_begin_token = '<tool_call{}>'.format(HYTK) %}
{%- set toolcall_end_token = '</tool_call{}>'.format(HYTK) %}
{%- set argkey_begin_token = '<arg_key{}>'.format(HYTK) %}
{%- set argkey_end_token = '</arg_key{}>'.format(HYTK) %}
{%- set argvalue_begin_token = '<arg_value{}>'.format(HYTK) %}
{%- set argvalue_end_token = '</arg_value{}>'.format(HYTK) %}
{%- set toolresponse_begin_token = '<tool_response{}>'.format(HYTK) %}
{%- set toolresponse_end_token = '</tool_response{}>'.format(HYTK) %}
{%- set reasoning_mode_token = '<|reasoning_mode{}|>'.format(HYTK) %}

{#- ----------‑‑‑ hyperparameters variables ‑‑‑---------- -#}
{%- if not add_generation_prompt is defined %}
    {%- set add_generation_prompt = false %}
{%- endif %}
{%- if not preserved_thinking is defined %}
    {%- if not tools %}
        {%- set preserved_thinking = false %}
    {%- else %}
        {%- set preserved_thinking = true %}
    {%- endif %}
{%- endif %}

{%- if not reasoning_effort is defined %}
    {%- set reasoning_effort = 'high' %}
{%- elif reasoning_effort not in ['high', 'no_think'] %}
    {%- if reasoning_effort is none %}
        {{- raise_exception('reasoning_effort error : None, should be no_think/high') }}
    {%- else %}
        {{- raise_exception('reasoning_effort error : ' + reasoning_effort + ', should be no_think/high') }}
    {%- endif %}
{%- endif %}

{%- if fallback_strategy is defined and fallback_strategy == 'reasoning_toolcall_retry' %}
    {%- set reasoning_effort = 'high' %}
    {%- set add_generation_prompt = false %}
{%- endif %}
{%- if not raw_last_assistant is defined %}
    {%- set raw_last_assistant = false %}
{%- endif %}

{%- 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 -%}

{%- macro render_content(content) -%}
    {%- if content is string -%}
        {{- content -}}
    {%- elif content is iterable and content is not mapping -%}
        {%- for item in content -%}
            {%- if item is mapping and item.type == 'text' -%}
                {{- item.text -}}
            {%- elif item is string -%}
                {{- item -}}
            {%- else -%}
                {{- item | string -}}
            {%- endif -%}
        {%- endfor -%}
    {%- elif content is none -%}
        {{- '' -}}
    {%- else -%}
        {{- content | string -}}
    {%- endif -%}
{%- endmacro -%}

{%- macro render_tools_prompt() -%}
    {{- '# Tools\n\nYou may call one or more functions to assist with the user query.' -}}
    {{- '\n\nYou are provided with function signatures within <tools></tools> XML tags:' -}}
    {{- '\n<tools>\n' -}}
    {%- set tool_ns = namespace(first=true) -%}
    {%- for tool in tools -%}
        {%- set t = tool['function'] if tool is mapping and 'function' in tool else tool -%}
        {%- if t.defer_loading is not defined or not t.defer_loading -%}
            {%- if not tool_ns.first -%}{{- '\n' -}}{%- endif -%}
            {%- set tool_ns.first = false -%}
            {{- tool_to_json(t) -}}
        {%- endif -%}
    {%- endfor -%}
    {{- '\n</tools>\n\n' -}}
    {{- 'For function call returns, you should first print ' ~ toolcalls_begin_token -}}
    {{- '\nFor each function call, you should return object like:\n' -}}
    {{- toolcall_begin_token ~ '{function-name}' -}}
    {{- argkey_begin_token ~ '{arg-key-1}' ~ argkey_end_token -}}
    {{- argvalue_begin_token ~ '{arg-value-1}' ~ argvalue_end_token -}}
    {{- argkey_begin_token ~ '{arg-key-2}' ~ argkey_end_token -}}
    {{- argvalue_begin_token ~ '{arg-value-2}' ~ argvalue_end_token -}}
    {{- '...' -}}
    {{- toolcall_end_token -}}
    {{- '\nAt the end of function call returns, you should print ' ~ toolcalls_end_token -}}
{%- endmacro -%}

{%- macro render_tool_response(message) -%}
    {%- set content = message['content'] -%}
    {%- if content is string -%}
        {{- toolresponse_begin_token ~ content ~ toolresponse_end_token -}}
    {%- elif content is iterable and content is not mapping and content and content[0] is mapping and content[0].type == 'tool_reference' -%}
        {{- toolresponse_begin_token -}}
        {{- '<tools>\n' -}}
        {%- for tr in content -%}
            {%- for tool in tools -%}
                {%- set t = tool['function'] if tool is mapping and 'function' in tool else tool -%}
                {%- if t.name == tr.name -%}
                    {{- tool_to_json(t) ~ '\n' -}}
                {%- endif -%}
            {%- endfor -%}
        {%- endfor -%}
        {{- '</tools>' -}}
        {{- toolresponse_end_token -}}
    {%- elif content is iterable and content is not mapping and content and content[0] is mapping and content[0].output is defined -%}
        {%- for tr in content -%}
            {{- toolresponse_begin_token ~ tr.output ~ toolresponse_end_token -}}
        {%- endfor -%}
    {%- else -%}
        {{- toolresponse_begin_token ~ render_content(content) ~ toolresponse_end_token -}}
    {%- endif -%}
{%- endmacro -%}

{#- consecutive tool messages are clustered into a single tool message whose content is the list of the original tool messages; the result is written to merged_ns.messages (a macro cannot return a list) -#}
{%- set merged_ns = namespace(messages=[]) %}
{%- macro merge_tool_responses(messages) -%}
    {%- set merge_ns = namespace(result=[], cluster=[]) -%}
    {%- for message in messages -%}
        {%- if message['role'] == 'tool' -%}
            {%- set merge_ns.cluster = merge_ns.cluster + [message] -%}
        {%- else -%}
            {%- if merge_ns.cluster -%}
                {%- set merge_ns.result = merge_ns.result + [{'role': 'tool', 'content': merge_ns.cluster}] -%}
                {%- set merge_ns.cluster = [] -%}
            {%- endif -%}
            {%- set merge_ns.result = merge_ns.result + [message] -%}
        {%- endif -%}
    {%- endfor -%}
    {%- if merge_ns.cluster -%}
        {%- set merge_ns.result = merge_ns.result + [{'role': 'tool', 'content': merge_ns.cluster}] -%}
    {%- endif -%}
    {%- set merged_ns.messages = merge_ns.result -%}
{%- endmacro -%}

{#- render one tool cluster, reordered to follow the tool_call order of the preceding assistant turn; any missing / ambiguous id falls back to the original tool message order for the whole cluster -#}
{%- macro render_tool_responses(tool_messages, tool_call_ids) -%}
    {%- set order_ns = namespace(ordered=[], matched=true) -%}
    {%- if tool_call_ids and tool_call_ids | length == tool_messages | length -%}
        {%- for tool_call_id in tool_call_ids -%}
            {%- set hit_ns = namespace(count=0, message=none) -%}
            {%- for tool_message in tool_messages -%}
                {%- set message_id = tool_message['tool_call_id'] if tool_message['tool_call_id'] is defined else tool_message['id'] -%}
                {%- if message_id is defined and message_id == tool_call_id -%}
                    {%- set hit_ns.count = hit_ns.count + 1 -%}
                    {%- set hit_ns.message = tool_message -%}
                {%- endif -%}
            {%- endfor -%}
            {%- if hit_ns.count == 1 -%}
                {%- set order_ns.ordered = order_ns.ordered + [hit_ns.message] -%}
            {%- else -%}
                {%- set order_ns.matched = false -%}
            {%- endif -%}
        {%- endfor -%}
    {%- else -%}
        {%- set order_ns.matched = false -%}
    {%- endif -%}
    {%- for tool_message in (order_ns.ordered if order_ns.matched else tool_messages) -%}
        {{- render_tool_response(tool_message) -}}
    {%- endfor -%}
{%- endmacro -%}

{%- set _ = merge_tool_responses(messages) %}
{%- set normed_messages = merged_ns.messages %}

{%- set ns = namespace(last_user_index=-1, has_leading_system=false) %}
{%- if normed_messages and normed_messages[0].role == 'system' %}
    {%- set ns.has_leading_system = true %}
{%- endif %}
{%- for message in normed_messages %}
    {%- if message['role'] == 'user' %}
        {%- set ns.last_user_index = loop.index0 %}
    {%- endif %}
{%- endfor %}

{#- no leading system: synthesize one for tools and/or reasoning_mode -#}
{%- if not ns.has_leading_system %}
    {{- hy_start_token ~ 'system' ~ hy_middle_token -}}
    {%- if tools %}
        {{- render_tools_prompt() -}}
    {%- endif %}
    {{- reasoning_mode_token ~ 'reasoning_effort:' ~ reasoning_effort -}}
    {{- hy_end_token -}}
{%- endif %}

{%- set last_ns = namespace(last_is_assistant=false) %}
{%- set prev_ns = namespace(tool_call_ids=[]) %}
{%- for message in normed_messages %}
    {%- if message['role'] == 'system' %}
        {{- hy_start_token ~ 'system' ~ hy_middle_token -}}
        {#- tools / reasoning_mode only attach to the leading system (messages[0]) -#}
        {%- if loop.first %}
            {%- if tools %}
                {{- render_tools_prompt() -}}
            {%- endif %}
            {%- set content = render_content(message['content']) -%}
            {%- if tools and content -%}
                {{- '\n\n' -}}
            {%- endif -%}
            {{- content -}}
            {{- reasoning_mode_token ~ 'reasoning_effort:' ~ reasoning_effort -}}
        {%- else %}
            {{- render_content(message['content']) -}}
        {%- endif %}
        {{- hy_end_token -}}
    {%- elif message['role'] == 'user' %}
        {{- hy_start_token ~ 'user' ~ hy_middle_token -}}
        {{- render_content(message['content']) -}}
        {{- hy_end_token -}}
    {%- elif message['role'] == 'assistant' %}
        {%- set content_body = render_content(message['content']) -%}
        {#- 'reasoning' takes precedence, fall back to 'reasoning_content' when it is empty -#}
        {%- set reasoning_text = '' %}
        {%- if message['reasoning'] is defined and message['reasoning'] is string and message['reasoning'] %}
            {%- set reasoning_text = message['reasoning'] %}
        {%- elif message['reasoning_content'] is defined and message['reasoning_content'] is string and message['reasoning_content'] %}
            {%- set reasoning_text = message['reasoning_content'] %}
        {%- endif %}
        {#- no_think overrides preserved_thinking: history is always emitted with empty think tags -#}
        {%- if reasoning_effort != 'no_think' and (preserved_thinking or loop.index0 > ns.last_user_index) and reasoning_text %}
            {%- set content = think_begin_token ~ reasoning_text ~ think_end_token ~ content_body %}
        {%- else %}
            {%- set content = think_begin_token ~ think_end_token ~ content_body %}
        {%- endif %}
        {{- hy_start_token ~ 'assistant' ~ hy_middle_token -}}
        {%- if message['tool_calls'] is defined and message['tool_calls'] %}
            {#- remember the tool_call ids of this turn to order the following tool responses; an incomplete id set means "do not reorder" -#}
            {%- set ids_ns = namespace(ids=[], complete=true) %}
            {%- for tool in message['tool_calls'] %}
                {%- set tool_call_id = tool['id'] if tool['id'] is defined and tool['id'] else tool['tool_call_id'] %}
                {%- if tool_call_id is defined and tool_call_id %}
                    {%- set ids_ns.ids = ids_ns.ids + [tool_call_id] %}
                {%- else %}
                    {%- set ids_ns.complete = false %}
                {%- endif %}
            {%- endfor %}
            {%- set prev_ns.tool_call_ids = ids_ns.ids if ids_ns.complete else [] %}
            {{- content -}}
            {{- toolcalls_begin_token -}}
            {%- for tool in message['tool_calls'] -%}
                {%- set func = tool['function'] if tool is mapping and 'function' in tool else tool -%}
                {%- set arguments = func['arguments'] -%}
                {{- toolcall_begin_token ~ func['name'] -}}
                {%- for key, value in arguments.items() -%}
                    {{- argkey_begin_token ~ key ~ argkey_end_token -}}
                    {%- if value is not string -%}
                        {%- set value = value | tojson(ensure_ascii=False) -%}
                    {%- endif -%}
                    {{- argvalue_begin_token ~ value ~ argvalue_end_token -}}
                {%- endfor -%}
                {{- toolcall_end_token -}}
            {%- endfor -%}
            {{- toolcalls_end_token -}}
        {%- else %}
            {%- if loop.last and raw_last_assistant %}
                {{- content_body -}}
            {%- else %}
                {{- content -}}
            {%- endif %}
        {%- endif %}
        {#- continuation / prefill: last assistant is still open, do not close with hy_end -#}
        {%- if not (loop.last and raw_last_assistant) %}
            {{- hy_end_token -}}
        {%- endif %}
    {%- elif message['role'] == 'tool' %}
        {{- hy_start_token ~ 'tool' ~ hy_middle_token -}}
        {{- render_tool_responses(message['content'], prev_ns.tool_call_ids) -}}
        {{- hy_end_token -}}
    {%- else %}
        {{- hy_start_token ~ message['role'] ~ hy_middle_token -}}
        {{- render_content(message['content']) -}}
        {{- hy_end_token -}}
    {%- endif %}
    {%- if message['role'] != 'assistant' or not (message['tool_calls'] is defined and message['tool_calls']) %}
        {%- set prev_ns.tool_call_ids = [] %}
    {%- endif %}
    {%- if loop.last and message['role'] == 'assistant' %}
        {%- set last_ns.last_is_assistant = true %}
    {%- endif %}
{%- endfor %}
{%- if add_generation_prompt %}
    {%- if not last_ns.last_is_assistant %}
        {%- if reasoning_effort == 'no_think' %}
            {{- hy_start_token ~ 'assistant' ~ hy_middle_token ~ think_begin_token ~ think_end_token -}}
        {%- else %}
            {{- hy_start_token ~ 'assistant' ~ hy_middle_token ~ think_begin_token -}}
        {%- endif %}
    {%- endif %}
{%- endif %}