41inline int token_id(
const std::vector<std::string>& tokens,
const std::string&
text) {
42 for (
size_t i = tokens.size();
i-- > 0;)
43 if (tokens[
i] ==
text)
return static_cast<int>(
i);
65 f.why = std::move(
why);
66 auto ids = [&](
const std::vector<std::string>&
stops,
const char*
field) {
68 for (
const auto&
s :
stops) {
72 "' is not a single token of this vocabulary (ember stops on single tokens only)");
77 f.eos_ids = ids(
t.stop_eos,
"eos");
78 f.turn_ids = ids(
t.stop_turn,
"turn");
79 if (
f.eos_ids.empty())
throw chat::Error(
"chat template " +
t.id() +
" has no stop.eos token");
81 f.tool_call_id =
token_id(tokens,
t.tool_call_open);
82 if (
f.tool_call_id < 0)
83 throw chat::Error(
"chat template " +
t.id() +
": tool_call.open '" +
t.tool_call_open +
84 "' is not a single token of this vocabulary");
91 if (
e ==
id)
return true;
95 if (
is_eos(
id))
return true;
97 if (
t ==
id)
return true;
119 std::string
prompt(
const std::vector<std::pair<std::string, std::string>>& turns,
size_t from,
120 const std::string& user,
const std::string* system =
nullptr,
int thinking = -1)
const {
121 const std::vector<std::pair<std::string, std::string>>
kept(turns.begin() +
static_cast<std::ptrdiff_t
>(
from),
130 virtual std::vector<int>
encode(
const std::string&
text)
const = 0;
131 virtual std::string
decode(
const std::vector<int>& ids)
const = 0;
132 virtual size_t size()
const = 0;
133 virtual const char*
kind()
const = 0;
146 std::vector<int>
encode(
const std::string&
text)
const override {
return t_.encode(
text); }
147 std::string
decode(
const std::vector<int>& ids)
const override {
return t_.decode(ids); }
148 size_t size()
const override {
return t_.size(); }
168 const std::vector<int32_t>& token_type) {
169 for (
size_t i = 0;
i < token_type.size() &&
i < tokens.size(); ++
i) {
170 const int t = token_type[
i];
171 if (
t != 2 &&
t != 3 &&
t != 4)
continue;
173 if (tokens[
i].
find(*
c) != std::string::npos)
174 throw std::runtime_error(
"Qwen vocabulary: added token '" + tokens[
i] +
"' holds '" +
176 "', which NFC writes for another character; ember's NFC cut "
177 "would differ from HF's there");
184 [&](
size_t a,
size_t b) { return texts_[a].size() > texts_[b].size(); });
186 std::vector<std::string>
t;
193 t_ = std::make_unique<microtorch::llama3::Tokenizer>(std::move(tokens), merges, token_type, 1);
198 std::vector<std::pair<std::string, int>>
frags = {{
text, -1}};
201 if (
s.empty())
continue;
202 std::vector<std::pair<std::string, int>> next;
203 for (
auto& [
t,
id] :
frags) {
205 next.emplace_back(std::move(
t),
id);
209 for (
size_t m; (
m =
t.find(
s,
at)) != std::string::npos;
at =
m +
s.size()) {
210 if (
m >
at) next.emplace_back(
t.substr(
at,
m -
at), -1);
211 next.emplace_back(std::string(),
specials_[
k]);
213 if (
at <
t.size()) next.emplace_back(
t.substr(
at), -1);
215 frags = std::move(next);
217 std::vector<int>
out;
218 for (
const auto& [
t,
id] :
frags) {
224 out.insert(
out.end(), ids.begin(), ids.end());
229 std::string
decode(
const std::vector<int>& ids)
const {
return t_->decode(ids); }
230 size_t size()
const {
return t_->size(); }
233 std::unique_ptr<microtorch::llama3::Tokenizer>
t_;
259 std::unique_ptr<ChatTokenizer>
tok;
270 const std::vector<std::pair<std::string, int>>&
want) {
271 for (
const auto& [
text,
id] :
want) {
272 const std::vector<int>
got =
tok.encode(
text);
273 if (
got.size() != 1 ||
got[0] !=
id)
274 throw std::runtime_error(
"chat template " +
t.id() +
": the " +
tok.kind() +
" tokenizer does not encode '" +
275 text +
"' as its control token " + std::to_string(
id) +
": the file is refused");
284 }
catch (
const std::exception&
e) {
285 s.why = std::string(
"paperkiln.chat_template refused (") +
e.what() +
"): raw completion only";
289 if (!
r.chat)
return s;
293 std::vector<std::pair<std::string, int>>
control;
294 if (
r.tmpl.has_tool) {
298 throw std::runtime_error(std::string(
e.what()) +
": the file is refused");
302 const bool bpe = g.model ==
"bpe";
303 const bool l3 = g.model ==
"gpt2" && (g.pre ==
"llama-bpe" || g.pre ==
"qwen2");
304 const bool spm = g.model ==
"llama";
305 if (!bpe && !
l3 && !
spm) {
306 if (g.model !=
"word")
s.why +=
"; ember has no chat tokenizer for tokenizer.ggml.model '" + g.model +
"'";
314 s.why =
r.why +
"; " +
e.what() +
": raw completion only";
318 if (g.merges.empty())
throw std::runtime_error(
"BPE GGUF has no tokenizer.ggml.merges");
319 s.tok = std::make_unique<ChatTokenizerOf<microtorch::bpe::Tokenizer>>(
322 if (g.merges.empty())
throw std::runtime_error(g.pre +
" GGUF has no tokenizer.ggml.merges");
323 if (g.pre ==
"qwen2")
324 s.tok = std::make_unique<ChatTokenizerOf<QwenNfcTokenizer>>(
325 QwenNfcTokenizer(std::move(g.tokens), g.merges, g.token_type),
"qwen2_bpe", std::move(
f));
327 s.tok = std::make_unique<ChatTokenizerOf<microtorch::llama3::Tokenizer>>(
331 s.tok = std::make_unique<ChatTokenizerOf<SpmTokenizer>>(
332 SpmTokenizer(std::move(g.tokens), std::move(
v.scores), g.token_type,
v.add_space_prefix),
"spm",
ChatTokenizerOf(T t, const char *kind, ChatFormat format)
std::vector< int > encode(const std::string &text) const override
std::string decode(const std::vector< int > &ids) const override
const char * kind() const override
size_t size() const override
const ChatFormat & format() const
virtual std::string decode(const std::vector< int > &ids) const =0
virtual ~ChatTokenizer()=default
virtual size_t size() const =0
virtual const char * kind() const =0
virtual std::vector< int > encode(const std::string &text) const =0
std::unique_ptr< microtorch::llama3::Tokenizer > t_
std::vector< std::string > texts_
std::vector< int > specials_
QwenNfcTokenizer(std::vector< std::string > tokens, const std::vector< std::string > &merges, const std::vector< int32_t > &token_type)
std::vector< int > encode(const std::string &text) const
std::string decode(const std::vector< int > &ids) const
constexpr char kNfcAsciiImages[]
std::string nfc_utf8(const std::string &text)
bool has_chat_specials(const std::vector< std::string > &tokens)
int token_id(const std::vector< std::string > &tokens, const std::string &text)
ChatSetup make_chat_setup(ChatVocab v)
void control_ids_check(const chat::Template &t, const ChatTokenizer &tok, const std::vector< std::pair< std::string, int > > &want)
std::string render(const std::vector< Message > &m, const Template &t, bool generation_prompt, int thinking=-1, const std::vector< json > &tools={})
std::vector< Message > from_pairs(const std::vector< std::pair< std::string, std::string > > &turns, const std::string *user=nullptr, const std::string *system=nullptr)
void check_control_tokens(const Template &t, const std::vector< std::string > &tokens, const std::vector< int32_t > &types)
const std::array< const char *, 4 > kV1Specials
Resolution resolve(const std::string &declared, const std::vector< std::string > &tokens, const std::string &model, const std::string &chat_template, const std::string &arch)
std::unique_ptr< ChatTokenizer > tok
microtorch::bpe::GgufTokenizer g
std::string chat_template
std::vector< float > scores
std::vector< std::string > stop_eos
std::string generation_prompt
std::vector< std::string > stop_turn
std::string assistant_open
std::string assistant_close