File size: 11,444 Bytes
4011095
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
{#- ======== Template Parameters ========  #}
{%- set add_generation_prompt = add_generation_prompt if add_generation_prompt is defined else true %}
{%- set provider_system_prompt = provider_system_prompt if provider_system_prompt else false %}
{%- set reasoning_effort = reasoning_effort if reasoning_effort is defined else "high" %}
{%- set think_render_option = think_render_option if think_render_option is defined else "interleaved" %}

{#- ======== System Block State ========  #}
{%- set sys_ns = namespace(is_first_block=true) -%}

{#- ======== Find last user message index ========  #}
{%- set last_user_idx = namespace(value=-1) -%}
{%- for message in messages -%}
    {%- if message.role == 'user' -%}
        {%- set last_user_idx.value = loop.index0 -%}
    {%- endif -%}
{%- endfor -%}

{#- ======== System messages renderers ========  #}
{%- macro render_system_message(user_system_messages) %}
    {%- if provider_system_prompt %}
        {%- if not sys_ns.is_first_block %}{{- "\n\n" }}{%- endif %}
        {%- set sys_ns.is_first_block = false %}
        {{- "## Provider System Prompt\n\nYou are Solar Open2 250B, a large language model trained by Upstage AI, a Korean startup. Your knowledge cutoff is 2026-02. The current date is " + strftime_now("%Y-%m-%d") + "." }}
    {%- endif -%}
    {%- if user_system_messages %}
        {%- if not sys_ns.is_first_block %}{{- "\n\n" }}{%- endif %}
        {%- set sys_ns.is_first_block = false %}
        {{- "## System Prompt" }}
        {%- for system_message in user_system_messages %}
            {{- "\n\n" }}
            {{- system_message }}
        {%- endfor %}
    {%- endif -%}
{%- endmacro %}

{%- macro render_tool_instruction(tools, is_last_block) %}
    {%- if not sys_ns.is_first_block %}{{- "\n\n" }}{%- endif %}
    {%- set sys_ns.is_first_block = false %}
    {{- "## Tools\n- You may invoke one or more tools to assist with the user's query." }}
    {{- "\n\n### Available Tools\n" }}
    {%- for tool in tools %}
        {{- "<|tool:start|>" }}
        {{- tool.function | tojson }}
        {{- "<|tool:end|>\n" }}
    {%- endfor %}
    {{- "\n### Tool Call Instruction\n" }}
    {{- "- If using a tool, any reasoning must strictly precede the call. Do not append any text after the tool call.\n" }}
    {{- "- If no tool is required, answer directly from your knowledge without ever mentioning the availability or absence of tools.\n" }}
    {{- "- Each tool call MUST use this following format: <|tool_call:start|>{example-tool-name}\n<|tool_arg:start|>{example-key-name-1}<|tool_arg:value|>{example-value-1}<|tool_arg:end|>\n<|tool_arg:start|>{example-key-name-2}<|tool_arg:value|>{example-value-2}<|tool_arg:end|>\n<|tool_call:end|>\n" }}
{%- endmacro %}

{#-
Input convention for `response_format`:
- Per the OpenAI Chat Completions API, `response_format` is an object of one of:
    {"type": "json_schema", "json_schema": {"name": ..., "schema": {...}, "strict": ...}}
    {"type": "json_object"}
- Only `response_format.json_schema.schema` (the inner JSON Schema) is injected
  into the prompt; the OpenAI wrapper fields (`name`, `strict`, ...) are dropped.
- For `json_object`, no schema exists, so we emit a generic JSON-only instruction.
- vLLM does NOT pass `response_format` to chat templates by default; it routes it
  to guided decoding instead. This template/whale opts into prompt injection,
  so callers serving the model directly via vLLM must opt in via
  `chat_template_kwargs` (or rely on whale's normalization).
-#}
{%- macro render_json_response_format_instruction(response_format) %}
    {%- if not sys_ns.is_first_block %}{{- "\n\n" }}{%- endif %}
    {%- set sys_ns.is_first_block = false %}
    {{- "## Output Format Constraint" }}
    {%- if response_format.type == "json_schema" %}
        {{- "\n\n- Your final response should follow the JSON schema:\n```json\n" }}
        {{- response_format.json_schema.schema | tojson }}
        {{- "\n```\n- Please ensure your answers adhere to this format and do not contain any unnecessary text." }}
    {%- elif response_format.type == "json_object" %}
        {{- "\n\n- Your final response must be a valid JSON object." }}
        {{- "\n- Do not include any text outside the JSON object." }}
    {%- endif %}
{%- endmacro %}

{#-
Input convention for `tool_arguments`:
- Must be a mapping (dict), NOT a JSON-encoded string.
- The OpenAI API spec defines `tool_calls[].function.arguments` as a JSON string,
  but vLLM, HuggingFace transformers tool-use templates (Qwen, Hermes, Llama, ...),
  and this template all expect the value to be parsed into a dict before rendering.
- vLLM normalizes this in `_postprocess_messages()` (vllm/entrypoints/chat_utils.py).
- Callers (whale, training pipelines that call `tokenizer.apply_chat_template`
  directly, ...) are responsible for parsing the JSON string into a dict before
  invoking the template.
-#}
{%- macro render_tool_arguments(tool_arguments) %}
    {%- for args_name, args_value in tool_arguments|items %}
        {{- "<|tool_arg:start|>"+ args_name + "<|tool_arg:value|>"}}
        {%- set args_value = args_value if args_value is string else (args_value | tojson | safe) %}
        {{- args_value }}
        {{- "<|tool_arg:end|>\n" }}
    {%- endfor %}
{%- endmacro %}

{#- ======== Render system message ========  #}
{%- set ns = namespace(system_messages=[]) -%}
{%- for message in messages -%}
    {%- if message.role == 'system' -%}
        {%- set ns.system_messages = ns.system_messages + [message.content] -%}
    {%- endif -%}
{%- endfor -%}

{%- if ns.system_messages or provider_system_prompt or tools or response_format -%}
    {{- "<|im:start|>system<|im:content|>" }}
        {{- render_system_message(ns.system_messages) }}
        {%- if tools -%}
            {{- render_tool_instruction(tools, not response_format) }}
        {%- endif %}
        {%- if response_format -%}
            {{- render_json_response_format_instruction(response_format) }}
        {%- endif %}
    {{- "<|im:end|>\n" }}
{%- endif -%}

{#- ======== Render main messages ========  #}
{%- for message in messages -%}
    {%- if message.role == 'user' -%}
         {{- "<|im:start|>user<|im:content|>" + message.content + "<|im:end|>\n" }}
    {%- elif message.role == 'tool' -%}
        {%- set prev_is_tool = loop.index0 > 0 and messages[loop.index0 - 1].role == 'tool' -%}
        {#- Only render at the start of a contiguous tool block; subsequent tool messages -#}
        {#- are already emitted (reordered) within the first one. -#}
        {%- if not prev_is_tool -%}
            {%- set start_idx = loop.index0 -%}
            {#- Collect contiguous tool messages starting at start_idx -#}
            {%- set tool_ns = namespace(items=[], scanning=true) -%}
            {%- for j in range(start_idx, messages | length) -%}
                {%- if tool_ns.scanning -%}
                    {%- if messages[j].role == 'tool' -%}
                        {%- set tool_ns.items = tool_ns.items + [messages[j]] -%}
                    {%- else -%}
                        {%- set tool_ns.scanning = false -%}
                    {%- endif -%}
                {%- endif -%}
            {%- endfor -%}

            {{- "<|im:start|>tool<|im:content|>" }}

            {%- set prev_msg = messages[start_idx - 1] if start_idx > 0 else none -%}
            {%- if prev_msg and prev_msg.role == 'assistant' and prev_msg.tool_calls -%}
                {#- Render tool responses in the order of the preceding assistant's tool_calls ids. -#}
                {%- set sep_ns = namespace(first=true, rendered_ids=[]) -%}
                {%- for tool_call in prev_msg.tool_calls -%}
                    {%- for tool_msg in tool_ns.items -%}
                        {%- if tool_msg.tool_call_id == tool_call.id -%}
                            {%- if not sep_ns.first -%}{{- "\n" }}{%- endif -%}
                            {%- set sep_ns.first = false -%}
                            {%- set sep_ns.rendered_ids = sep_ns.rendered_ids + [tool_msg.tool_call_id] -%}
                            {{- "<|tool_response:start|>" + tool_msg.content + "<|tool_response:end|>" }}
                        {%- endif -%}
                    {%- endfor -%}
                {%- endfor -%}
                {#- Append any orphan tool messages (no matching id) in their original order. -#}
                {%- for tool_msg in tool_ns.items -%}
                    {%- if tool_msg.tool_call_id not in sep_ns.rendered_ids -%}
                        {%- if not sep_ns.first -%}{{- "\n" }}{%- endif -%}
                        {%- set sep_ns.first = false -%}
                        {{- "<|tool_response:start|>" + tool_msg.content + "<|tool_response:end|>" }}
                    {%- endif -%}
                {%- endfor -%}
            {%- else -%}
                {%- for tool_msg in tool_ns.items -%}
                    {%- if not loop.first -%}{{- "\n" }}{%- endif -%}
                    {{- "<|tool_response:start|>" + tool_msg.content + "<|tool_response:end|>" }}
                {%- endfor -%}
            {%- endif -%}

            {{- "\n<|im:end|>\n" }}
        {%- endif -%}
    {%- elif message.role == 'assistant' -%}
        {{- "<|im:start|>assistant<|im:content|>" }}

        {#- ======== Resolve reasoning field (reasoning or reasoning_content) ========  #}
        {%- if message.reasoning is defined -%}
            {%- set reasoning = message.reasoning -%}
        {%- elif message.reasoning_content is defined -%}
            {%- set reasoning = message.reasoning_content -%}
        {%- else -%}
            {%- set reasoning = none -%}
        {%- endif -%}

        {#- ======== Assistant Thinking ========  #}
        {%- if think_render_option == "preserved" -%}
            {%- if reasoning -%}
                {{- "<|think:start|>" + reasoning + "<|think:end|>" }}
            {%- else -%}
                {{- "<|think:start|><|think:end|>" }}
            {%- endif -%}
        {%- elif think_render_option == "interleaved" -%}
            {%- if reasoning and loop.index0 > last_user_idx.value -%}
                {{- "<|think:start|>" + reasoning + "<|think:end|>" }}
            {%- else -%}
                {{- "<|think:start|><|think:end|>" }}
            {%- endif -%}
        {%- endif -%}

        {#- ======== Assistant Messages ========  #}
        {%- if message.content -%}
            {{- message.content }}
        {%- endif -%}

        {#- ======== Assistant Tool calls ========  #}
        {%- if message.tool_calls -%}
            {%- for tool_call in message.tool_calls -%}
                {%- if not loop.first -%}{{- "\n" }}{%- endif -%}
                {{- "<|tool_call:start|>" + tool_call.function.name + "\n" }}
                {#- ======== FIXME: QWEN3.5 Method ========  #}
                {%- if tool_call.function.arguments is defined %}
                    {{- render_tool_arguments(tool_call.function.arguments) }}
                {%- endif %}
                {{- "<|tool_call:end|>" }}
            {%- endfor -%}
            {{- "\n" }}
        {%- endif -%}
        {{- "<|im:end|>\n" }}
    {%- endif -%}
{%- endfor -%}

{%- if add_generation_prompt -%}
    {%- if reasoning_effort in ["medium", "high"] -%}
        {{- "<|im:start|>assistant<|im:content|><|think:start|>" }}
    {%- else -%}
        {{- "<|im:start|>assistant<|im:content|><|think:start|><|think:end|>" }}
    {%- endif -%}
{%- endif -%}