Coverage Report

Created: 2026-06-22 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/llama.cpp/src/llama-chat.cpp
Line
Count
Source
1
#include "llama-chat.h"
2
3
#include "llama.h"
4
5
#include <map>
6
#include <sstream>
7
#include <algorithm>
8
9
#if __cplusplus >= 202000L
10
    #define LU8(x) (const char*)(u8##x)
11
#else
12
2.52k
    #define LU8(x) u8##x
13
#endif
14
15
// trim whitespace from the beginning and end of a string
16
2.32k
static std::string trim(const std::string & str) {
17
2.32k
    size_t start = 0;
18
2.32k
    size_t end = str.size();
19
24.2k
    while (start < end && isspace(static_cast<unsigned char>(str[start]))) {
20
21.9k
        start += 1;
21
21.9k
    }
22
1.23M
    while (end > start && isspace(static_cast<unsigned char>(str[end - 1]))) {
23
1.22M
        end -= 1;
24
1.22M
    }
25
2.32k
    return str.substr(start, end - start);
26
2.32k
}
27
28
static const std::map<std::string, llm_chat_template> LLM_CHAT_TEMPLATES = {
29
    { "chatml",            LLM_CHAT_TEMPLATE_CHATML            },
30
    { "llama2",            LLM_CHAT_TEMPLATE_LLAMA_2           },
31
    { "llama2-sys",        LLM_CHAT_TEMPLATE_LLAMA_2_SYS       },
32
    { "llama2-sys-bos",    LLM_CHAT_TEMPLATE_LLAMA_2_SYS_BOS   },
33
    { "llama2-sys-strip",  LLM_CHAT_TEMPLATE_LLAMA_2_SYS_STRIP },
34
    { "mistral-v1",        LLM_CHAT_TEMPLATE_MISTRAL_V1        },
35
    { "mistral-v3",        LLM_CHAT_TEMPLATE_MISTRAL_V3        },
36
    { "mistral-v3-tekken", LLM_CHAT_TEMPLATE_MISTRAL_V3_TEKKEN },
37
    { "mistral-v7",        LLM_CHAT_TEMPLATE_MISTRAL_V7        },
38
    { "mistral-v7-tekken", LLM_CHAT_TEMPLATE_MISTRAL_V7_TEKKEN },
39
    { "phi3",              LLM_CHAT_TEMPLATE_PHI_3             },
40
    { "phi4",              LLM_CHAT_TEMPLATE_PHI_4             },
41
    { "falcon3",           LLM_CHAT_TEMPLATE_FALCON_3          },
42
    { "zephyr",            LLM_CHAT_TEMPLATE_ZEPHYR            },
43
    { "monarch",           LLM_CHAT_TEMPLATE_MONARCH           },
44
    { "gemma",             LLM_CHAT_TEMPLATE_GEMMA             },
45
    { "orion",             LLM_CHAT_TEMPLATE_ORION             },
46
    { "openchat",          LLM_CHAT_TEMPLATE_OPENCHAT          },
47
    { "vicuna",            LLM_CHAT_TEMPLATE_VICUNA            },
48
    { "vicuna-orca",       LLM_CHAT_TEMPLATE_VICUNA_ORCA       },
49
    { "deepseek",          LLM_CHAT_TEMPLATE_DEEPSEEK          },
50
    { "deepseek2",         LLM_CHAT_TEMPLATE_DEEPSEEK_2        },
51
    { "deepseek3",         LLM_CHAT_TEMPLATE_DEEPSEEK_3        },
52
    { "deepseek-ocr",      LLM_CHAT_TEMPLATE_DEEPSEEK_OCR      },
53
    { "command-r",         LLM_CHAT_TEMPLATE_COMMAND_R         },
54
    { "llama3",            LLM_CHAT_TEMPLATE_LLAMA_3           },
55
    { "chatglm3",          LLM_CHAT_TEMPLATE_CHATGLM_3         },
56
    { "chatglm4",          LLM_CHAT_TEMPLATE_CHATGLM_4         },
57
    { "glmedge",           LLM_CHAT_TEMPLATE_GLMEDGE           },
58
    { "minicpm",           LLM_CHAT_TEMPLATE_MINICPM           },
59
    { "exaone3",           LLM_CHAT_TEMPLATE_EXAONE_3          },
60
    { "exaone4",           LLM_CHAT_TEMPLATE_EXAONE_4          },
61
    { "exaone-moe",        LLM_CHAT_TEMPLATE_EXAONE_MOE        },
62
    { "rwkv-world",        LLM_CHAT_TEMPLATE_RWKV_WORLD        },
63
    { "granite",           LLM_CHAT_TEMPLATE_GRANITE_3_X       },
64
    { "granite-4.0",       LLM_CHAT_TEMPLATE_GRANITE_4_0       },
65
    { "granite-4.1",       LLM_CHAT_TEMPLATE_GRANITE_4_1       },
66
    { "gigachat",          LLM_CHAT_TEMPLATE_GIGACHAT          },
67
    { "megrez",            LLM_CHAT_TEMPLATE_MEGREZ            },
68
    { "yandex",            LLM_CHAT_TEMPLATE_YANDEX            },
69
    { "bailing",           LLM_CHAT_TEMPLATE_BAILING           },
70
    { "bailing-think",     LLM_CHAT_TEMPLATE_BAILING_THINK     },
71
    { "bailing2",          LLM_CHAT_TEMPLATE_BAILING2          },
72
    { "llama4",            LLM_CHAT_TEMPLATE_LLAMA4            },
73
    { "smolvlm",           LLM_CHAT_TEMPLATE_SMOLVLM           },
74
    { "hunyuan-moe",       LLM_CHAT_TEMPLATE_HUNYUAN_MOE       },
75
    { "gpt-oss",           LLM_CHAT_TEMPLATE_OPENAI_MOE        },
76
    { "hunyuan-dense",     LLM_CHAT_TEMPLATE_HUNYUAN_DENSE     },
77
    { "hunyuan-vl",        LLM_CHAT_TEMPLATE_HUNYUAN_VL        },
78
    { "kimi-k2",           LLM_CHAT_TEMPLATE_KIMI_K2           },
79
    { "seed_oss",          LLM_CHAT_TEMPLATE_SEED_OSS          },
80
    { "grok-2",            LLM_CHAT_TEMPLATE_GROK_2            },
81
    { "pangu-embedded",    LLM_CHAT_TEMPLATE_PANGU_EMBED       },
82
    { "solar-open",        LLM_CHAT_TEMPLATE_SOLAR_OPEN        },
83
};
84
85
1.65k
llm_chat_template llm_chat_template_from_str(const std::string & name) {
86
1.65k
    return LLM_CHAT_TEMPLATES.at(name);
87
1.65k
}
88
89
1.65k
llm_chat_template llm_chat_detect_template(const std::string & tmpl) {
90
1.65k
    try {
91
1.65k
        return llm_chat_template_from_str(tmpl);
92
1.65k
    } catch (const std::out_of_range &) {
93
        // ignore
94
1.06k
    }
95
96
34.8k
    auto tmpl_contains = [&tmpl](const char * haystack) -> bool {
97
34.8k
        return tmpl.find(haystack) != std::string::npos;
98
34.8k
    };
99
1.06k
    if (tmpl_contains("<|im_start|>")) {
100
7
        return tmpl_contains("<|im_sep|>")
101
7
            ? LLM_CHAT_TEMPLATE_PHI_4
102
7
            : tmpl_contains("<end_of_utterance>")
103
5
                ? LLM_CHAT_TEMPLATE_SMOLVLM // SmolVLM uses <|im_start|> as BOS, but it is NOT chatml
104
5
                : LLM_CHAT_TEMPLATE_CHATML;
105
1.05k
    } else if (tmpl.find("mistral") == 0 || tmpl_contains("[INST]")) {
106
123
        if (tmpl_contains("[SYSTEM_PROMPT]")) {
107
12
            return LLM_CHAT_TEMPLATE_MISTRAL_V7;
108
111
        } else if (
109
            // catches official 'v1' template
110
111
            tmpl_contains("' [INST] ' + system_message")
111
            // catches official 'v3' and 'v3-tekken' templates
112
101
            || tmpl_contains("[AVAILABLE_TOOLS]")
113
111
        ) {
114
            // Official mistral 'v1', 'v3' and 'v3-tekken' templates
115
            // See: https://github.com/mistralai/cookbook/blob/main/concept-deep-dive/tokenization/chat_templates.md
116
            // See: https://github.com/mistralai/cookbook/blob/main/concept-deep-dive/tokenization/templates.md
117
21
            if (tmpl_contains(" [INST]")) {
118
12
                return LLM_CHAT_TEMPLATE_MISTRAL_V1;
119
12
            } else if (tmpl_contains("\"[INST]\"")) {
120
2
                return LLM_CHAT_TEMPLATE_MISTRAL_V3_TEKKEN;
121
2
            }
122
7
            return LLM_CHAT_TEMPLATE_MISTRAL_V3;
123
90
        } else {
124
            // llama2 template and its variants
125
            // [variant] support system message
126
            // See: https://huggingface.co/blog/llama2#how-to-prompt-llama-2
127
90
            bool support_system_message = tmpl_contains("<<SYS>>");
128
90
            bool add_bos_inside_history = tmpl_contains("bos_token + '[INST]");
129
90
            bool strip_message = tmpl_contains("content.strip()");
130
90
            if (strip_message) {
131
6
                return LLM_CHAT_TEMPLATE_LLAMA_2_SYS_STRIP;
132
84
            } else if (add_bos_inside_history) {
133
4
                return LLM_CHAT_TEMPLATE_LLAMA_2_SYS_BOS;
134
80
            } else if (support_system_message) {
135
2
                return LLM_CHAT_TEMPLATE_LLAMA_2_SYS;
136
78
            } else {
137
78
                return LLM_CHAT_TEMPLATE_LLAMA_2;
138
78
            }
139
90
        }
140
933
    } else if (tmpl_contains("<|assistant|>") && tmpl_contains("<|end|>")) {
141
3
        return LLM_CHAT_TEMPLATE_PHI_3;
142
930
    } else if (tmpl_contains("[gMASK]<sop>")) {
143
2
        return LLM_CHAT_TEMPLATE_CHATGLM_4;
144
928
    } else if (tmpl_contains("<|assistant|>") && tmpl_contains("<|user|>")) {
145
29
        if (tmpl_contains("<|tool_declare|>")) {
146
1
            return LLM_CHAT_TEMPLATE_EXAONE_MOE;
147
1
        }
148
28
        return tmpl_contains("</s>") ? LLM_CHAT_TEMPLATE_FALCON_3 : LLM_CHAT_TEMPLATE_GLMEDGE;
149
899
    } else if (tmpl_contains("<|{{ item['role'] }}|>") && tmpl_contains("<|begin_of_image|>")) {
150
5
        return LLM_CHAT_TEMPLATE_GLMEDGE;
151
894
    } else if (tmpl_contains("<|user|>") && tmpl_contains("<|endoftext|>")) {
152
2
        return LLM_CHAT_TEMPLATE_ZEPHYR;
153
892
    } else if (tmpl_contains("bos_token + message['role']")) {
154
5
        return LLM_CHAT_TEMPLATE_MONARCH;
155
887
    } else if (tmpl_contains("<start_of_turn>")) {
156
10
        return LLM_CHAT_TEMPLATE_GEMMA;
157
877
    } else if (tmpl_contains("'\\n\\nAssistant: ' + eos_token")) {
158
        // OrionStarAI/Orion-14B-Chat
159
5
        return LLM_CHAT_TEMPLATE_ORION;
160
872
    } else if (tmpl_contains("GPT4 Correct ")) {
161
        // openchat/openchat-3.5-0106
162
4
        return LLM_CHAT_TEMPLATE_OPENCHAT;
163
868
    } else if (tmpl_contains("USER: ") && tmpl_contains("ASSISTANT: ")) {
164
        // eachadea/vicuna-13b-1.1 (and Orca variant)
165
4
        if (tmpl_contains("SYSTEM: ")) {
166
2
            return LLM_CHAT_TEMPLATE_VICUNA_ORCA;
167
2
        }
168
2
        return LLM_CHAT_TEMPLATE_VICUNA;
169
864
    } else if (tmpl_contains("### Instruction:") && tmpl_contains("<|EOT|>")) {
170
        // deepseek-ai/deepseek-coder-33b-instruct
171
2
        return LLM_CHAT_TEMPLATE_DEEPSEEK;
172
862
    } else if (tmpl_contains("<|START_OF_TURN_TOKEN|>") && tmpl_contains("<|USER_TOKEN|>")) {
173
        // CohereForAI/c4ai-command-r-plus
174
3
        return LLM_CHAT_TEMPLATE_COMMAND_R;
175
859
    } else if (tmpl_contains("<|start_header_id|>") && tmpl_contains("<|end_header_id|>")) {
176
5
        return LLM_CHAT_TEMPLATE_LLAMA_3;
177
854
    } else if (tmpl_contains("[gMASK]sop")) {
178
        // chatglm3-6b
179
12
        return LLM_CHAT_TEMPLATE_CHATGLM_3;
180
842
    } else if (tmpl_contains(LU8("<用户>"))) {
181
        // MiniCPM-3B-OpenHermes-2.5-v2-GGUF
182
9
        return LLM_CHAT_TEMPLATE_MINICPM;
183
833
    } else if (tmpl_contains("'Assistant: ' + message['content'] + eos_token")) {
184
3
        return LLM_CHAT_TEMPLATE_DEEPSEEK_2;
185
830
    } else if (tmpl_contains(LU8("<|Assistant|>")) && tmpl_contains(LU8("<|User|>")) && tmpl_contains(LU8("<|end▁of▁sentence|>"))) {
186
1
        return LLM_CHAT_TEMPLATE_DEEPSEEK_3;
187
829
    } else if (tmpl_contains("[|system|]") && tmpl_contains("[|assistant|]") && tmpl_contains("[|endofturn|]")) {
188
5
        if (tmpl_contains("[|tool|]")) {
189
1
            return LLM_CHAT_TEMPLATE_EXAONE_4;
190
1
        }
191
        // ref: https://huggingface.co/LGAI-EXAONE/EXAONE-3.0-7.8B-Instruct/discussions/8#66bae61b1893d14ee8ed85bb
192
        // EXAONE-3.0-7.8B-Instruct
193
4
        return LLM_CHAT_TEMPLATE_EXAONE_3;
194
824
    } else if (tmpl_contains("rwkv-world") || tmpl_contains("{{- 'User: ' + message['content']|trim + '\\n\\n' -}}")) {
195
10
        return LLM_CHAT_TEMPLATE_RWKV_WORLD;
196
814
    } else if (tmpl_contains("<|start_of_role|>")) {
197
5
        if (tmpl_contains("<tool_call>") || tmpl_contains("<tools>")) {
198
2
            if (tmpl_contains("g4_default_system_message")) {
199
0
                return LLM_CHAT_TEMPLATE_GRANITE_4_0;
200
0
            }
201
2
            return LLM_CHAT_TEMPLATE_GRANITE_4_1;
202
2
        }
203
3
        return LLM_CHAT_TEMPLATE_GRANITE_3_X;
204
809
    } else if (tmpl_contains("message['role'] + additional_special_tokens[0] + message['content'] + additional_special_tokens[1]")) {
205
1
        return LLM_CHAT_TEMPLATE_GIGACHAT;
206
808
    } else if (tmpl_contains("<|role_start|>")) {
207
3
        return LLM_CHAT_TEMPLATE_MEGREZ;
208
805
    } else if (tmpl_contains(" Ассистент:")) {
209
3
        return LLM_CHAT_TEMPLATE_YANDEX;
210
802
    } else if (tmpl_contains("<role>ASSISTANT</role>") && tmpl_contains("'HUMAN'")) {
211
1
        return LLM_CHAT_TEMPLATE_BAILING;
212
801
    } else if (tmpl_contains("<role>ASSISTANT</role>") && tmpl_contains("\"HUMAN\"") && tmpl_contains("<think>")) {
213
4
        return LLM_CHAT_TEMPLATE_BAILING_THINK;
214
797
    } else if (tmpl_contains("<role>ASSISTANT</role>") && tmpl_contains("<role>HUMAN</role>") && tmpl_contains("<|role_end|>")) {
215
7
        return LLM_CHAT_TEMPLATE_BAILING2;
216
790
    } else if (tmpl_contains("<|header_start|>") && tmpl_contains("<|header_end|>")) {
217
2
        return LLM_CHAT_TEMPLATE_LLAMA4;
218
788
    } else if (tmpl_contains("<|endofuserprompt|>")) {
219
5
        return LLM_CHAT_TEMPLATE_DOTS1;
220
783
    } else if (tmpl_contains("<|extra_0|>") && tmpl_contains("<|extra_4|>")) {
221
2
        return LLM_CHAT_TEMPLATE_HUNYUAN_MOE;
222
781
    } else if (tmpl_contains("<|start|>") && tmpl_contains("<|channel|>")) {
223
1
        return LLM_CHAT_TEMPLATE_OPENAI_MOE;
224
780
    } else if (tmpl_contains("<|hy_Assistant|>") && tmpl_contains("<|hy_begin▁of▁sentence|>")) {
225
4
        return LLM_CHAT_TEMPLATE_HUNYUAN_VL;
226
776
    } else if (tmpl_contains("<|hy_Assistant|>") && tmpl_contains("<|hy_place▁holder▁no▁3|>")) {
227
2
        return LLM_CHAT_TEMPLATE_HUNYUAN_DENSE;
228
774
    } else if (tmpl_contains("<|im_assistant|>assistant<|im_middle|>")) {
229
4
        return LLM_CHAT_TEMPLATE_KIMI_K2;
230
770
    } else if (tmpl_contains("<seed:bos>")) {
231
7
        return LLM_CHAT_TEMPLATE_SEED_OSS;
232
763
    } else if (tmpl_contains("'Assistant: '  + message['content'] + '<|separator|>")) {
233
3
        return LLM_CHAT_TEMPLATE_GROK_2;
234
760
    } else if (tmpl_contains(LU8("[unused9]系统:[unused10]"))) {
235
5
        return LLM_CHAT_TEMPLATE_PANGU_EMBED;
236
755
    } else if (tmpl_contains("<|begin|>") && tmpl_contains("<|end|>") && tmpl_contains("<|content|>")) {
237
2
        return LLM_CHAT_TEMPLATE_SOLAR_OPEN;
238
2
    }
239
753
    return LLM_CHAT_TEMPLATE_UNKNOWN;
240
1.06k
}
241
242
// Simple version of "llama_apply_chat_template" that only works with strings
243
// This function uses heuristic checks to determine commonly used template. It is not a jinja parser.
244
int32_t llm_chat_apply_template(
245
    llm_chat_template tmpl,
246
    const std::vector<const llama_chat_message *> & chat,
247
902
    std::string & dest, bool add_ass) {
248
    // Taken from the research: https://github.com/ggml-org/llama.cpp/issues/5527
249
902
    std::stringstream ss;
250
902
    if (tmpl == LLM_CHAT_TEMPLATE_CHATML) {
251
        // chatml template
252
36
        for (auto message : chat) {
253
36
            ss << "<|im_start|>" << message->role << "\n" << message->content << "<|im_end|>\n";
254
36
        }
255
6
        if (add_ass) {
256
6
            ss << "<|im_start|>assistant\n";
257
6
        }
258
896
    } else if (tmpl == LLM_CHAT_TEMPLATE_MISTRAL_V7 || tmpl == LLM_CHAT_TEMPLATE_MISTRAL_V7_TEKKEN) {
259
        // Official mistral 'v7' template
260
        // See: https://huggingface.co/mistralai/Mistral-Large-Instruct-2411#basic-instruct-template-v7
261
        //      https://huggingface.co/mistralai/Mistral-Small-3.1-24B-Instruct-2503#basic-instruct-template-v7-tekken
262
76
        const char * trailing_space = tmpl == LLM_CHAT_TEMPLATE_MISTRAL_V7 ? " " : "";
263
456
        for (auto message : chat) {
264
456
            std::string role(message->role);
265
456
            std::string content(message->content);
266
456
            if (role == "system") {
267
76
                ss << "[SYSTEM_PROMPT]" << trailing_space << content << "[/SYSTEM_PROMPT]";
268
380
            } else if (role == "user") {
269
228
                ss << "[INST]" << trailing_space << content << "[/INST]";
270
228
            } else {
271
152
                ss << trailing_space << content << "</s>";
272
152
            }
273
456
        }
274
820
    } else if (tmpl == LLM_CHAT_TEMPLATE_MISTRAL_V1
275
741
            || tmpl == LLM_CHAT_TEMPLATE_MISTRAL_V3
276
704
            || tmpl == LLM_CHAT_TEMPLATE_MISTRAL_V3_TEKKEN) {
277
        // See: https://github.com/mistralai/cookbook/blob/main/concept-deep-dive/tokenization/chat_templates.md
278
        // See: https://github.com/mistralai/cookbook/blob/main/concept-deep-dive/tokenization/templates.md
279
120
        std::string leading_space = tmpl == LLM_CHAT_TEMPLATE_MISTRAL_V1 ? " " : "";
280
120
        std::string trailing_space = tmpl == LLM_CHAT_TEMPLATE_MISTRAL_V3_TEKKEN ? "" : " ";
281
120
        bool trim_assistant_message = tmpl == LLM_CHAT_TEMPLATE_MISTRAL_V3;
282
120
        bool is_inside_turn = false;
283
720
        for (auto message : chat) {
284
720
            if (!is_inside_turn) {
285
360
                ss << leading_space << "[INST]" << trailing_space;
286
360
                is_inside_turn = true;
287
360
            }
288
720
            std::string role(message->role);
289
720
            std::string content(message->content);
290
720
            if (role == "system") {
291
120
                ss << content << "\n\n";
292
600
            } else if (role == "user") {
293
360
                ss << content << leading_space << "[/INST]";
294
360
            } else {
295
240
                ss << trailing_space << (trim_assistant_message ? trim(content) : content) << "</s>";
296
240
                is_inside_turn = false;
297
240
            }
298
720
        }
299
700
    } else if (
300
700
            tmpl == LLM_CHAT_TEMPLATE_LLAMA_2
301
594
            || tmpl == LLM_CHAT_TEMPLATE_LLAMA_2_SYS
302
588
            || tmpl == LLM_CHAT_TEMPLATE_LLAMA_2_SYS_BOS
303
583
            || tmpl == LLM_CHAT_TEMPLATE_LLAMA_2_SYS_STRIP) {
304
        // llama2 template and its variants
305
        // [variant] support system message
306
        // See: https://huggingface.co/blog/llama2#how-to-prompt-llama-2
307
188
        bool support_system_message = tmpl != LLM_CHAT_TEMPLATE_LLAMA_2;
308
        // [variant] add BOS inside history
309
188
        bool add_bos_inside_history = tmpl == LLM_CHAT_TEMPLATE_LLAMA_2_SYS_BOS;
310
        // [variant] trim spaces from the input message
311
188
        bool strip_message = tmpl == LLM_CHAT_TEMPLATE_LLAMA_2_SYS_STRIP;
312
        // construct the prompt
313
188
        bool is_inside_turn = true; // skip BOS at the beginning
314
188
        ss << "[INST] ";
315
1.12k
        for (auto message : chat) {
316
1.12k
            std::string content = strip_message ? trim(message->content) : message->content;
317
1.12k
            std::string role(message->role);
318
1.12k
            if (!is_inside_turn) {
319
376
                is_inside_turn = true;
320
376
                ss << (add_bos_inside_history ? "<s>[INST] " : "[INST] ");
321
376
            }
322
1.12k
            if (role == "system") {
323
188
                if (support_system_message) {
324
82
                    ss << "<<SYS>>\n" << content << "\n<</SYS>>\n\n";
325
106
                } else {
326
                    // if the model does not support system message, we still include it in the first message, but without <<SYS>>
327
106
                    ss << content << "\n";
328
106
                }
329
940
            } else if (role == "user") {
330
564
                ss << content << " [/INST]";
331
564
            } else {
332
376
                ss << content << "</s>";
333
376
                is_inside_turn = false;
334
376
            }
335
1.12k
        }
336
512
    } else if (tmpl == LLM_CHAT_TEMPLATE_PHI_3) {
337
        // Phi 3
338
24
        for (auto message : chat) {
339
24
            std::string role(message->role);
340
24
            ss << "<|" << role << "|>\n" << message->content << "<|end|>\n";
341
24
        }
342
4
        if (add_ass) {
343
4
            ss << "<|assistant|>\n";
344
4
        }
345
508
    } else if (tmpl == LLM_CHAT_TEMPLATE_PHI_4) {
346
        // chatml template
347
18
        for (auto message : chat) {
348
18
            ss << "<|im_start|>" << message->role << "<|im_sep|>" << message->content << "<|im_end|>";
349
18
        }
350
3
        if (add_ass) {
351
3
            ss << "<|im_start|>assistant<|im_sep|>";
352
3
        }
353
505
    } else if (tmpl == LLM_CHAT_TEMPLATE_FALCON_3) {
354
        // Falcon 3
355
90
        for (auto message : chat) {
356
90
            std::string role(message->role);
357
90
            ss << "<|" << role << "|>\n" << message->content << "\n";
358
90
        }
359
15
        if (add_ass) {
360
15
            ss << "<|assistant|>\n";
361
15
        }
362
490
    } else if (tmpl == LLM_CHAT_TEMPLATE_ZEPHYR) {
363
        // zephyr template
364
24
        for (auto message : chat) {
365
24
            ss << "<|" << message->role << "|>" << "\n" << message->content << "<|endoftext|>\n";
366
24
        }
367
4
        if (add_ass) {
368
4
            ss << "<|assistant|>\n";
369
4
        }
370
486
    } else if (tmpl == LLM_CHAT_TEMPLATE_MONARCH) {
371
        // mlabonne/AlphaMonarch-7B template (the <s> is included inside history)
372
36
        for (auto message : chat) {
373
36
            std::string bos = (message == chat.front()) ? "" : "<s>"; // skip BOS for first message
374
36
            ss << bos << message->role << "\n" << message->content << "</s>\n";
375
36
        }
376
6
        if (add_ass) {
377
6
            ss << "<s>assistant\n";
378
6
        }
379
480
    } else if (tmpl == LLM_CHAT_TEMPLATE_GEMMA) {
380
        // google/gemma-7b-it
381
165
        std::string system_prompt = "";
382
990
        for (auto message : chat) {
383
990
            std::string role(message->role);
384
990
            if (role == "system") {
385
                // there is no system message for gemma, but we will merge it with user prompt, so nothing is broken
386
165
                system_prompt += trim(message->content);
387
165
                continue;
388
165
            }
389
            // in gemma, "assistant" is "model"
390
825
            role = role == "assistant" ? "model" : message->role;
391
825
            ss << "<start_of_turn>" << role << "\n";
392
825
            if (!system_prompt.empty() && role != "model") {
393
141
                ss << system_prompt << "\n\n";
394
141
                system_prompt = "";
395
141
            }
396
825
            ss << trim(message->content) << "<end_of_turn>\n";
397
825
        }
398
165
        if (add_ass) {
399
165
            ss << "<start_of_turn>model\n";
400
165
        }
401
315
    } else if (tmpl == LLM_CHAT_TEMPLATE_ORION) {
402
        // OrionStarAI/Orion-14B-Chat
403
27
        std::string system_prompt = "";
404
162
        for (auto message : chat) {
405
162
            std::string role(message->role);
406
162
            if (role == "system") {
407
                // there is no system message support, we will merge it with user prompt
408
27
                system_prompt += message->content;
409
27
                continue;
410
135
            } else if (role == "user") {
411
81
                ss << "Human: ";
412
81
                if (!system_prompt.empty()) {
413
21
                    ss << system_prompt << "\n\n";
414
21
                    system_prompt = "";
415
21
                }
416
81
                ss << message->content << "\n\nAssistant: </s>";
417
81
            } else {
418
54
                ss << message->content << "</s>";
419
54
            }
420
162
        }
421
288
    } else if (tmpl == LLM_CHAT_TEMPLATE_OPENCHAT) {
422
        // openchat/openchat-3.5-0106,
423
30
        for (auto message : chat) {
424
30
            std::string role(message->role);
425
30
            if (role == "system") {
426
5
                ss << message->content << "<|end_of_turn|>";
427
25
            } else {
428
25
                role[0] = toupper(role[0]);
429
25
                ss << "GPT4 Correct " << role << ": " << message->content << "<|end_of_turn|>";
430
25
            }
431
30
        }
432
5
        if (add_ass) {
433
5
            ss << "GPT4 Correct Assistant:";
434
5
        }
435
283
    } else if (tmpl == LLM_CHAT_TEMPLATE_VICUNA || tmpl == LLM_CHAT_TEMPLATE_VICUNA_ORCA) {
436
        // eachadea/vicuna-13b-1.1 (and Orca variant)
437
36
        for (auto message : chat) {
438
36
            std::string role(message->role);
439
36
            if (role == "system") {
440
                // Orca-Vicuna variant uses a system prefix
441
6
                if (tmpl == LLM_CHAT_TEMPLATE_VICUNA_ORCA) {
442
3
                    ss << "SYSTEM: " << message->content << "\n";
443
3
                } else {
444
3
                    ss << message->content << "\n\n";
445
3
                }
446
30
            } else if (role == "user") {
447
18
                ss << "USER: " << message->content << "\n";
448
18
            } else if (role == "assistant") {
449
12
                ss << "ASSISTANT: " << message->content << "</s>\n";
450
12
            }
451
36
        }
452
6
        if (add_ass) {
453
6
            ss << "ASSISTANT:";
454
6
        }
455
277
    } else if (tmpl == LLM_CHAT_TEMPLATE_DEEPSEEK) {
456
        // deepseek-ai/deepseek-coder-33b-instruct
457
18
        for (auto message : chat) {
458
18
            std::string role(message->role);
459
18
            if (role == "system") {
460
3
                ss << message->content;
461
15
            } else if (role == "user") {
462
9
                ss << "### Instruction:\n" << message->content << "\n";
463
9
            } else if (role == "assistant") {
464
6
                ss << "### Response:\n" << message->content << "\n<|EOT|>\n";
465
6
            }
466
18
        }
467
3
        if (add_ass) {
468
3
            ss << "### Response:\n";
469
3
        }
470
274
    } else if (tmpl == LLM_CHAT_TEMPLATE_COMMAND_R) {
471
        // CohereForAI/c4ai-command-r-plus
472
126
        for (auto message : chat) {
473
126
            std::string role(message->role);
474
126
            if (role == "system") {
475
21
                ss << "<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>" << trim(message->content) << "<|END_OF_TURN_TOKEN|>";
476
105
            } else if (role == "user") {
477
63
                ss << "<|START_OF_TURN_TOKEN|><|USER_TOKEN|>" << trim(message->content) << "<|END_OF_TURN_TOKEN|>";
478
63
            } else if (role == "assistant") {
479
42
                ss << "<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>" << trim(message->content) << "<|END_OF_TURN_TOKEN|>";
480
42
            }
481
126
        }
482
21
        if (add_ass) {
483
21
            ss << "<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>";
484
21
        }
485
253
    } else if (tmpl == LLM_CHAT_TEMPLATE_LLAMA_3) {
486
        // Llama 3
487
78
        for (auto message : chat) {
488
78
            std::string role(message->role);
489
78
            ss << "<|start_header_id|>" << role << "<|end_header_id|>\n\n" << trim(message->content) << "<|eot_id|>";
490
78
        }
491
13
        if (add_ass) {
492
13
            ss << "<|start_header_id|>assistant<|end_header_id|>\n\n";
493
13
        }
494
240
    } else if (tmpl == LLM_CHAT_TEMPLATE_CHATGLM_3) {
495
        // chatglm3-6b
496
13
        ss << "[gMASK]" << "sop";
497
78
        for (auto message : chat) {
498
78
            std::string role(message->role);
499
78
            ss << "<|" << role << "|>" << "\n " << message->content;
500
78
        }
501
13
        if (add_ass) {
502
13
            ss << "<|assistant|>";
503
13
        }
504
227
    } else if (tmpl == LLM_CHAT_TEMPLATE_CHATGLM_4) {
505
3
        ss << "[gMASK]" << "<sop>";
506
18
        for (auto message : chat) {
507
18
            std::string role(message->role);
508
18
            ss << "<|" << role << "|>" << "\n" << message->content;
509
18
        }
510
3
        if (add_ass) {
511
3
            ss << "<|assistant|>\n";
512
3
        }
513
224
    } else if (tmpl == LLM_CHAT_TEMPLATE_GLMEDGE) {
514
120
        for (auto message : chat) {
515
120
            std::string role(message->role);
516
120
            ss << "<|" << role << "|>" << "\n" << message->content;
517
120
        }
518
20
        if (add_ass) {
519
20
            ss << "<|assistant|>";
520
20
        }
521
204
    } else if (tmpl == LLM_CHAT_TEMPLATE_MINICPM) {
522
        // MiniCPM-3B-OpenHermes-2.5-v2-GGUF
523
108
        for (auto message : chat) {
524
108
            std::string role(message->role);
525
108
            if (role == "user") {
526
54
                ss << LU8("<用户>");
527
54
                ss << trim(message->content);
528
54
                ss << "<AI>";
529
54
            } else {
530
54
                ss << trim(message->content);
531
54
            }
532
108
        }
533
186
    } else if (tmpl == LLM_CHAT_TEMPLATE_DEEPSEEK_2) {
534
        // DeepSeek-V2
535
24
        for (auto message : chat) {
536
24
            std::string role(message->role);
537
24
            if (role == "system") {
538
4
                ss << message->content << "\n\n";
539
20
            } else if (role == "user") {
540
12
                ss << "User: " << message->content << "\n\n";
541
12
            } else if (role == "assistant") {
542
8
                ss << "Assistant: " << message->content << LU8("<|end▁of▁sentence|>");
543
8
            }
544
24
        }
545
4
        if (add_ass) {
546
4
            ss << "Assistant:";
547
4
        }
548
182
    } else if (tmpl == LLM_CHAT_TEMPLATE_DEEPSEEK_3) {
549
        // DeepSeek-V3
550
12
        for (auto message : chat) {
551
12
            std::string role(message->role);
552
12
            if (role == "system") {
553
2
                ss << message->content << "\n\n";
554
10
            } else if (role == "user") {
555
6
                ss << LU8("<|User|>") << message->content;
556
6
            } else if (role == "assistant") {
557
4
                ss << LU8("<|Assistant|>") << message->content << LU8("<|end▁of▁sentence|>");
558
4
            }
559
12
        }
560
2
        if (add_ass) {
561
2
            ss << LU8("<|Assistant|>");
562
2
        }
563
180
    } else if (tmpl == LLM_CHAT_TEMPLATE_DEEPSEEK_OCR) {
564
24
        for (auto message : chat) {
565
            // no template
566
24
            ss << message->content;
567
24
        }
568
176
    } else if (tmpl == LLM_CHAT_TEMPLATE_EXAONE_3) {
569
        // ref: https://huggingface.co/LGAI-EXAONE/EXAONE-3.0-7.8B-Instruct/discussions/8#66bae61b1893d14ee8ed85bb
570
        // EXAONE-3.0-7.8B-Instruct
571
84
        for (auto message : chat) {
572
84
            std::string role(message->role);
573
84
            if (role == "system") {
574
14
                ss << "[|system|]" << trim(message->content) << "[|endofturn|]\n";
575
70
            } else if (role == "user") {
576
42
                ss << "[|user|]" << trim(message->content) << "\n";
577
42
            } else if (role == "assistant") {
578
28
                ss << "[|assistant|]" << trim(message->content) << "[|endofturn|]\n";
579
28
            }
580
84
        }
581
14
        if (add_ass) {
582
14
            ss << "[|assistant|]";
583
14
        }
584
162
    } else if (tmpl == LLM_CHAT_TEMPLATE_EXAONE_4) {
585
78
        for (auto message : chat) {
586
78
            std::string role(message->role);
587
78
            if (role == "system") {
588
13
                ss << "[|system|]" << trim(message->content) << "[|endofturn|]\n";
589
65
            } else if (role == "user") {
590
39
                ss << "[|user|]" << trim(message->content) << "\n";
591
39
            } else if (role == "assistant") {
592
26
                ss << "[|assistant|]" << trim(message->content) << "[|endofturn|]\n";
593
26
            } else if (role == "tool") {
594
0
                ss << "[|tool|]" << trim(message->content) << "[|endofturn|]\n";
595
0
            }
596
78
        }
597
13
        if (add_ass) {
598
13
            ss << "[|assistant|]";
599
13
        }
600
149
    } else if (tmpl == LLM_CHAT_TEMPLATE_EXAONE_MOE) {
601
66
        for (auto message : chat) {
602
66
            std::string role(message->role);
603
66
            if (role == "system") {
604
11
                ss << "<|system|>\n" << trim(message->content) << "<|endofturn|>\n";
605
55
            } else if (role == "user") {
606
33
                ss << "<|user|>\n" << trim(message->content) << "<|endofturn|>\n";
607
33
            } else if (role == "assistant") {
608
22
                ss << "<|assistant|>\n" << trim(message->content) << "<|endofturn|>\n";
609
22
            } else if (role == "tool") {
610
0
                ss << "<|tool|>\n" << trim(message->content) << "<|endofturn|>\n";
611
0
            }
612
66
        }
613
11
        if (add_ass) {
614
11
            ss << "<|assistant|>\n";
615
11
        }
616
138
    } else if (tmpl == LLM_CHAT_TEMPLATE_RWKV_WORLD) {
617
        // this template requires the model to have "\n\n" as EOT token
618
119
        for (size_t i = 0; i < chat.size(); i++) {
619
102
            std::string role(chat[i]->role);
620
102
            if (role == "system") {
621
17
                ss << "System: " << trim(chat[i]->content) << "\n\n";
622
85
            } else if (role == "user") {
623
51
                ss << "User: " << trim(chat[i]->content) << "\n\n";
624
51
                if (i == chat.size() - 1) {
625
17
                    ss << "Assistant:";
626
17
                }
627
51
            } else if (role == "assistant") {
628
34
                ss << "Assistant: " << trim(chat[i]->content) << "\n\n";
629
34
            }
630
102
        }
631
121
    } else if (tmpl == LLM_CHAT_TEMPLATE_GRANITE_3_X) {
632
        // IBM Granite 3.x template
633
24
        for (const auto & message : chat) {
634
24
            std::string role(message->role);
635
24
            ss << "<|start_of_role|>" << role << "<|end_of_role|>";
636
24
            if (role == "assistant_tool_call") {
637
0
                ss << "<|tool_call|>";
638
0
            }
639
24
            ss << message->content << "<|end_of_text|>\n";
640
24
        }
641
4
        if (add_ass) {
642
4
            ss << "<|start_of_role|>assistant<|end_of_role|>";
643
4
        }
644
117
    } else if (tmpl == LLM_CHAT_TEMPLATE_GRANITE_4_0) {
645
        // IBM Granite 4.0 template
646
0
        for (const auto & message : chat) {
647
0
            std::string role(message->role);
648
0
            if (role == "assistant_tool_call") {
649
0
                ss << "<|start_of_role|>assistant<|end_of_role|><|tool_call|>";
650
0
            } else {
651
0
                ss << "<|start_of_role|>" << role << "<|end_of_role|>";
652
0
            }
653
0
            ss << message->content << "<|end_of_text|>\n";
654
0
        }
655
0
        if (add_ass) {
656
0
            ss << "<|start_of_role|>assistant<|end_of_role|>";
657
0
        }
658
117
    } else if (tmpl == LLM_CHAT_TEMPLATE_GRANITE_4_1) {
659
        // IBM Granite 4.1 template
660
12
        for (const auto & message : chat) {
661
12
            std::string role(message->role);
662
12
            if (role == "assistant_tool_call") {
663
0
                ss << "<|start_of_role|>assistant<|end_of_role|><|tool_call|>";
664
12
            } else {
665
12
                ss << "<|start_of_role|>" << role << "<|end_of_role|>";
666
12
            }
667
12
            ss << message->content << "<|end_of_text|>\n";
668
12
        }
669
2
        if (add_ass) {
670
2
            ss << "<|start_of_role|>assistant<|end_of_role|>";
671
2
        }
672
115
    } else if (tmpl == LLM_CHAT_TEMPLATE_GIGACHAT) {
673
        // GigaChat template
674
2
        bool has_system = !chat.empty() && std::string(chat[0]->role) == "system";
675
676
        // Handle system message if present
677
2
        if (has_system) {
678
2
            ss << "<s>" << chat[0]->content << "<|message_sep|>";
679
2
        } else {
680
0
            ss << "<s>";
681
0
        }
682
683
        // Process remaining messages
684
12
        for (size_t i = has_system ? 1 : 0; i < chat.size(); i++) {
685
10
            std::string role(chat[i]->role);
686
10
            if (role == "user") {
687
6
                ss << "user<|role_sep|>" << chat[i]->content << "<|message_sep|>"
688
6
                << "available functions<|role_sep|>[]<|message_sep|>";
689
6
            } else if (role == "assistant") {
690
4
                ss << "assistant<|role_sep|>" << chat[i]->content << "<|message_sep|>";
691
4
            }
692
10
        }
693
694
        // Add generation prompt if needed
695
2
        if (add_ass) {
696
2
            ss << "assistant<|role_sep|>";
697
2
        }
698
113
    }  else if (tmpl == LLM_CHAT_TEMPLATE_MEGREZ) {
699
        // Megrez template
700
24
        for (auto message : chat) {
701
24
            std::string role(message->role);
702
24
            ss << "<|role_start|>" << role << "<|role_end|>" << message->content << "<|turn_end|>";
703
24
        }
704
705
4
        if (add_ass) {
706
4
            ss << "<|role_start|>assistant<|role_end|>";
707
4
        }
708
109
    } else if (tmpl == LLM_CHAT_TEMPLATE_YANDEX) {
709
        // Yandex template ("\n\n" is defined as EOT token)
710
711
28
        for (size_t i = 0; i < chat.size(); i++) {
712
24
            std::string role(chat[i]->role);
713
24
            if (role == "user") {
714
12
                ss << " Пользователь: " << chat[i]->content << "\n\n";
715
12
            } else if (role == "assistant") {
716
8
                ss << " Ассистент: " << chat[i]->content << "\n\n";
717
8
            }
718
24
        }
719
720
        // Add generation prompt if needed
721
4
        if (add_ass) {
722
4
            ss << " Ассистент:[SEP]";
723
4
        }
724
105
    } else if (tmpl == LLM_CHAT_TEMPLATE_BAILING || tmpl == LLM_CHAT_TEMPLATE_BAILING_THINK) {
725
        // Bailing (Ling/Ring) template
726
42
        for (auto message : chat) {
727
42
            std::string role(message->role);
728
729
42
            if (role == "user") {
730
21
                role = "HUMAN";
731
21
            } else {
732
21
                std::transform(role.begin(), role.end(), role.begin(), ::toupper);
733
21
            }
734
735
42
            ss << "<role>" << role << "</role>" << message->content;
736
42
        }
737
738
7
        if (add_ass) {
739
7
            ss << "<role>ASSISTANT</role>";
740
741
7
            if (tmpl == LLM_CHAT_TEMPLATE_BAILING_THINK) {
742
5
                ss << "<think>";
743
5
            }
744
7
        }
745
98
    } else if (tmpl == LLM_CHAT_TEMPLATE_BAILING2) {
746
        // Bailing2 (Ling 2.0) template
747
8
        bool has_system = !chat.empty() && std::string(chat[0]->role) == "system";
748
749
8
        if (!has_system) {
750
0
            ss << "<role>SYSTEM</role>detailed thinking off<|role_end|>";
751
0
        }
752
753
48
        for (auto message : chat) {
754
48
            std::string role(message->role);
755
756
48
            if (role == "user") {
757
24
                role = "HUMAN";
758
24
            } else {
759
24
                std::transform(role.begin(), role.end(), role.begin(), ::toupper);
760
24
            }
761
762
48
            ss << "<role>" << role << "</role>" << message->content << "<|role_end|>";
763
48
        }
764
765
8
        if (add_ass) {
766
8
            ss << "<role>ASSISTANT</role>";
767
8
        }
768
90
    } else if (tmpl == LLM_CHAT_TEMPLATE_LLAMA4) {
769
        // Llama 4
770
72
        for (auto message : chat) {
771
72
            std::string role(message->role);
772
72
            ss << "<|header_start|>" << role << "<|header_end|>\n\n" << trim(message->content) << "<|eot|>";
773
72
        }
774
12
        if (add_ass) {
775
12
            ss << "<|header_start|>assistant<|header_end|>\n\n";
776
12
        }
777
78
    } else if (tmpl == LLM_CHAT_TEMPLATE_SMOLVLM) {
778
        // SmolVLM
779
1
        ss << "<|im_start|>"; // uses <|im_start|> as BOS, but the actual content is NOT chatml
780
6
        for (auto message : chat) {
781
6
            std::string role(message->role);
782
6
            if (role == "system") {
783
1
                ss << message->content << "\n\n";
784
5
            } else if (role == "user") {
785
3
                ss << "User: " << message->content << "<end_of_utterance>\n";
786
3
            } else {
787
2
                ss << "Assistant: " << message->content << "<end_of_utterance>\n";
788
2
            }
789
6
        }
790
1
        if (add_ass) {
791
1
            ss << "Assistant:";
792
1
        }
793
77
    } else if (tmpl == LLM_CHAT_TEMPLATE_DOTS1) {
794
        // dots.llm1.inst (DOTS1)
795
30
        for (auto message : chat) {
796
30
            std::string role(message->role);
797
30
            if (role == "system") {
798
5
                ss << "<|system|>" << message->content << "<|endofsystem|>";
799
25
            } else if (role == "user") {
800
15
                ss << "<|userprompt|>" << message->content << "<|endofuserprompt|>";
801
15
            } else {
802
10
                ss << "<|response|>" << message->content << "<|endofresponse|>";
803
10
            }
804
30
        }
805
5
        if (add_ass) {
806
5
            ss << "<|response|>";
807
5
        }
808
72
    } else if (tmpl == LLM_CHAT_TEMPLATE_HUNYUAN_MOE) {
809
        // tencent/Hunyuan-A13B-Instruct
810
18
        for (auto message : chat) {
811
18
            std::string role(message->role);
812
18
            if (role == "system") {
813
3
                ss << "<|startoftext|>" << message->content << "<|extra_4|>";
814
15
            } else if (role == "assistant") {
815
6
                ss << message->content << "<|eos|>";
816
9
            } else {
817
9
                ss << "<|startoftext|>" << message->content << "<|extra_0|>";
818
9
            }
819
18
        }
820
69
    } else if (tmpl == LLM_CHAT_TEMPLATE_OPENAI_MOE) {
821
        // OpenAI MoE (based on Harmony chat template)
822
12
        for (auto message : chat) {
823
12
            std::string role(message->role);
824
12
            ss << "<|start|>" << role << "<|message|>" << message->content;
825
12
            ss << (role == "assistant" ? "<|return|>" : "<|end|>");
826
12
        }
827
2
        if (add_ass) {
828
2
            ss << "<|start|>assistant";
829
2
        }
830
67
    } else if (tmpl == LLM_CHAT_TEMPLATE_HUNYUAN_DENSE) {
831
        // tencent/Hunyuan-4B-Instruct
832
21
        for (size_t i = 0; i < chat.size(); i++) {
833
18
            std::string role(chat[i]->role);
834
18
            if (i == 0) {
835
3
                if (role == "system") {
836
3
                    ss << chat[i]->content << "<|hy_place▁holder▁no▁3|>";
837
3
                }
838
3
            }
839
840
18
            if (role == "assistant") {
841
6
                ss << "<|hy_Assistant|>" << chat[i]->content << "<|hy_place▁holder▁no▁2|>";
842
12
            } else if (role == "user") {
843
9
                ss << "<|hy_User|>" << chat[i]->content << "<|hy_Assistant|>";
844
9
            }
845
18
        }
846
64
    } else if (tmpl == LLM_CHAT_TEMPLATE_HUNYUAN_VL) {
847
        // tencent/HunyuanOCR & tencent/HunyuanVL
848
4
        ss << "<|hy_begin▁of▁sentence|>";
849
28
        for (size_t i = 0; i < chat.size(); i++) {
850
24
            std::string role(chat[i]->role);
851
24
            if (i == 0 && role == "system") {
852
4
                ss << chat[i]->content << "<|hy_place▁holder▁no▁3|>";
853
4
                continue;
854
4
            }
855
856
20
            if (role == "user") {
857
12
                ss << chat[i]->content << "<|hy_User|>";
858
12
            } else if (role == "assistant") {
859
8
                ss << chat[i]->content << "<|hy_Assistant|>";
860
8
            }
861
20
        }
862
60
    } else if (tmpl == LLM_CHAT_TEMPLATE_KIMI_K2) {
863
        // moonshotai/Kimi-K2-Instruct
864
30
        for (auto message : chat) {
865
30
            std::string role(message->role);
866
30
            if (role == "system") {
867
5
                ss << "<|im_system|>system<|im_middle|>";
868
25
            } else if (role == "user") {
869
15
                ss << "<|im_user|>user<|im_middle|>";
870
15
            } else if (role == "assistant") {
871
10
                ss << "<|im_assistant|>assistant<|im_middle|>";
872
10
            } else if (role == "tool") {
873
0
                ss << "<|im_system|>tool<|im_middle|>";
874
0
            }
875
876
30
            ss << message->content << "<|im_end|>";
877
30
        }
878
5
        if (add_ass) {
879
5
            ss << "<|im_assistant|>assistant<|im_middle|>";
880
5
        }
881
55
    } else if (tmpl == LLM_CHAT_TEMPLATE_SEED_OSS) {
882
96
        for (auto message: chat) {
883
96
            std::string role(message->role);
884
96
            ss << "<seed:bos>" << role << "\n" << (role == "assistant" ? trim(message->content) : message->content) << "<seed:eos>";
885
96
        }
886
16
        if (add_ass) {
887
16
            ss << "<seed:bos>assistant\n";
888
16
        }
889
39
    } else if (tmpl == LLM_CHAT_TEMPLATE_GROK_2) {
890
126
        for (auto message : chat) {
891
126
            std::string role(message->role);
892
126
            if (role == "system") {
893
21
                ss << "System: " << trim(message->content) << "<|separator|>\n\n";
894
105
            } else if (role == "user") {
895
63
                ss << "Human: " << trim(message->content) << "<|separator|>\n\n";
896
63
            } else if (role == "assistant") {
897
42
                ss << "Assistant: " << message->content << "<|separator|>\n\n";
898
42
            }
899
126
        }
900
21
        if (add_ass) {
901
21
            ss << "Assistant:";
902
21
        }
903
21
    }else if (tmpl == LLM_CHAT_TEMPLATE_PANGU_EMBED) {
904
        // [unused9]系统:xxx[unused10]
905
        // [unused9]用户:xxx[unused10]
906
        // [unused9]助手:xxx[unused10]
907
        // ...
908
105
        for (size_t i = 0; i < chat.size(); ++i) {
909
90
            const auto & msg = chat[i];
910
90
            const std::string & role = msg->role;
911
90
            const std::string & content = msg->content;
912
913
90
            if (i == 0 && role != "system") {
914
0
                ss << "[unused9]系统:[unused10]";
915
0
            }
916
917
90
            if (role == "system") {
918
15
                ss << "[unused9]系统:" << content << "[unused10]";
919
75
            } else if (role == "user") {
920
45
                ss << "[unused9]用户:" << content << "[unused10]";
921
45
            } else if (role == "assistant") {
922
30
                ss << "[unused9]助手:" << content << "[unused10]";
923
30
            } else if (role == "tool") {
924
0
                ss << "[unused9]工具:" << content << "[unused10]";
925
0
            } else if (role == "function") {
926
0
                ss << "[unused9]方法:" << content << "[unused10]";
927
0
            }
928
90
        }
929
15
        if (add_ass) {
930
15
            ss << "[unused9]助手:";
931
15
        }
932
15
    } else if (tmpl == LLM_CHAT_TEMPLATE_SOLAR_OPEN) {
933
18
        for (auto message : chat) {
934
18
            std::string role(message->role);
935
18
            ss << "<|begin|>" << role << "<|content|>" << message->content << "<|end|>";
936
18
        }
937
3
        if (add_ass) {
938
3
            ss << "<|begin|>assistant";
939
3
        }
940
3
    } else {
941
        // template not supported
942
0
        return -1;
943
0
    }
944
902
    dest = ss.str();
945
902
    return dest.size();
946
902
}
947
948
// public interface
949
950
0
int32_t llama_chat_builtin_templates(const char ** output, size_t len) {
951
0
    auto it = LLM_CHAT_TEMPLATES.begin();
952
0
    for (size_t i = 0; i < std::min(len, LLM_CHAT_TEMPLATES.size()); i++) {
953
0
        output[i] = it->first.c_str();
954
0
        std::advance(it, 1);
955
0
    }
956
0
    return (int32_t) LLM_CHAT_TEMPLATES.size();
957
0
}