{#- ----------‑‑‑ 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 = ''.format(HYTK) %} {%- set think_end_token = ''.format(HYTK) %} {%- set toolcalls_begin_token = ''.format(HYTK) %} {%- set toolcalls_end_token = ''.format(HYTK) %} {%- set toolcall_begin_token = ''.format(HYTK) %} {%- set toolcall_end_token = ''.format(HYTK) %} {%- set argkey_begin_token = ''.format(HYTK) %} {%- set argkey_end_token = ''.format(HYTK) %} {%- set argvalue_begin_token = ''.format(HYTK) %} {%- set argvalue_end_token = ''.format(HYTK) %} {%- set toolresponse_begin_token = ''.format(HYTK) %} {%- set toolresponse_end_token = ''.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 XML tags:' -}} {{- '\n\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\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 -}} {{- '\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 -%} {{- '' -}} {{- 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 %}