LLM for Unity  v2.4.2
Create characters in Unity with LLMs!
Loading...
Searching...
No Matches
LLMChatTemplates.cs
Go to the documentation of this file.
1
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.IO;
6using System.Text;
7
8namespace LLMUnity
9{
14 public abstract class ChatTemplate
15 {
17 public static string DefaultTemplate;
24 public static Dictionary<string, ChatTemplate> templates;
26 public static ChatTemplate[] templateClasses;
27 public static Dictionary<string, string> templatesDescription;
28 public static Dictionary<string, string> modelTemplates;
29 public static Dictionary<string, string> chatTemplates;
31
32 static ChatTemplate()
33 {
34 DefaultTemplate = "chatml";
35
36 templateClasses = new ChatTemplate[]
37 {
38 new ChatMLTemplate(),
39 new AlpacaTemplate(),
40 new GemmaTemplate(),
45 new LLama2Template(),
46 new Phi3_5Template(),
47 new Phi3Template(),
48 new Phi2Template(),
52 new VicunaTemplate(),
53 new ZephyrTemplate(),
54 };
55
56 templates = new Dictionary<string, ChatTemplate>();
57 templatesDescription = new Dictionary<string, string>();
58 modelTemplates = new Dictionary<string, string>();
59 chatTemplates = new Dictionary<string, string>();
60 foreach (ChatTemplate template in templateClasses)
61 {
62 if (templates.ContainsKey(template.GetName())) LLMUnitySetup.LogError($"{template.GetName()} already in templates");
63 templates[template.GetName()] = template;
64 if (templatesDescription.ContainsKey(template.GetDescription())) LLMUnitySetup.LogError($"{template.GetDescription()} already in templatesDescription");
65 templatesDescription[template.GetDescription()] = template.GetName();
66 foreach (string match in template.GetNameMatches())
67 {
68 if (modelTemplates.ContainsKey(match)) LLMUnitySetup.LogError($"Name for {template.GetName()} already in modelTemplates");
69 modelTemplates[match] = template.GetName();
70 }
71 foreach (string match in template.GetChatTemplateMatches())
72 {
73 if (chatTemplates.ContainsKey(match)) LLMUnitySetup.LogError($"Chat template for {template.GetName()} already in chatTemplates");
74 chatTemplates[match] = template.GetName();
75 }
76 }
77 }
78
85 public static string FromName(string name)
86 {
87 if (name == null) return null;
88 string nameLower = name.ToLower();
89 foreach (var pair in modelTemplates)
90 {
91 if (nameLower.Contains(pair.Key)) return pair.Value;
92 }
93 return null;
94 }
95
101 public static string FromTemplate(string template)
102 {
103 if (template == null) return null;
104 string templateTrim = template.Trim();
105 if (chatTemplates.TryGetValue(templateTrim, out string value))
106 return value;
107 return null;
108 }
109
120 public static string FromGGUF(string path)
121 {
122 return FromGGUF(new GGUFReader(path), path);
123 }
124
125 public static string FromGGUF(GGUFReader reader, string path)
126 {
127 string name;
128 name = FromTemplate(reader.GetStringField("tokenizer.chat_template"));
129 if (name != null) return name;
130
131 name = FromName(reader.GetStringField("general.name"));
132 if (name != null) return name;
133
134 name = FromName(Path.GetFileNameWithoutExtension(path));
135 if (name != null) return name;
136
137 LLMUnitySetup.Log("No chat template could be matched, fallback to ChatML");
138 return DefaultTemplate;
139 }
140
146 public static ChatTemplate GetTemplate(string template)
147 {
148 return templates[template];
149 }
150
152 public virtual string GetName() { return ""; }
154 public virtual string GetDescription() { return ""; }
156 public virtual string[] GetNameMatches() { return new string[] {}; }
158 public virtual string[] GetChatTemplateMatches() { return new string[] {}; }
160 public virtual string[] GetStop(string playerName, string AIName) { return new string[] {}; }
161
162 protected virtual string PromptPrefix() { return ""; }
163 protected virtual string SystemPrefix() { return ""; }
164 protected virtual string SystemSuffix() { return ""; }
165 protected virtual string PlayerPrefix(string playerName) { return ""; }
166 protected virtual string AIPrefix(string AIName) { return ""; }
167 protected virtual string PrefixMessageSeparator() { return ""; }
168 protected virtual string RequestPrefix() { return ""; }
169 protected virtual string RequestSuffix() { return ""; }
170 protected virtual string PairSuffix() { return ""; }
171
172 protected virtual bool SystemPromptSupported() { return true; }
173
179 public virtual string ComputePrompt(List<ChatMessage> chatMessages, string playerName, string AIName, bool endWithPrefix = true)
180 {
181 List<ChatMessage> messages = chatMessages;
182 if (!SystemPromptSupported())
183 {
184 if (chatMessages[0].role == "system")
185 {
186 string firstUserMessage = chatMessages[0].content;
187 int newStart = 1;
188 if (chatMessages.Count > 1)
189 {
190 if (firstUserMessage != "") firstUserMessage += "\n\n";
191 firstUserMessage += chatMessages[1].content;
192 newStart = 2;
193 }
194 messages = new List<ChatMessage>(){new ChatMessage { role = playerName, content = firstUserMessage }};
195 messages.AddRange(chatMessages.GetRange(newStart, chatMessages.Count - newStart));
196 }
197 }
198
199 string chatPrompt = PromptPrefix();
200 int start = 0;
201 if (messages[0].role == "system")
202 {
203 chatPrompt += RequestPrefix() + SystemPrefix() + messages[0].content + SystemSuffix();
204 start = 1;
205 }
206 for (int i = start; i < messages.Count; i += 2)
207 {
208 if (i > start || start == 0) chatPrompt += RequestPrefix();
209 chatPrompt += PlayerPrefix(messages[i].role) + PrefixMessageSeparator() + messages[i].content + RequestSuffix();
210 if (i < messages.Count - 1)
211 {
212 chatPrompt += AIPrefix(messages[i + 1].role) + PrefixMessageSeparator() + messages[i + 1].content + PairSuffix();
213 }
214 }
215 if (endWithPrefix) chatPrompt += AIPrefix(AIName);
216 return chatPrompt;
217 }
218
219 protected string[] AddStopNewlines(string[] stop)
220 {
221 List<string> stopWithNewLines = new List<string>();
222 foreach (string stopword in stop)
223 {
224 stopWithNewLines.Add(stopword);
225 stopWithNewLines.Add("\n" + stopword);
226 }
227 return stopWithNewLines.ToArray();
228 }
229 }
230
236 {
237 public override string GetName() { return "chatml"; }
238 public override string GetDescription() { return "chatml (most widely used)"; }
239 public override string[] GetNameMatches() { return new string[] {"chatml", "hermes", "qwen"}; }
240 public override string[] GetChatTemplateMatches() { return new string[] {"{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}"}; }
241
242 protected override string SystemPrefix() { return "<|im_start|>system\n"; }
243 protected override string SystemSuffix() { return "<|im_end|>\n"; }
244 protected override string PlayerPrefix(string playerName) { return $"<|im_start|>{playerName}\n"; }
245 protected override string AIPrefix(string AIName) { return $"<|im_start|>{AIName}\n"; }
246 protected override string RequestSuffix() { return "<|im_end|>\n"; }
247 protected override string PairSuffix() { return "<|im_end|>\n"; }
248
249 public override string[] GetStop(string playerName, string AIName)
250 {
251 return AddStopNewlines(new string[] { "<|im_start|>", "<|im_end|>" });
252 }
253 }
254
260 {
261 public override string GetName() { return "llama"; }
262 public override string GetDescription() { return "llama 2"; }
263
264 protected override string SystemPrefix() { return "<<SYS>>\n"; }
265 protected override string SystemSuffix() { return "\n<</SYS>> "; }
266 protected override string RequestPrefix() { return "<s>[INST] "; }
267 protected override string RequestSuffix() { return " [/INST]"; }
268 protected override string PairSuffix() { return " </s>"; }
269
270 public override string[] GetStop(string playerName, string AIName)
271 {
272 return AddStopNewlines(new string[] { "[INST]", "[/INST]" });
273 }
274 }
275
281 {
282 public override string GetName() { return "llama chat"; }
283 public override string GetDescription() { return "llama 2 (chat)"; }
284 public override string[] GetNameMatches() { return new string[] {"llama-2", "llama v2"}; }
285
286 protected override string PlayerPrefix(string playerName) { return "### " + playerName + ":"; }
287 protected override string AIPrefix(string AIName) { return "### " + AIName + ":"; }
288 protected override string PrefixMessageSeparator() { return " "; }
289
290 public override string[] GetStop(string playerName, string AIName)
291 {
292 return AddStopNewlines(new string[] { "[INST]", "[/INST]", "###" });
293 }
294 }
295
301 {
302 public override string GetName() { return "llama3 chat"; }
303 public override string GetDescription() { return "llama 3 (chat)"; }
304 public override string[] GetNameMatches() { return new string[] {"llama-3", "llama v3"}; }
305 public override string[] GetChatTemplateMatches() { return new string[] {"{% set loop_messages = messages %}{% for message in loop_messages %}{% set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n'+ message['content'] | trim + '<|eot_id|>' %}{% if loop.index0 == 0 %}{% set content = bos_token + content %}{% endif %}{{ content }}{% endfor %}{{ '<|start_header_id|>assistant<|end_header_id|>\n\n' }}"};}
306
307 protected override string SystemPrefix() { return "<|start_header_id|>system<|end_header_id|>\n\n"; }
308 protected override string SystemSuffix() { return "<|eot_id|>"; }
309
310 protected override string RequestSuffix() { return "<|eot_id|>"; }
311 protected override string PairSuffix() { return "<|eot_id|>"; }
312
313 protected override string PlayerPrefix(string playerName) { return $"<|start_header_id|>{playerName}<|end_header_id|>\n\n"; }
314 protected override string AIPrefix(string AIName) { return $"<|start_header_id|>{AIName}<|end_header_id|>\n\n"; }
315
316 public override string[] GetStop(string playerName, string AIName)
317 {
318 return AddStopNewlines(new string[] { "<|eot_id|>" });
319 }
320 }
321
327 {
328 public override string GetName() { return "mistral instruct"; }
329 public override string GetDescription() { return "mistral instruct"; }
330
331 protected override string SystemPrefix() { return ""; }
332 protected override string SystemSuffix() { return "\n\n"; }
333 protected override string RequestPrefix() { return "[INST] "; }
334 protected override string RequestSuffix() { return " [/INST]"; }
335 protected override string PairSuffix() { return "</s>"; }
336
337 public override string[] GetStop(string playerName, string AIName)
338 {
339 return AddStopNewlines(new string[] { "</s>", "[INST]", "[/INST]" });
340 }
341 }
342
348 {
349 public override string GetName() { return "mistral chat"; }
350 public override string GetDescription() { return "mistral (chat)"; }
351 public override string[] GetNameMatches() { return new string[] {"mistral"}; }
352 public override string[] GetChatTemplateMatches() { return new string[] {"{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '[INST] ' + message['content'] + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ message['content'] + eos_token}}{% else %}{{ raise_exception('Only user and assistant roles are supported!') }}{% endif %}{% endfor %}"}; }
353
354 protected override string PlayerPrefix(string playerName) { return "### " + playerName + ":"; }
355 protected override string AIPrefix(string AIName) { return "### " + AIName + ":"; }
356 protected override string PrefixMessageSeparator() { return " "; }
357
358 public override string[] GetStop(string playerName, string AIName)
359 {
360 return AddStopNewlines(new string[] { "</s>", "[INST]", "[/INST]", "###" });
361 }
362 }
363
369 {
370 public override string GetName() { return "gemma"; }
371 public override string GetDescription() { return "gemma"; }
372 public override string[] GetNameMatches() { return new string[] {"gemma"}; }
373
374 protected override string RequestSuffix() { return "<end_of_turn>\n"; }
375 protected override string PairSuffix() { return "<end_of_turn>\n"; }
376
377 protected override string PlayerPrefix(string playerName) { return "<start_of_turn>" + playerName + "\n"; }
378 protected override string AIPrefix(string AIName) { return "<start_of_turn>" + AIName + "\n"; }
379
380 protected override bool SystemPromptSupported() { return false; }
381
382 public override string[] GetStop(string playerName, string AIName)
383 {
384 return AddStopNewlines(new string[] { "<start_of_turn>", "<end_of_turn>" });
385 }
386 }
387
393 {
394 public override string GetName() { return "alpaca"; }
395 public override string GetDescription() { return "alpaca (best alternative)"; }
396 public override string[] GetNameMatches() { return new string[] {"alpaca"}; }
397
398 protected override string SystemSuffix() { return "\n\n"; }
399 protected override string RequestSuffix() { return "\n"; }
400 protected override string PlayerPrefix(string playerName) { return "### " + playerName + ":"; }
401 protected override string AIPrefix(string AIName) { return "### " + AIName + ":"; }
402 protected override string PrefixMessageSeparator() { return " "; }
403 protected override string PairSuffix() { return "\n"; }
404
405 public override string[] GetStop(string playerName, string AIName)
406 {
407 return AddStopNewlines(new string[] { "###" });
408 }
409 }
410
416 {
417 public override string GetName() { return "vicuna"; }
418 public override string GetDescription() { return "vicuna"; }
419 public override string[] GetNameMatches() { return new string[] {"vicuna"}; }
420 public override string[] GetChatTemplateMatches() { return new string[] {"{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in messages %}{% if message['role'] == 'system' %}{{message['content'] + ' '}}{% elif message['role'] == 'user' %}{{ 'USER: ' + message['content'] + ' '}}{% elif message['role'] == 'assistant' %}{{ 'ASSISTANT: ' + message['content'] + ' '}}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ 'ASSISTANT: '}}{% endif %}"}; }
421
422 protected override string SystemSuffix() { return "\n"; }
423 protected override string PlayerPrefix(string playerName) { return "\n" + playerName + ":"; }
424 protected override string AIPrefix(string AIName) { return "\n" + AIName + ":"; }
425 protected override string PrefixMessageSeparator() { return " "; }
426
427 public override string[] GetStop(string playerName, string AIName)
428 {
429 return AddStopNewlines(new string[] { playerName + ":", AIName + ":" });
430 }
431 }
432
438 {
439 public override string GetName() { return "phi"; }
440 public override string GetDescription() { return "phi-2"; }
441 public override string[] GetNameMatches() { return new string[] {"phi-2"}; }
442
443 protected override string SystemSuffix() { return "\n\n"; }
444 protected override string RequestSuffix() { return "\n"; }
445 protected override string PlayerPrefix(string playerName) { return playerName + ":"; }
446 protected override string AIPrefix(string AIName) { return AIName + ":"; }
447 protected override string PrefixMessageSeparator() { return " "; }
448 protected override string PairSuffix() { return "\n"; }
449
450 public override string[] GetStop(string playerName, string AIName)
451 {
452 return AddStopNewlines(new string[] { playerName + ":", AIName + ":" });
453 }
454 }
455
461 {
462 public override string GetName() { return "phi-3"; }
463 public override string GetDescription() { return "phi-3"; }
464 public override string[] GetNameMatches() { return new string[] {"phi-3"}; }
465 public override string[] GetChatTemplateMatches() { return new string[] {"{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') %}{{'<|user|>' + '\n' + message['content'] + '<|end|>' + '\n' + '<|assistant|>' + '\n'}}{% elif (message['role'] == 'assistant') %}{{message['content'] + '<|end|>' + '\n'}}{% endif %}{% endfor %}"}; }
466
467 protected override string PlayerPrefix(string playerName) { return $"<|user|>\n"; }
468 protected override string AIPrefix(string AIName) { return $"<|assistant|>\n"; }
469 protected override string RequestSuffix() { return "<|end|>\n"; }
470 protected override string PairSuffix() { return "<|end|>\n"; }
471
472 protected override bool SystemPromptSupported() { return false; }
473
474 public override string[] GetStop(string playerName, string AIName)
475 {
476 return AddStopNewlines(new string[] { "<|end|>", "<|user|>", "<|assistant|>" });
477 }
478 }
479
485 {
486 public override string GetName() { return "phi-3.5"; }
487 public override string GetDescription() { return "phi-3.5"; }
488 public override string[] GetNameMatches() { return new string[] {"phi-3.5"}; }
489 public override string[] GetChatTemplateMatches() { return new string[] {"{% for message in messages %}{% if message['role'] == 'system' and message['content'] %}{{'<|system|>\n' + message['content'] + '<|end|>\n'}}{% elif message['role'] == 'user' %}{{'<|user|>\n' + message['content'] + '<|end|>\n'}}{% elif message['role'] == 'assistant' %}{{'<|assistant|>\n' + message['content'] + '<|end|>\n'}}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ '<|assistant|>\n' }}{% else %}{{ eos_token }}{% endif %}"};}
490
491 protected override string PlayerPrefix(string playerName) { return $"<|user|>\n"; }
492 protected override string AIPrefix(string AIName) { return $"<|assistant|>\n"; }
493 protected override string RequestSuffix() { return "<|end|>\n"; }
494 protected override string PairSuffix() { return "<|end|>\n"; }
495 protected override string SystemPrefix() { return "<|system|>\n"; }
496 protected override string SystemSuffix() { return "<|end|>\n"; }
497
498 public override string[] GetStop(string playerName, string AIName)
499 {
500 return AddStopNewlines(new string[] { "<|end|>", "<|user|>", "<|assistant|>" });
501 }
502 }
503
509 {
510 public override string GetName() { return "zephyr"; }
511 public override string GetDescription() { return "zephyr"; }
512 public override string[] GetNameMatches() { return new string[] {"zephyr"}; }
513 public override string[] GetChatTemplateMatches() { return new string[] {"{% for message in messages %}\n{% if message['role'] == 'user' %}\n{{ '<|user|>\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'system' %}\n{{ '<|system|>\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'assistant' %}\n{{ '<|assistant|>\n' + message['content'] + eos_token }}\n{% endif %}\n{% if loop.last and add_generation_prompt %}\n{{ '<|assistant|>' }}\n{% endif %}\n{% endfor %}"}; }
514
515 protected override string SystemPrefix() { return "<|system|>\n"; }
516 protected override string SystemSuffix() { return "</s>\n"; }
517 protected override string PlayerPrefix(string playerName) { return $"<|user|>\n"; }
518 protected override string AIPrefix(string AIName) { return $"<|assistant|>\n"; }
519 protected override string RequestSuffix() { return "</s>\n"; }
520 protected override string PairSuffix() { return "</s>\n"; }
521
522 public override string[] GetStop(string playerName, string AIName)
523 {
524 return AddStopNewlines(new string[] { $"<|user|>", $"<|assistant|>" });
525 }
526 }
527
533 {
534 public override string GetName() { return "deepseek-v2"; }
535 public override string GetDescription() { return "deepseek-v2"; }
536 public override string[] GetNameMatches() { return new string[] {"deepseek-v2", "deepseek-llm"}; }
537 public override string[] GetChatTemplateMatches() { return new string[] {"{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{{ bos_token }}{% for message in messages %}{% if message['role'] == 'user' %}{{ 'User: ' + message['content'] + '\n\n' }}{% elif message['role'] == 'assistant' %}{{ 'Assistant: ' + message['content'] + eos_token }}{% elif message['role'] == 'system' %}{{ message['content'] + '\n\n' }}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ 'Assistant:' }}{% endif %}"}; }
538
539 protected override string PrefixMessageSeparator() { return " "; }
540 protected override string PromptPrefix() { return "<|begin▁of▁sentence|>"; }
541 protected override string PlayerPrefix(string playerName) { return "User:"; }
542 protected override string AIPrefix(string AIName) { return "Assistant:"; }
543 protected override string PairSuffix() { return "<|end▁of▁sentence|>"; }
544 protected override string RequestSuffix() { return "\n\n"; }
545 protected override string SystemSuffix() { return "\n\n"; }
546
547 // protected override bool SystemPromptSupported() { return false; }
548
549 public override string[] GetStop(string playerName, string AIName)
550 {
551 return AddStopNewlines(new string[] { "<|end▁of▁sentence|>", "User:", "Assistant:" });
552 }
553 }
554
560 {
561 public override string GetName() { return "deepseek-v3"; }
562 public override string GetDescription() { return "deepseek-v3"; }
563 public override string[] GetNameMatches() { return new string[] {"deepseek-v2.5", "deepseek-v3"}; }
564 public override string[] GetChatTemplateMatches()
565 {
566 return new string[]
567 {
568 "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='') %}{%- for message in messages %}{%- if message['role'] == 'system' %}{% set ns.system_prompt = message['content'] %}{%- endif %}{%- endfor %}{{bos_token}}{{ns.system_prompt}}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is none %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls']%}{%- if not ns.is_first %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{%- set ns.is_first = true -%}{%- else %}{{'\\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- endfor %}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is not none %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{{'<|Assistant|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'\\n<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|>'}}{% endif %}",
569 "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='', is_first_sp=true) %}{%- for message in messages %}{%- if message['role'] == 'system' %}{%- if ns.is_first_sp %}{% set ns.system_prompt = ns.system_prompt + message['content'] %}{% set ns.is_first_sp = false %}{%- else %}{% set ns.system_prompt = ns.system_prompt + '\n\n' + message['content'] %}{%- endif %}{%- endif %}{%- endfor %}{{bos_token}}{{ns.system_prompt}}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is none %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls']%}{%- if not ns.is_first %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\n' + '```json' + '\n' + tool['function']['arguments'] + '\n' + '```' + '<|tool▁call▁end|>'}}{%- set ns.is_first = true -%}{%- else %}{{'\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\n' + '```json' + '\n' + tool['function']['arguments'] + '\n' + '```' + '<|tool▁call▁end|>'}}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- endfor %}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is not none %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{{'<|Assistant|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'\n<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|>'}}{% endif %}"
570 };
571 }
572
573 protected override string PrefixMessageSeparator() { return ""; }
574 protected override string PlayerPrefix(string playerName) { return "<|User|>"; }
575 protected override string AIPrefix(string AIName) { return "<|Assistant|>"; }
576 protected override string RequestSuffix() { return ""; }
577
578 public override string[] GetStop(string playerName, string AIName)
579 {
580 return AddStopNewlines(new string[] { "<|end▁of▁sentence|>", "<|User|>", "<|Assistant|>" });
581 }
582 }
583
589 {
590 public override string GetName() { return "deepseek-r1"; }
591 public override string GetDescription() { return "deepseek-r1"; }
592 public override string[] GetNameMatches() { return new string[] {"deepseek-r1"}; }
593 public override string[] GetChatTemplateMatches()
594 {
595 return new string[]
596 {
597 "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='', is_first_sp=true) %}{%- for message in messages %}{%- if message['role'] == 'system' %}{%- if ns.is_first_sp %}{% set ns.system_prompt = ns.system_prompt + message['content'] %}{% set ns.is_first_sp = false %}{%- else %}{% set ns.system_prompt = ns.system_prompt + '\\n\\n' + message['content'] %}{%- endif %}{%- endif %}{%- endfor %}{{ bos_token }}{{ ns.system_prompt }}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and 'tool_calls' in message %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls'] %}{%- if not ns.is_first %}{%- if message['content'] is none %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{%- else %}{{'<|Assistant|>' + message['content'] + '<|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{%- endif %}{%- set ns.is_first = true -%}{%- else %}{{'\\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{%- endif %}{%- endfor %}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- if message['role'] == 'assistant' and 'tool_calls' not in message %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{% set content = message['content'] %}{% if '</think>' in content %}{% set content = content.split('</think>')[-1] %}{% endif %}{{'<|Assistant|>' + content + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|><think>\\n'}}{% endif %}",
598 "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='') %}{%- for message in messages %}{%- if message['role'] == 'system' %}{% set ns.system_prompt = message['content'] %}{%- endif %}{%- endfor %}{{bos_token}}{{ns.system_prompt}}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is none %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls']%}{%- if not ns.is_first %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{%- set ns.is_first = true -%}{%- else %}{{'\\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- endfor %}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is not none %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{% set content = message['content'] %}{% if '</think>' in content %}{% set content = content.split('</think>')[-1] %}{% endif %}{{'<|Assistant|>' + content + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'\\n<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|><think>\\n'}}{% endif %}"
599 };
600 }
601
602 public override string ComputePrompt(List<ChatMessage> chatMessages, string playerName, string AIName, bool endWithPrefix = true)
603 {
604 string prompt = base.ComputePrompt(chatMessages, playerName, AIName, endWithPrefix);
605 if (endWithPrefix) prompt += "<think>\n\n</think>\n\n";
606 return prompt;
607 }
608
609 public override string[] GetStop(string playerName, string AIName)
610 {
611 return AddStopNewlines(new string[] { "<|end▁of▁sentence|>", "<|User|>", "<|Assistant|>", "</think>" });
612 }
613 }
614}
Class implementing the Alpaca template.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string GetDescription()
Returns the chat template description.
override string GetName()
Returns the chat template name.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
Class implementing the ChatML template.
override string GetName()
Returns the chat template name.
override string GetDescription()
Returns the chat template description.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
Class implementing the skeleton of a chat template.
static string FromGGUF(string path)
Determines the chat template name from a GGUF file. It reads the GGUF file and then determines the ch...
virtual string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
static Dictionary< string, ChatTemplate > templates
a dictionary from chat template name to chat template type. It can be used to get the chat template n...
virtual string ComputePrompt(List< ChatMessage > chatMessages, string playerName, string AIName, bool endWithPrefix=true)
Constructs the prompt using the template based on a list of ChatMessages.
virtual string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
static string FromName(string name)
Determines the chat template name from a search name. It searches if any of the chat template names i...
virtual string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
static ChatTemplate GetTemplate(string template)
Creates the chat template based on the provided chat template name.
virtual string GetName()
Returns the chat template name.
virtual string GetDescription()
Returns the chat template description.
static string FromTemplate(string template)
Determines the chat template name from a Jinja template.
static string DefaultTemplate
the default template used when it can't be determined ("chatml")
Class implementing the DeepSeek R1 template.
override string ComputePrompt(List< ChatMessage > chatMessages, string playerName, string AIName, bool endWithPrefix=true)
Constructs the prompt using the template based on a list of ChatMessages.
override string GetName()
Returns the chat template name.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string GetDescription()
Returns the chat template description.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
Class implementing the DeepSeek V2 template.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string GetDescription()
Returns the chat template description.
override string GetName()
Returns the chat template name.
Class implementing the DeepSeek V3 template.
override string GetDescription()
Returns the chat template description.
override string GetName()
Returns the chat template name.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
Class implementing the GGUF reader.
Definition LLMGGUF.cs:55
string GetStringField(string key)
Allows to retrieve a string GGUF field.
Definition LLMGGUF.cs:145
Class implementing the Gemma template.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string GetDescription()
Returns the chat template description.
override string GetName()
Returns the chat template name.
Class implementing helper functions for setup and process management.
Class implementing a modified version of the LLama2 template for chat.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string GetDescription()
Returns the chat template description.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string GetName()
Returns the chat template name.
Class implementing the LLama2 template.
override string GetName()
Returns the chat template name.
override string GetDescription()
Returns the chat template description.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
Class implementing the LLama3 template for chat.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string GetName()
Returns the chat template name.
override string GetDescription()
Returns the chat template description.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
Class implementing a modified version of the Mistral Instruct template for chat.
override string GetDescription()
Returns the chat template description.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string GetName()
Returns the chat template name.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
Class implementing the Mistral Instruct template.
override string GetDescription()
Returns the chat template description.
override string GetName()
Returns the chat template name.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
Class implementing the Phi-2 template.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string GetName()
Returns the chat template name.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string GetDescription()
Returns the chat template description.
Class implementing the Phi-3 template.
override string GetDescription()
Returns the chat template description.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string GetName()
Returns the chat template name.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
Class implementing the Phi-3.5 template.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string GetName()
Returns the chat template name.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
override string GetDescription()
Returns the chat template description.
Class implementing the Vicuna template.
override string GetDescription()
Returns the chat template description.
override string GetName()
Returns the chat template name.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
Class implementing the Zephyr template.
override string[] GetChatTemplateMatches()
Returns an array of jinja templates that can be used to match the chat template.
override string GetDescription()
Returns the chat template description.
override string GetName()
Returns the chat template name.
override string[] GetStop(string playerName, string AIName)
Returns an array of the stopwords used by the template.
override string[] GetNameMatches()
Returns an array of names that can be used to match the chat template.