jcbtc commited on
Commit
2528934
·
verified ·
1 Parent(s): 2de9afc

Add Step 3.7 chat template download

Browse files
README.md CHANGED
@@ -55,7 +55,7 @@ This FPX3/Q3 QualityPlus recipe was built for that constraint:
55
  - `14.35%` smaller than StepFun's original `Q3_K_L` GGUF split
56
  - up to 256K one-slot serving profile with q8_0 target KV and q8_0 draft KV
57
  - Step MTP Q8 draft support through `draft-mtp`
58
- - fixed Step tool/chat template using native `tool_response` observations and protocol-boundary escaping
59
 
60
  In practice, the original StepFun `Q3_K_L` local split was not a compact 3-bit-feeling model: it measured about `95.46 GiB`, or roughly `4.17 BPW` by effective size. This QualityPlus build is the one I would publish/use as the FPX3 lane.
61
 
@@ -149,6 +149,18 @@ Step-3.7-Flash-ROCmFPX-Q3-QualityPlus-00009-of-00009.gguf
149
 
150
  The Step MTP draft model is not duplicated here. Use the Q8 draft from [`notSnix/Step-3.7-Flash-MTP-Draft-GGUF`](https://huggingface.co/notSnix/Step-3.7-Flash-MTP-Draft-GGUF), for example `Step-3.7-Flash-MTP-Q8_0.gguf`.
151
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  ## Recommended Serving Profile
153
 
154
  The locally tested long-context profile:
@@ -204,13 +216,20 @@ Example shape:
204
  --spec-draft-n-min 0 \
205
  --spec-draft-p-min 0.75 \
206
  --spec-draft-p-split 0.10 \
207
- --chat-template-file step37-native-tool-response-template.jinja \
208
  --metrics
209
  ```
210
 
211
  ## Template Note
212
 
213
- The best local Step setup uses a Step-native tool/chat template that renders tool outputs as `tool_response` turns and escapes protocol-boundary tokens inside tool output. This is a general protocol-adapter fix: tool/file/search results stay observations instead of being flattened into user text.
 
 
 
 
 
 
 
214
 
215
  That matters for real agents because Step 3.7 can otherwise confuse tool output with conversation authority, especially in file/search-result injection cases.
216
 
 
55
  - `14.35%` smaller than StepFun's original `Q3_K_L` GGUF split
56
  - up to 256K one-slot serving profile with q8_0 target KV and q8_0 draft KV
57
  - Step MTP Q8 draft support through `draft-mtp`
58
+ - downloadable fixed Step tool/chat template using native `tool_response` observations and protocol-boundary escaping
59
 
60
  In practice, the original StepFun `Q3_K_L` local split was not a compact 3-bit-feeling model: it measured about `95.46 GiB`, or roughly `4.17 BPW` by effective size. This QualityPlus build is the one I would publish/use as the FPX3 lane.
61
 
 
149
 
150
  The Step MTP draft model is not duplicated here. Use the Q8 draft from [`notSnix/Step-3.7-Flash-MTP-Draft-GGUF`](https://huggingface.co/notSnix/Step-3.7-Flash-MTP-Draft-GGUF), for example `Step-3.7-Flash-MTP-Q8_0.gguf`.
151
 
152
+ This repo also includes the tested chat/tool template:
153
+
154
+ ```text
155
+ step37-native-tool-response-template.jinja
156
+ ```
157
+
158
+ Download it from the Files tab or directly from:
159
+
160
+ ```text
161
+ https://huggingface.co/jcbtc/Step-3.7-Flash-ROCmFPX-Q3-QualityPlus/resolve/main/step37-native-tool-response-template.jinja
162
+ ```
163
+
164
  ## Recommended Serving Profile
165
 
166
  The locally tested long-context profile:
 
216
  --spec-draft-n-min 0 \
217
  --spec-draft-p-min 0.75 \
218
  --spec-draft-p-split 0.10 \
219
+ --chat-template-file /path/to/step37-native-tool-response-template.jinja \
220
  --metrics
221
  ```
222
 
223
  ## Template Note
224
 
225
+ The best local Step setup uses the included [`step37-native-tool-response-template.jinja`](step37-native-tool-response-template.jinja) template. It renders tool outputs as `tool_response` turns and escapes protocol-boundary tokens inside tool output. This is a general protocol-adapter fix: tool/file/search results stay observations instead of being flattened into user text.
226
+
227
+ Download:
228
+
229
+ ```bash
230
+ curl -L -o step37-native-tool-response-template.jinja \
231
+ https://huggingface.co/jcbtc/Step-3.7-Flash-ROCmFPX-Q3-QualityPlus/resolve/main/step37-native-tool-response-template.jinja
232
+ ```
233
 
234
  That matters for real agents because Step 3.7 can otherwise confuse tool output with conversation authority, especially in file/search-result injection cases.
235
 
step37-native-tool-response-template.jinja ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% macro render_message_content(message) %}{% if message.content is none %}{{- '' }}{% elif message.content is string %}{{- message.content }}{% elif message.content is mapping %}{{- message.content['value'] if 'value' in message.content else message.content['text'] }}{% elif message.content is iterable %}{% set ns = namespace(needs_text_separator=false) %}{% for item in message.content %}{% if item.type == 'text' %}{% if ns.needs_text_separator %}{{- ' ' }}{% endif %}{{- item['value'] if 'value' in item else item['text'] }}{% set ns.needs_text_separator = true %}{% elif item.type == 'image' %}<im_patch>{% set ns.needs_text_separator = false %}{% endif %}{% endfor %}{% endif %}{% endmacro %}
2
+ {% macro render_tool_response_content(content) %}{{- content | replace('<|im_start|>', '&lt;|im_start|&gt;') | replace('<|im_end|>', '&lt;|im_end|&gt;') | replace('<tool_call>', '&lt;tool_call&gt;') | replace('</tool_call>', '&lt;/tool_call&gt;') | replace('<tool_response>', '&lt;tool_response&gt;') | replace('</tool_response>', '&lt;/tool_response&gt;') | replace('<function=', '&lt;function=') | replace('</function>', '&lt;/function&gt;') | replace('<parameter=', '&lt;parameter=') | replace('</parameter>', '&lt;/parameter&gt;') }}{% endmacro %}
3
+ {{bos_token}}{%- if tools %}
4
+ {{- '<|im_start|>system\n' }}
5
+ {%- if reasoning_effort is defined %}
6
+ {{- "Reasoning: " + reasoning_effort + '\n\n' }}
7
+ {%- endif %}
8
+ {%- if messages[0].role == 'system' %}
9
+ {{- render_message_content(messages[0]) + '\n\n' }}
10
+ {%- endif %}
11
+ {{- "# Tools\n\nYou have access to the following functions in JSONSchema format:\n\n<tools>" }}
12
+ {%- for tool in tools %}
13
+ {{- "\n" }}
14
+ {{- tool | tojson(ensure_ascii=False) }}
15
+ {%- endfor %}
16
+ {{- "\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=...>\n...\n</function> block must be nested within <tool_call>\n...\n</tool_call> XML tags\n- Required parameters MUST be specified\n</IMPORTANT><|im_end|>\n" }}
17
+ {%- else %}
18
+ {%- if messages[0].role == 'system' %}
19
+ {{- '<|im_start|>system\n' }}
20
+ {%- if reasoning_effort is defined %}
21
+ {{- "Reasoning: " + reasoning_effort + '\n\n' }}
22
+ {%- endif %}
23
+ {{- render_message_content(messages[0]) + '<|im_end|>\n' }}
24
+ {%- elif reasoning_effort is defined %}
25
+ {{- '<|im_start|>system\n' + "Reasoning: " + reasoning_effort + '\n\n' + '<|im_end|>\n' }}
26
+ {%- endif %}
27
+ {%- endif %}
28
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
29
+ {%- for message in messages[::-1] %}
30
+ {%- set index = (messages|length - 1) - loop.index0 %}
31
+ {%- if ns.multi_step_tool and message.role == "user" and render_message_content(message) is string and not(render_message_content(message).startswith('<tool_response>') and render_message_content(message).endswith('</tool_response>')) %}
32
+ {%- set ns.multi_step_tool = false %}
33
+ {%- set ns.last_query_index = index %}
34
+ {%- endif %}
35
+ {%- endfor %}
36
+ {%- for message in messages %}
37
+ {%- set content = render_message_content(message) %}
38
+ {%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
39
+ {%- set role_name = 'observation' if (message.role == "system" and not loop.first and message.name == 'observation') else message.role %}
40
+ {{- '<|im_start|>' + role_name + '\n' + content + '<|im_end|>' + '\n' }}
41
+ {%- elif message.role == "assistant" %}
42
+ {%- if message.reasoning_content is string %}
43
+ {%- set reasoning_content = message.reasoning_content %}
44
+ {%- else %}
45
+ {%- if '</think>' in content %}
46
+ {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
47
+ {%- set content = content.split('</think>')[-1].lstrip('\n') %}
48
+ {%- else %}
49
+ {%- set reasoning_content = '' %}
50
+ {%- endif %}
51
+ {%- endif %}
52
+ {%- set reasoning_content = reasoning_content|trim %}
53
+ {%- set content = content|trim %}
54
+ {%- if (preserve_thinking is defined and preserve_thinking is true) or (loop.index0 > ns.last_query_index) %}
55
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n' + content }}
56
+ {%- else %}
57
+ {{- '<|im_start|>' + message.role + '\n' + content }}
58
+ {%- endif %}
59
+ {%- if message.tool_calls %}
60
+ {%- for tool_call in message.tool_calls %}
61
+ {%- if tool_call.function is defined %}
62
+ {%- set tool_call = tool_call.function %}
63
+ {%- endif %}
64
+ {{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
65
+ {%- if tool_call.arguments is defined %}
66
+ {%- set arguments = tool_call.arguments | fromjson if tool_call.arguments is string else tool_call.arguments %}
67
+ {%- for args_name, args_value in arguments|items %}
68
+ {{- '<parameter=' + args_name + '>\n' }}
69
+ {%- set args_value = args_value | tojson(ensure_ascii=False) | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
70
+ {{- args_value }}
71
+ {{- '\n</parameter>\n' }}
72
+ {%- endfor %}
73
+ {%- endif %}
74
+ {{- '</function>\n</tool_call>' }}
75
+ {%- endfor %}
76
+ {%- endif %}
77
+ {{- '<|im_end|>\n' }}
78
+ {%- elif message.role == "tool" or message.role == "tool_response" %}
79
+ {%- if loop.first or (messages[loop.index0 - 1].role != "tool" and messages[loop.index0 - 1].role != "tool_response") %}
80
+ {{- '<|im_start|>tool_response\n' }}
81
+ {%- endif %}
82
+ {{- '<tool_response>' }}
83
+ {{- render_tool_response_content(content) }}
84
+ {{- '</tool_response>' }}
85
+ {%- if loop.last or (messages[loop.index0 + 1].role != "tool" and messages[loop.index0 + 1].role != "tool_response") %}
86
+ {{- '<|im_end|>\n' }}
87
+ {%- endif %}
88
+ {%- endif %}
89
+ {%- endfor %}
90
+ {%- if add_generation_prompt %}
91
+ {{- '<|im_start|>assistant\n' }}
92
+ {%- if enable_thinking is defined and enable_thinking is false %}
93
+ {{- '<think>\n\n</think>\n\n' }}
94
+ {%- else %}
95
+ {{- '<think>\n' }}
96
+ {%- endif %}
97
+ {%- endif %}