kth8 commited on
Commit
bf6399b
·
verified ·
1 Parent(s): 0c3346b

Update chat_template.jinja

Browse files
Files changed (1) hide show
  1. chat_template.jinja +145 -9
chat_template.jinja CHANGED
@@ -1,10 +1,40 @@
1
  {{- bos_token -}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  {%- set keep_past_thinking = keep_past_thinking | default(false) -%}
 
 
3
  {%- set ns = namespace(system_prompt="") -%}
4
  {%- if messages[0]["role"] == "system" -%}
5
  {%- set ns.system_prompt = messages[0]["content"] -%}
6
  {%- set messages = messages[1:] -%}
7
  {%- endif -%}
 
 
8
  {%- if tools -%}
9
  {%- set ns.system_prompt = ns.system_prompt + ("\n" if ns.system_prompt else "") + "List of tools: [" -%}
10
  {%- for tool in tools -%}
@@ -18,28 +48,134 @@
18
  {%- endfor -%}
19
  {%- set ns.system_prompt = ns.system_prompt + "]" -%}
20
  {%- endif -%}
 
21
  {%- if ns.system_prompt -%}
22
  {{- "<|im_start|>system\n" + ns.system_prompt + "<|im_end|>\n" -}}
23
  {%- endif -%}
 
 
24
  {%- set ns.last_assistant_index = -1 -%}
25
  {%- for message in messages -%}
26
  {%- if message["role"] == "assistant" -%}
27
  {%- set ns.last_assistant_index = loop.index0 -%}
28
  {%- endif -%}
29
  {%- endfor -%}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  {%- for message in messages -%}
31
  {{- "<|im_start|>" + message["role"] + "\n" -}}
32
- {%- set content = message["content"] -%}
33
- {%- if content is not string -%}
34
- {%- set content = content | tojson -%}
35
- {%- endif -%}
36
- {%- if message["role"] == "assistant" and not keep_past_thinking and loop.index0 != ns.last_assistant_index -%}
37
- {%- if "</think>" in content -%}
38
- {%- set content = content.split("</think>")[-1] | trim -%}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  {%- endif -%}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  {%- endif -%}
41
- {{- content + "<|im_end|>\n" -}}
 
42
  {%- endfor -%}
 
43
  {%- if add_generation_prompt -%}
44
  {{- "<|im_start|>assistant\n" -}}
45
- {%- endif -%}
 
1
  {{- bos_token -}}
2
+ {#-
3
+ Fixed chat template for LiquidAI/LFM2.5-1.2B-Instruct.
4
+
5
+ Framework-agnostic — works with HuggingFace transformers apply_chat_template(),
6
+ vLLM, TGI, and any Jinja2-based rendering pipeline.
7
+
8
+ Bugs fixed in the upstream HF template:
9
+ 1. content=None on assistant messages renders "null" (via tojson) instead of empty
10
+ 2. tool_calls field on assistant messages is completely ignored — multi-turn tool
11
+ conversations lose all tool call context
12
+ 3. content=None on tool messages renders "null" instead of empty
13
+
14
+ Tool call reconstruction: when assistant messages carry a tool_calls field (OpenAI
15
+ format), this template reconstructs them into LFM's native format:
16
+ <|tool_call_start|>[func_name(param="value")]<|tool_call_end|>
17
+
18
+ Handles function.arguments as either a Python dict (pre-parsed) or a JSON string
19
+ (OpenAI wire format). Dict arguments get full pythonic formatting; JSON strings
20
+ are embedded as-is (best effort — no from_json filter in standard Jinja2).
21
+
22
+ Options:
23
+ keep_past_thinking (default: false) — preserve <think> blocks in non-final assistant turns
24
+
25
+ Ref: https://docs.liquid.ai/lfm/key-concepts/tool-use
26
+ Ref: https://huggingface.co/LiquidAI/LFM2.5-1.2B-Instruct/blob/main/chat_template.jinja
27
+ -#}
28
  {%- set keep_past_thinking = keep_past_thinking | default(false) -%}
29
+
30
+ {#- Extract system prompt from first message if present -#}
31
  {%- set ns = namespace(system_prompt="") -%}
32
  {%- if messages[0]["role"] == "system" -%}
33
  {%- set ns.system_prompt = messages[0]["content"] -%}
34
  {%- set messages = messages[1:] -%}
35
  {%- endif -%}
36
+
37
+ {#- Append tool definitions to system prompt -#}
38
  {%- if tools -%}
39
  {%- set ns.system_prompt = ns.system_prompt + ("\n" if ns.system_prompt else "") + "List of tools: [" -%}
40
  {%- for tool in tools -%}
 
48
  {%- endfor -%}
49
  {%- set ns.system_prompt = ns.system_prompt + "]" -%}
50
  {%- endif -%}
51
+
52
  {%- if ns.system_prompt -%}
53
  {{- "<|im_start|>system\n" + ns.system_prompt + "<|im_end|>\n" -}}
54
  {%- endif -%}
55
+
56
+ {#- Find last assistant index for think-stripping logic -#}
57
  {%- set ns.last_assistant_index = -1 -%}
58
  {%- for message in messages -%}
59
  {%- if message["role"] == "assistant" -%}
60
  {%- set ns.last_assistant_index = loop.index0 -%}
61
  {%- endif -%}
62
  {%- endfor -%}
63
+
64
+ {#- Macro: format a Python value in the style LFM was trained on -#}
65
+ {#- Strings → double quotes (via tojson), bools → True/False, None → None -#}
66
+ {%- macro pyval(v) -%}
67
+ {%- if v is none -%}None
68
+ {%- elif v is boolean and v -%}True
69
+ {%- elif v is boolean and not v -%}False
70
+ {%- elif v is string -%}{{ v | tojson }}
71
+ {%- elif v is mapping -%}{
72
+ {%- for mk, mv in v.items() -%}
73
+ {{ mk | tojson }}: {{ pyval(mv) }}
74
+ {%- if not loop.last -%}, {% endif -%}
75
+ {%- endfor -%}}
76
+ {%- elif v is iterable -%}[
77
+ {%- for item in v -%}
78
+ {{ pyval(item) }}
79
+ {%- if not loop.last -%}, {% endif -%}
80
+ {%- endfor -%}]
81
+ {%- else -%}{{ v }}
82
+ {%- endif -%}
83
+ {%- endmacro -%}
84
+
85
+ {#- Render each message -#}
86
  {%- for message in messages -%}
87
  {{- "<|im_start|>" + message["role"] + "\n" -}}
88
+
89
+ {%- if message["role"] == "assistant" -%}
90
+ {#- --- ASSISTANT MESSAGE --- -#}
91
+
92
+ {#- Get text content, treating None as empty -#}
93
+ {%- set text_content = message["content"] if "content" in message and message["content"] is string else "" -%}
94
+
95
+ {#- Reconstruct tool_calls into native format if present -#}
96
+ {%- set tc = message.get("tool_calls", none) if message.get is defined else message["tool_calls"] if "tool_calls" in message else none -%}
97
+ {%- if tc -%}
98
+ {%- set ns.tc_parts = [] -%}
99
+ {%- for call in tc -%}
100
+ {%- if call is mapping -%}
101
+ {%- set func = call["function"] -%}
102
+ {%- else -%}
103
+ {%- set func = call.function -%}
104
+ {%- endif -%}
105
+ {%- if func is mapping -%}
106
+ {%- set fname = func["name"] -%}
107
+ {%- set fargs = func.get("arguments", {}) if func.get is defined else func["arguments"] if "arguments" in func else {} -%}
108
+ {%- else -%}
109
+ {%- set fname = func.name -%}
110
+ {%- set fargs = func.arguments if func.arguments is defined else {} -%}
111
+ {%- endif -%}
112
+
113
+ {#- Arguments can be a dict (pre-parsed) or a JSON string (wire format) -#}
114
+ {%- if fargs is mapping -%}
115
+ {#- Dict: format as pythonic key=value pairs -#}
116
+ {%- set ns.kv_parts = [] -%}
117
+ {%- for k, v in fargs.items() -%}
118
+ {%- set ns.kv_parts = ns.kv_parts + [k + "=" + pyval(v)] -%}
119
+ {%- endfor -%}
120
+ {%- set ns.tc_parts = ns.tc_parts + [fname + "(" + ns.kv_parts | join(", ") + ")"] -%}
121
+ {%- elif fargs is string -%}
122
+ {#- JSON string: embed as-is (no from_json in standard Jinja2) -#}
123
+ {%- set ns.tc_parts = ns.tc_parts + [fname + "(" + fargs + ")"] -%}
124
+ {%- else -%}
125
+ {%- set ns.tc_parts = ns.tc_parts + [fname + "()"] -%}
126
+ {%- endif -%}
127
+ {%- endfor -%}
128
+ {%- set tool_call_str = "<|tool_call_start|>[" + ns.tc_parts | join(", ") + "]<|tool_call_end|>" -%}
129
+ {%- else -%}
130
+ {%- set tool_call_str = "" -%}
131
+ {%- endif -%}
132
+
133
+ {#- Combine: text content BEFORE tool call markers -#}
134
+ {#- This ordering is safe for think-stripping: -#}
135
+ {#- <think>...</think>text<|tool_call_start|>...<|tool_call_end|> -#}
136
+ {#- split("</think>")[-1] → text + markers preserved -#}
137
+ {%- if text_content and tool_call_str -%}
138
+ {%- set content = text_content + tool_call_str -%}
139
+ {%- elif tool_call_str -%}
140
+ {%- set content = tool_call_str -%}
141
+ {%- else -%}
142
+ {%- set content = text_content -%}
143
+ {%- endif -%}
144
+
145
+ {#- Strip thinking from non-final assistant messages -#}
146
+ {%- if not keep_past_thinking and loop.index0 != ns.last_assistant_index -%}
147
+ {%- if "</think>" in content -%}
148
+ {%- set content = content.split("</think>")[-1] | trim -%}
149
+ {%- endif -%}
150
  {%- endif -%}
151
+
152
+ {{- content -}}
153
+
154
+ {%- elif message["role"] == "tool" -%}
155
+ {#- --- TOOL RESULT MESSAGE --- -#}
156
+ {#- Handle content=None gracefully (render empty instead of "null") -#}
157
+ {%- set content = message["content"] if "content" in message else "" -%}
158
+ {%- if content is none -%}
159
+ {%- set content = "" -%}
160
+ {%- endif -%}
161
+ {%- if content is not string -%}
162
+ {%- set content = content | tojson -%}
163
+ {%- endif -%}
164
+ {{- content -}}
165
+
166
+ {%- else -%}
167
+ {#- --- USER / SYSTEM / OTHER MESSAGES --- -#}
168
+ {%- set content = message["content"] -%}
169
+ {%- if content is not string -%}
170
+ {%- set content = content | tojson -%}
171
+ {%- endif -%}
172
+ {{- content -}}
173
+
174
  {%- endif -%}
175
+
176
+ {{- "<|im_end|>\n" -}}
177
  {%- endfor -%}
178
+
179
  {%- if add_generation_prompt -%}
180
  {{- "<|im_start|>assistant\n" -}}
181
+ {%- endif -%}