1use std::collections::{BinaryHeap, HashMap};
10use std::io;
11use std::path::Path;
12
13use serde_json::Value;
14
15use super::invalid;
16use super::onig::{self, Regex};
17use super::unicode_data::{CANONICAL_DECOMPOSITIONS, COMBINING_CLASSES, PRIMARY_COMPOSITES};
18
19enum Pattern {
21 Text(Vec<char>),
22 Re(Regex),
23 Char(fn(char) -> bool),
24}
25
26fn segments(pattern: &Pattern, s: &[char]) -> Vec<(usize, usize, bool)> {
28 match pattern {
29 Pattern::Text(t) if t.is_empty() => vec![(0, s.len(), false)],
30 Pattern::Text(t) => {
31 let mut out = Vec::new();
32 let (mut prev, mut i) = (0, 0);
33 while i + t.len() <= s.len() {
34 if s[i..i + t.len()] == t[..] {
35 if prev != i {
36 out.push((prev, i, false));
37 }
38 out.push((i, i + t.len(), true));
39 i += t.len();
40 prev = i;
41 } else {
42 i += 1;
43 }
44 }
45 if prev != s.len() || s.is_empty() {
46 out.push((prev, s.len(), false));
47 }
48 out
49 }
50 Pattern::Re(re) => {
51 if s.is_empty() {
52 return vec![(0, 0, false)];
53 }
54 let cps: Vec<u32> = s.iter().map(|&c| c as u32).collect();
55 let mut out = Vec::new();
56 let mut prev = 0;
57 for (start, end) in re.find_iter(&cps) {
58 if prev != start {
59 out.push((prev, start, false));
60 }
61 out.push((start, end, true));
62 prev = end;
63 }
64 if prev != s.len() {
65 out.push((prev, s.len(), false));
66 }
67 out
68 }
69 Pattern::Char(f) => {
70 if s.is_empty() {
71 return vec![(0, 0, false)];
72 }
73 let mut out = Vec::new();
74 let mut last = 0;
75 for (i, &c) in s.iter().enumerate() {
76 if f(c) {
77 if last < i {
78 out.push((last, i, false));
79 }
80 out.push((i, i + 1, true));
81 last = i + 1;
82 }
83 }
84 if s.len() > last {
85 out.push((last, s.len(), false));
86 }
87 out
88 }
89 }
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
94enum Behavior {
95 Removed,
96 Isolated,
97 MergedWithPrevious,
98 MergedWithNext,
99 Contiguous,
100}
101
102impl Behavior {
103 fn of(v: &Value) -> io::Result<Behavior> {
104 Ok(match v.as_str() {
105 Some("Removed") => Behavior::Removed,
106 Some("Isolated") => Behavior::Isolated,
107 Some("MergedWithPrevious") => Behavior::MergedWithPrevious,
108 Some("MergedWithNext") => Behavior::MergedWithNext,
109 Some("Contiguous") => Behavior::Contiguous,
110 other => return Err(invalid(format!("tokenizer.json: split behavior {other:?}"))),
111 })
112 }
113}
114
115fn split(s: &str, pattern: &Pattern, behavior: Behavior, invert: bool) -> Vec<String> {
117 let chars: Vec<char> = s.chars().collect();
118 let segs: Vec<(usize, usize, bool)> =
119 segments(pattern, &chars).into_iter().map(|(a, b, m)| (a, b, m != invert)).collect();
120 let pieces: Vec<(usize, usize, bool)> = match behavior {
122 Behavior::Isolated => segs.iter().map(|&(a, b, _)| (a, b, false)).collect(),
123 Behavior::Removed => segs.clone(),
124 Behavior::Contiguous => {
125 let mut acc: Vec<(usize, usize, bool)> = Vec::new();
126 let mut previous = false;
127 for &(a, b, m) in &segs {
128 match acc.last_mut() {
129 Some(last) if m == previous => last.1 = b,
130 _ => acc.push((a, b, false)),
131 }
132 previous = m;
133 }
134 acc
135 }
136 Behavior::MergedWithPrevious => {
137 let mut acc: Vec<(usize, usize, bool)> = Vec::new();
138 let mut previous = false;
139 for &(a, b, m) in &segs {
140 match acc.last_mut() {
141 Some(last) if m && !previous => last.1 = b,
142 _ => acc.push((a, b, false)),
143 }
144 previous = m;
145 }
146 acc
147 }
148 Behavior::MergedWithNext => {
149 let mut acc: Vec<(usize, usize, bool)> = Vec::new();
150 let mut previous = false;
151 for &(a, b, m) in segs.iter().rev() {
152 match acc.last_mut() {
153 Some(last) if m && !previous => last.0 = a,
154 _ => acc.push((a, b, false)),
155 }
156 previous = m;
157 }
158 acc.reverse();
159 acc
160 }
161 };
162 pieces
163 .into_iter()
164 .filter(|&(a, b, removed)| !removed && a != b)
165 .map(|(a, b, _)| chars[a..b].iter().collect())
166 .collect()
167}
168
169fn replace(s: &str, pattern: &Pattern, content: &str) -> String {
171 let chars: Vec<char> = s.chars().collect();
172 let mut out = String::new();
173 for (a, b, m) in segments(pattern, &chars) {
174 if m {
175 out.push_str(content);
176 } else {
177 out.extend(&chars[a..b]);
178 }
179 }
180 out
181}
182
183fn pattern_of(v: &Value) -> io::Result<Pattern> {
184 if let Some(s) = v.get("String").and_then(Value::as_str) {
185 return Ok(Pattern::Text(s.chars().collect()));
186 }
187 if let Some(r) = v.get("Regex").and_then(Value::as_str) {
188 return Ok(Pattern::Re(Regex::new(r).map_err(invalid)?));
189 }
190 Err(invalid(format!("tokenizer.json: a pattern {v}")))
191}
192
193fn ccc(c: u32) -> u8 {
194 let at = COMBINING_CLASSES.partition_point(|&(first, _, _)| first <= c);
195 match at.checked_sub(1).map(|i| COMBINING_CLASSES[i]) {
196 Some((first, last, class)) if (first..=last).contains(&c) => class,
197 _ => 0,
198 }
199}
200
201const S_BASE: u32 = 0xAC00;
202const L_BASE: u32 = 0x1100;
203const V_BASE: u32 = 0x1161;
204const T_BASE: u32 = 0x11A7;
205const L_COUNT: u32 = 19;
206const V_COUNT: u32 = 21;
207const T_COUNT: u32 = 28;
208const N_COUNT: u32 = V_COUNT * T_COUNT;
209const S_COUNT: u32 = L_COUNT * N_COUNT;
210
211fn decompose_into(c: u32, out: &mut Vec<u32>) {
212 if (S_BASE..S_BASE + S_COUNT).contains(&c) {
213 let s = c - S_BASE;
214 out.push(L_BASE + s / N_COUNT);
215 out.push(V_BASE + (s % N_COUNT) / T_COUNT);
216 if s % T_COUNT != 0 {
217 out.push(T_BASE + s % T_COUNT);
218 }
219 return;
220 }
221 match CANONICAL_DECOMPOSITIONS.binary_search_by_key(&c, |&(cp, _, _)| cp) {
222 Ok(i) => {
223 let (_, a, b) = CANONICAL_DECOMPOSITIONS[i];
224 decompose_into(a, out);
225 if b != 0 {
226 decompose_into(b, out);
227 }
228 }
229 Err(_) => out.push(c),
230 }
231}
232
233fn nfd(s: &str) -> Vec<u32> {
235 let mut out = Vec::with_capacity(s.len());
236 for c in s.chars() {
237 decompose_into(c as u32, &mut out);
238 }
239 let mut i = 0;
240 while i < out.len() {
241 if ccc(out[i]) == 0 {
242 i += 1;
243 continue;
244 }
245 let start = i;
246 while i < out.len() && ccc(out[i]) != 0 {
247 i += 1;
248 }
249 out[start..i].sort_by_key(|&c| ccc(c));
250 }
251 out
252}
253
254fn compose_pair(a: u32, b: u32) -> Option<u32> {
255 if (L_BASE..L_BASE + L_COUNT).contains(&a) && (V_BASE..V_BASE + V_COUNT).contains(&b) {
256 return Some(S_BASE + ((a - L_BASE) * V_COUNT + (b - V_BASE)) * T_COUNT);
257 }
258 if (S_BASE..S_BASE + S_COUNT).contains(&a) && (a - S_BASE) % T_COUNT == 0 && (T_BASE + 1..T_BASE + T_COUNT).contains(&b) {
259 return Some(a + (b - T_BASE));
260 }
261 PRIMARY_COMPOSITES.binary_search_by_key(&(a, b), |&(x, y, _)| (x, y)).ok().map(|i| PRIMARY_COMPOSITES[i].2)
262}
263
264pub fn nfc(s: &str) -> String {
266 let d = nfd(s);
267 let mut out: Vec<u32> = Vec::with_capacity(d.len());
268 let mut starter: Option<usize> = None;
269 let mut last_class: i32 = -1;
270 for c in d {
271 let class = ccc(c) as i32;
272 if let Some(st) = starter {
273 let blocked = last_class != -1 && (last_class == 0 || last_class >= class);
274 if !blocked {
275 if let Some(comp) = compose_pair(out[st], c) {
276 out[st] = comp;
277 continue;
278 }
279 }
280 }
281 if class == 0 {
282 starter = Some(out.len());
283 last_class = -1;
284 } else {
285 last_class = class;
286 }
287 out.push(c);
288 }
289 out.into_iter().filter_map(char::from_u32).collect()
290}
291
292enum Normalizer {
294 Nfc,
295 Nfd,
296 Lowercase,
297 Replace(Pattern, String),
298 Prepend(String),
299 Strip(bool, bool),
300 StripAccents,
301 Sequence(Vec<Normalizer>),
302}
303
304impl Normalizer {
305 fn of(v: &Value) -> io::Result<Option<Normalizer>> {
306 if v.is_null() {
307 return Ok(None);
308 }
309 Ok(Some(match v["type"].as_str() {
310 Some("NFC") => Normalizer::Nfc,
311 Some("NFD") => Normalizer::Nfd,
312 Some("Lowercase") => Normalizer::Lowercase,
313 Some("Replace") => {
314 Normalizer::Replace(pattern_of(&v["pattern"])?, v["content"].as_str().unwrap_or_default().to_string())
315 }
316 Some("Prepend") => Normalizer::Prepend(v["prepend"].as_str().unwrap_or_default().to_string()),
317 Some("Strip") => Normalizer::Strip(
318 v["strip_left"].as_bool().unwrap_or(true),
319 v["strip_right"].as_bool().unwrap_or(true),
320 ),
321 Some("StripAccents") => Normalizer::StripAccents,
322 Some("Sequence") => Normalizer::Sequence(
323 v["normalizers"]
324 .as_array()
325 .into_iter()
326 .flatten()
327 .map(|n| Normalizer::of(n).map(|o| o.unwrap_or(Normalizer::Sequence(Vec::new()))))
328 .collect::<io::Result<_>>()?,
329 ),
330 other => return Err(invalid(format!("tokenizer.json: the {other:?} normalizer, which this port does not run"))),
331 }))
332 }
333
334 fn run(&self, s: String) -> String {
335 match self {
336 Normalizer::Nfc => nfc(&s),
337 Normalizer::Nfd => nfd(&s).into_iter().filter_map(char::from_u32).collect(),
338 Normalizer::Lowercase => s.chars().flat_map(char::to_lowercase).collect(),
339 Normalizer::Replace(p, content) => replace(&s, p, content),
340 Normalizer::Prepend(p) if !s.is_empty() => format!("{p}{s}"),
341 Normalizer::Prepend(_) => s,
342 Normalizer::Strip(left, right) => {
343 let t = if *left { s.trim_start() } else { &s };
344 (if *right { t.trim_end() } else { t }).to_string()
345 }
346 Normalizer::StripAccents => s.chars().filter(|&c| !matches!(onig::category_name(c as u32), "Mn" | "Mc" | "Me")).collect(),
347 Normalizer::Sequence(v) => v.iter().fold(s, |s, n| n.run(s)),
348 }
349 }
350}
351
352#[derive(Clone, Copy, PartialEq, Eq)]
354enum PrependScheme {
355 Always,
356 First,
357 Never,
358}
359
360enum PreTokenizer {
362 Sequence(Vec<PreTokenizer>),
363 Split { pattern: Pattern, behavior: Behavior, invert: bool },
364 ByteLevel { add_prefix_space: bool, use_regex: bool },
365 Digits(bool),
366 Whitespace,
367 WhitespaceSplit,
368 Punctuation(Behavior),
369 Metaspace { replacement: char, scheme: PrependScheme, split: bool },
370}
371
372const BYTE_LEVEL_PATTERN: &str = r"'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+";
374
375const WHITESPACE_PATTERN: &str = r"\w+|[^\w\s]+";
377
378struct Piece {
380 text: String,
381 at_origin: bool,
382}
383
384impl PreTokenizer {
385 fn of(v: &Value) -> io::Result<Option<PreTokenizer>> {
386 if v.is_null() {
387 return Ok(None);
388 }
389 Ok(Some(match v["type"].as_str() {
390 Some("Sequence") => PreTokenizer::Sequence(
391 v["pretokenizers"]
392 .as_array()
393 .into_iter()
394 .flatten()
395 .filter_map(|p| PreTokenizer::of(p).transpose())
396 .collect::<io::Result<_>>()?,
397 ),
398 Some("Split") => PreTokenizer::Split {
399 pattern: pattern_of(&v["pattern"])?,
400 behavior: Behavior::of(&v["behavior"])?,
401 invert: v["invert"].as_bool().unwrap_or(false),
402 },
403 Some("ByteLevel") => PreTokenizer::ByteLevel {
404 add_prefix_space: v["add_prefix_space"].as_bool().unwrap_or(true),
405 use_regex: v["use_regex"].as_bool().unwrap_or(true),
406 },
407 Some("Digits") => PreTokenizer::Digits(v["individual_digits"].as_bool().unwrap_or(false)),
408 Some("Whitespace") => PreTokenizer::Whitespace,
409 Some("WhitespaceSplit") => PreTokenizer::WhitespaceSplit,
410 Some("Punctuation") => {
411 PreTokenizer::Punctuation(if v["behavior"].is_null() { Behavior::Isolated } else { Behavior::of(&v["behavior"])? })
412 }
413 Some("Metaspace") => PreTokenizer::Metaspace {
414 replacement: v["replacement"].as_str().and_then(|s| s.chars().next()).unwrap_or('\u{2581}'),
415 scheme: match v["prepend_scheme"].as_str() {
416 Some("always") | None => PrependScheme::Always,
417 Some("first") => PrependScheme::First,
418 Some("never") => PrependScheme::Never,
419 Some(other) => return Err(invalid(format!("tokenizer.json: Metaspace prepend scheme `{other}`"))),
420 },
421 split: v["split"].as_bool().unwrap_or(true),
422 },
423 other => return Err(invalid(format!("tokenizer.json: the {other:?} pre-tokenizer, which this port does not run"))),
424 }))
425 }
426
427 fn split_each(pieces: Vec<Piece>, f: impl Fn(&Piece) -> Vec<String>) -> Vec<Piece> {
430 let mut out = Vec::new();
431 for p in pieces {
432 for (i, text) in f(&p).into_iter().enumerate() {
433 if !text.is_empty() {
434 out.push(Piece { text, at_origin: p.at_origin && i == 0 });
435 }
436 }
437 }
438 out
439 }
440
441 fn run(&self, pieces: Vec<Piece>) -> Vec<Piece> {
442 match self {
443 PreTokenizer::Sequence(v) => v.iter().fold(pieces, |p, t| t.run(p)),
444 PreTokenizer::Split { pattern, behavior, invert } => {
445 Self::split_each(pieces, |p| split(&p.text, pattern, *behavior, *invert))
446 }
447 PreTokenizer::ByteLevel { add_prefix_space, use_regex } => {
448 let re = use_regex.then(|| Pattern::Re(Regex::new(BYTE_LEVEL_PATTERN).expect("the byte-level pattern compiles")));
449 let split = Self::split_each(pieces, |p| {
450 let text = if *add_prefix_space && !p.text.starts_with(' ') { format!(" {}", p.text) } else { p.text.clone() };
451 match &re {
452 Some(re) => split(&text, re, Behavior::Isolated, false),
453 None => vec![text],
454 }
455 });
456 split.into_iter().map(|p| Piece { text: byte_level(&p.text), at_origin: p.at_origin }).collect()
457 }
458 PreTokenizer::Digits(individual) => {
459 let b = if *individual { Behavior::Isolated } else { Behavior::Contiguous };
460 Self::split_each(pieces, |p| split(&p.text, &Pattern::Char(char::is_numeric), b, false))
461 }
462 PreTokenizer::Whitespace => {
463 let re = Pattern::Re(Regex::new(WHITESPACE_PATTERN).expect("the whitespace pattern compiles"));
464 Self::split_each(pieces, |p| split(&p.text, &re, Behavior::Removed, true))
465 }
466 PreTokenizer::WhitespaceSplit => {
467 Self::split_each(pieces, |p| split(&p.text, &Pattern::Char(char::is_whitespace), Behavior::Removed, false))
468 }
469 PreTokenizer::Punctuation(b) => Self::split_each(pieces, |p| {
470 split(&p.text, &Pattern::Char(|c| c.is_ascii_punctuation() || onig::category_name(c as u32).starts_with('P')), *b, false)
471 }),
472 PreTokenizer::Metaspace { replacement, scheme, split: do_split } => {
473 let rep = replacement.to_string();
474 Self::split_each(pieces, |p| {
475 let mut text = p.text.replace(' ', &rep);
476 let prepend = match scheme {
477 PrependScheme::Always => !text.starts_with(*replacement),
478 PrependScheme::First => !text.starts_with(*replacement) && p.at_origin,
479 PrependScheme::Never => false,
480 };
481 if prepend {
482 text = format!("{rep}{text}");
483 }
484 if *do_split {
485 split(&text, &Pattern::Text(vec![*replacement]), Behavior::MergedWithNext, false)
486 } else {
487 vec![text]
488 }
489 })
490 }
491 }
492 }
493}
494
495fn byte_level(s: &str) -> String {
497 s.bytes().map(byte_char).collect()
498}
499
500pub(super) fn byte_char(b: u8) -> char {
501 let printable = |b: u8| matches!(b, 33..=126 | 161..=172 | 174..=255);
502 if printable(b) {
503 return b as char;
504 }
505 let n = (0..b).filter(|&x| !printable(x)).count() as u32;
506 char::from_u32(256 + n).unwrap()
507}
508
509struct Bpe {
511 vocab: HashMap<String, u32>,
512 merges: HashMap<(u32, u32), (u32, u32)>,
514 unk: Option<u32>,
515 fuse_unk: bool,
516 byte_fallback: bool,
517 prefix: Option<String>,
518 suffix: Option<String>,
519 ignore_merges: bool,
520}
521
522#[derive(Clone, Copy)]
523struct Symbol {
524 id: u32,
525 prev: isize,
526 next: isize,
527 len: usize,
528}
529
530#[derive(PartialEq, Eq)]
531struct Merge {
532 pos: usize,
533 rank: u32,
534 new_id: u32,
535}
536
537impl Ord for Merge {
538 fn cmp(&self, o: &Self) -> std::cmp::Ordering {
539 o.rank.cmp(&self.rank).then(o.pos.cmp(&self.pos))
540 }
541}
542
543impl PartialOrd for Merge {
544 fn partial_cmp(&self, o: &Self) -> Option<std::cmp::Ordering> {
545 Some(self.cmp(o))
546 }
547}
548
549impl Bpe {
550 fn of(m: &Value) -> io::Result<Bpe> {
551 if m["type"].as_str() != Some("BPE") {
552 return Err(invalid(format!("tokenizer.json: a `{}` model, which this port does not run", m["type"])));
553 }
554 if m["dropout"].as_f64().is_some_and(|d| d > 0.0) {
555 return Err(invalid("tokenizer.json: a byte-pair model with dropout, whose output is random"));
556 }
557 let mut vocab = HashMap::new();
558 for (t, id) in m["vocab"].as_object().into_iter().flatten() {
559 vocab.insert(t.clone(), id.as_u64().ok_or_else(|| invalid(format!("tokenizer.json: `{t}` has no id")))? as u32);
560 }
561 let prefix = m["continuing_subword_prefix"].as_str().map(str::to_string);
562 let prefix_len = prefix.as_ref().map_or(0, String::len);
563 let mut merges = HashMap::new();
564 for (rank, entry) in m["merges"].as_array().into_iter().flatten().enumerate() {
565 let (a, b) = match entry {
566 Value::String(s) => {
567 let parts: Vec<&str> = s.split(' ').collect();
568 if parts.len() != 2 {
569 return Err(invalid(format!("tokenizer.json: merge `{s}` is not two parts")));
570 }
571 (parts[0].to_string(), parts[1].to_string())
572 }
573 Value::Array(p) if p.len() == 2 => (
574 p[0].as_str().unwrap_or_default().to_string(),
575 p[1].as_str().unwrap_or_default().to_string(),
576 ),
577 other => return Err(invalid(format!("tokenizer.json: merge {other}"))),
578 };
579 let id = |t: &str| vocab.get(t).copied().ok_or_else(|| invalid(format!("tokenizer.json: merge token `{t}` is not in the vocabulary")));
580 let merged = format!("{a}{}", b.get(prefix_len..).unwrap_or_default());
581 merges.insert((id(&a)?, id(&b)?), (rank as u32, id(&merged)?));
582 }
583 let unk = match m["unk_token"].as_str() {
584 Some(u) => Some(*vocab.get(u).ok_or_else(|| invalid(format!("tokenizer.json: unknown token `{u}` is not in the vocabulary")))?),
585 None => None,
586 };
587 Ok(Bpe {
588 vocab,
589 merges,
590 unk,
591 fuse_unk: m["fuse_unk"].as_bool().unwrap_or(false),
592 byte_fallback: m["byte_fallback"].as_bool().unwrap_or(false),
593 prefix,
594 suffix: m["end_of_word_suffix"].as_str().map(str::to_string),
595 ignore_merges: m["ignore_merges"].as_bool().unwrap_or(false),
596 })
597 }
598
599 fn tokenize(&self, w: &str, out: &mut Vec<u32>) {
601 if w.is_empty() {
602 return;
603 }
604 if self.ignore_merges {
605 if let Some(&id) = self.vocab.get(w) {
606 out.push(id);
607 return;
608 }
609 }
610 let mut syms: Vec<Symbol> = Vec::new();
611 let add = |syms: &mut Vec<Symbol>, id: u32, len: usize| {
612 let n = syms.len() as isize;
613 if let Some(last) = syms.last_mut() {
614 last.next = n;
615 }
616 syms.push(Symbol { id, prev: n - 1, next: -1, len });
617 };
618 let mut unk: Option<(u32, usize)> = None;
619 let chars: Vec<(usize, char)> = w.char_indices().collect();
620 for (k, &(_, c)) in chars.iter().enumerate() {
621 let (first, last) = (k == 0, k + 1 == chars.len());
622 let byte_len = c.len_utf8();
623 let mut s = c.to_string();
624 if !first {
625 if let Some(p) = &self.prefix {
626 s = format!("{p}{s}");
627 }
628 }
629 if last {
630 if let Some(x) = &self.suffix {
631 s = format!("{s}{x}");
632 }
633 }
634 if let Some(&id) = self.vocab.get(&s) {
635 if let Some((u, l)) = unk.take() {
636 add(&mut syms, u, l);
637 }
638 add(&mut syms, id, byte_len);
639 continue;
640 }
641 if self.byte_fallback {
642 let ids: Option<Vec<u32>> = s.bytes().map(|b| self.vocab.get(&format!("<0x{b:02X}>")).copied()).collect();
643 if let Some(ids) = ids {
644 for id in ids {
645 add(&mut syms, id, 1);
646 }
647 continue;
648 }
649 }
650 if let Some(u) = self.unk {
651 unk = match (unk, self.fuse_unk) {
652 (Some((id, l)), true) => Some((id, l + byte_len)),
653 (Some((id, l)), false) => {
654 add(&mut syms, id, l);
655 Some((u, byte_len))
656 }
657 (None, _) => Some((u, byte_len)),
658 };
659 }
660 }
661 if let Some((u, l)) = unk {
662 add(&mut syms, u, l);
663 }
664 let mut queue: BinaryHeap<Merge> = syms
665 .windows(2)
666 .enumerate()
667 .filter_map(|(pos, w)| self.merges.get(&(w[0].id, w[1].id)).map(|&(rank, new_id)| Merge { pos, rank, new_id }))
668 .collect();
669 while let Some(top) = queue.pop() {
670 if syms[top.pos].len == 0 || syms[top.pos].next == -1 {
671 continue;
672 }
673 let next = syms[top.pos].next as usize;
674 let right = syms[next];
675 if self.merges.get(&(syms[top.pos].id, right.id)).is_none_or(|&(_, id)| id != top.new_id) {
676 continue;
677 }
678 syms[top.pos].id = top.new_id;
679 syms[top.pos].len += right.len;
680 syms[top.pos].next = right.next;
681 syms[next].len = 0;
682 if right.next > -1 && (right.next as usize) < syms.len() {
683 syms[right.next as usize].prev = top.pos as isize;
684 }
685 let cur = syms[top.pos];
686 if cur.prev >= 0 {
687 let prev = cur.prev as usize;
688 if let Some(&(rank, new_id)) = self.merges.get(&(syms[prev].id, cur.id)) {
689 queue.push(Merge { pos: prev, rank, new_id });
690 }
691 }
692 if cur.next >= 0 && (cur.next as usize) < syms.len() {
693 if let Some(&(rank, new_id)) = self.merges.get(&(cur.id, syms[cur.next as usize].id)) {
694 queue.push(Merge { pos: top.pos, rank, new_id });
695 }
696 }
697 }
698 out.extend(syms.iter().filter(|s| s.len != 0).map(|s| s.id));
699 }
700}
701
702struct AddedToken {
704 id: u32,
705 content: Vec<char>,
706 single_word: bool,
707 lstrip: bool,
708 rstrip: bool,
709}
710
711struct AddedSet {
713 tokens: Vec<AddedToken>,
714 by_first: HashMap<char, Vec<usize>>,
716}
717
718impl AddedSet {
719 fn new(tokens: Vec<AddedToken>) -> AddedSet {
720 let mut by_first: HashMap<char, Vec<usize>> = HashMap::new();
721 for (i, t) in tokens.iter().enumerate() {
722 if let Some(&c) = t.content.first() {
723 by_first.entry(c).or_default().push(i);
724 }
725 }
726 for v in by_first.values_mut() {
727 v.sort_by(|&a, &b| tokens[b].content.len().cmp(&tokens[a].content.len()).then(a.cmp(&b)));
728 }
729 AddedSet { tokens, by_first }
730 }
731
732 fn split(&self, s: &[char]) -> Vec<(Option<u32>, usize, usize)> {
734 if s.is_empty() {
735 return vec![(None, 0, 0)];
736 }
737 let word = |c: char| c.is_alphanumeric() || c == '_';
738 let mut out = Vec::new();
739 let mut start_offset = 0;
740 let mut i = 0;
741 while i < s.len() {
742 let hit = self.by_first.get(&s[i]).and_then(|cands| {
743 cands.iter().copied().find(|&t| s[i..].starts_with(&self.tokens[t].content))
744 });
745 let Some(t) = hit else {
746 i += 1;
747 continue;
748 };
749 let tok = &self.tokens[t];
750 let (mut start, mut stop) = (i, i + tok.content.len());
751 i = stop;
752 if tok.single_word {
753 let start_space = start == 0 || !word(s[start - 1]);
754 let stop_space = stop == s.len() || !word(s[stop]);
755 if !start_space || !stop_space {
756 continue;
757 }
758 }
759 if tok.lstrip {
760 let mut l = start;
761 while l > 0 && s[l - 1].is_whitespace() {
762 l -= 1;
763 }
764 start = l.max(start_offset);
765 }
766 if tok.rstrip {
767 while stop < s.len() && s[stop].is_whitespace() {
768 stop += 1;
769 }
770 }
771 if start_offset < start {
772 out.push((None, start_offset, start));
773 }
774 out.push((Some(tok.id), start, stop));
775 start_offset = stop;
776 }
777 if start_offset != s.len() {
778 out.push((None, start_offset, s.len()));
779 }
780 out
781 }
782}
783
784fn post_processor_wrap(pp: &Value, before: &mut Vec<u32>, after: &mut Vec<u32>) -> io::Result<()> {
786 let id_of = |v: &Value, what: &str| -> io::Result<u32> {
787 v.as_u64().map(|x| x as u32).ok_or_else(|| invalid(format!("tokenizer.json: the post-processor's {what} has no id")))
788 };
789 match pp["type"].as_str() {
790 None if pp.is_null() => {}
791 Some("Sequence") => {
792 for p in pp["processors"].as_array().into_iter().flatten() {
793 post_processor_wrap(p, before, after)?;
794 }
795 }
796 Some("ByteLevel") => {}
797 Some("TemplateProcessing") => {
798 let single = pp["single"].as_array().cloned().unwrap_or_default();
799 let at = single.iter().position(|i| i.get("Sequence").is_some()).unwrap_or(single.len());
800 let ids = |items: &[Value]| -> io::Result<Vec<u32>> {
801 let mut out = Vec::new();
802 for i in items {
803 if let Some(name) = i["SpecialToken"]["id"].as_str() {
804 for id in pp["special_tokens"][name]["ids"].as_array().into_iter().flatten() {
805 out.push(id_of(id, name)?);
806 }
807 }
808 }
809 Ok(out)
810 };
811 let mut head = ids(&single[..at])?;
812 head.append(before);
813 *before = head;
814 after.extend(ids(&single[(at + 1).min(single.len())..])?);
815 }
816 Some("RobertaProcessing" | "BertProcessing") => {
817 before.insert(0, id_of(&pp["cls"][1], "cls")?);
818 after.push(id_of(&pp["sep"][1], "sep")?);
819 }
820 Some(other) => return Err(invalid(format!("tokenizer.json: the `{other}` post-processor, which this port does not run"))),
821 None => return Err(invalid("tokenizer.json: a post-processor without a type")),
822 }
823 Ok(())
824}
825
826pub struct HfPreTokenizer(PreTokenizer);
828
829impl HfPreTokenizer {
830 pub fn from_json(v: &Value) -> io::Result<HfPreTokenizer> {
831 PreTokenizer::of(v)?.map(HfPreTokenizer).ok_or_else(|| invalid("tokenizer.json: no pre-tokenizer"))
832 }
833
834 pub fn pre_tokenize_str(&self, text: &str) -> Vec<String> {
836 self.0.run(vec![Piece { text: text.to_string(), at_origin: true }]).into_iter().map(|p| p.text).collect()
837 }
838}
839
840pub struct HfTokenizer {
842 raw: AddedSet,
843 normalized: AddedSet,
844 normalizer: Option<Normalizer>,
845 pre: Option<PreTokenizer>,
846 model: Bpe,
847 before: Vec<u32>,
848 after: Vec<u32>,
849}
850
851impl HfTokenizer {
852 pub fn from_file(path: &Path) -> io::Result<HfTokenizer> {
853 let text = std::fs::read_to_string(path).map_err(|e| io::Error::new(e.kind(), format!("{}: {e}", path.display())))?;
854 let json: Value = serde_json::from_str(&text).map_err(|e| invalid(format!("{}: {e}", path.display())))?;
855 HfTokenizer::from_json(&json).map_err(|e| invalid(format!("{}: {e}", path.display())))
856 }
857
858 pub fn from_json(tj: &Value) -> io::Result<HfTokenizer> {
859 let normalizer = Normalizer::of(&tj["normalizer"])?;
860 let mut raw = Vec::new();
861 let mut normalized = Vec::new();
862 for a in tj["added_tokens"].as_array().into_iter().flatten() {
863 let content = a["content"].as_str().unwrap_or_default().to_string();
864 let id = a["id"].as_u64().ok_or_else(|| invalid(format!("tokenizer.json: added token `{content}` has no id")))? as u32;
865 let flag = |k: &str| a[k].as_bool().unwrap_or(false);
866 let is_normalized = flag("normalized");
867 let text = match (&normalizer, is_normalized) {
868 (Some(n), true) => n.run(content),
869 _ => content,
870 };
871 let t = AddedToken { id, content: text.chars().collect(), single_word: flag("single_word"), lstrip: flag("lstrip"), rstrip: flag("rstrip") };
872 if is_normalized {
873 normalized.push(t);
874 } else {
875 raw.push(t);
876 }
877 }
878 let (mut before, mut after) = (Vec::new(), Vec::new());
879 post_processor_wrap(&tj["post_processor"], &mut before, &mut after)?;
880 Ok(HfTokenizer {
881 raw: AddedSet::new(raw),
882 normalized: AddedSet::new(normalized),
883 normalizer,
884 pre: PreTokenizer::of(&tj["pre_tokenizer"])?,
885 model: Bpe::of(&tj["model"])?,
886 before,
887 after,
888 })
889 }
890
891 pub fn encode(&self, text: &str, add_special_tokens: bool) -> Vec<u32> {
893 enum Part {
895 Token(u32),
896 Text(String, bool),
897 }
898 let chars: Vec<char> = text.chars().collect();
899 let mut parts: Vec<Part> = self
900 .raw
901 .split(&chars)
902 .into_iter()
903 .filter(|&(id, a, b)| id.is_some() || a != b)
904 .map(|(id, a, b)| match id {
905 Some(id) => Part::Token(id),
906 None => Part::Text(chars[a..b].iter().collect(), a == 0),
907 })
908 .collect();
909 let mut second = Vec::new();
910 for p in parts.drain(..) {
911 match p {
912 Part::Token(id) => second.push(Part::Token(id)),
913 Part::Text(s, at_origin) => {
914 let s = match &self.normalizer {
915 Some(n) => n.run(s),
916 None => s,
917 };
918 let cs: Vec<char> = s.chars().collect();
919 for (id, a, b) in self.normalized.split(&cs) {
920 match id {
921 Some(id) => second.push(Part::Token(id)),
922 None if a != b => second.push(Part::Text(cs[a..b].iter().collect(), at_origin && a == 0)),
923 None => {}
924 }
925 }
926 }
927 }
928 }
929 let mut ids = Vec::new();
930 if add_special_tokens {
931 ids.extend(&self.before);
932 }
933 for p in second {
934 match p {
935 Part::Token(id) => ids.push(id),
936 Part::Text(text, at_origin) => {
937 let pieces = vec![Piece { text, at_origin }];
938 let pieces = match &self.pre {
939 Some(pre) => pre.run(pieces),
940 None => pieces,
941 };
942 for piece in pieces {
943 self.model.tokenize(&piece.text, &mut ids);
944 }
945 }
946 }
947 }
948 if add_special_tokens {
949 ids.extend(&self.after);
950 }
951 ids
952 }
953}