1use crate::error::{Error, Result};
8use serde_json::Value;
9use std::path::Path;
10use tokenizers::Tokenizer as Backend;
11
12pub struct Tokenizer {
14 backend: Backend,
15 pub cls_token: String,
16 pub sep_token: String,
17 pub pad_token: String,
18 pub mask_token: String,
19 pub cls_token_id: u32,
20 pub sep_token_id: u32,
21 pub pad_token_id: u32,
22 pub mask_token_id: u32,
23}
24
25impl std::fmt::Debug for Tokenizer {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 f.debug_struct("Tokenizer")
28 .field("mask_token", &self.mask_token)
29 .field("vocab_size", &self.backend.get_vocab_size(true))
30 .finish_non_exhaustive()
31 }
32}
33
34fn special_token(config: &Value, name: &'static str) -> Option<String> {
35 match config.get(name)? {
36 Value::String(text) => Some(text.clone()),
37 Value::Object(map) => map.get("content")?.as_str().map(str::to_string),
39 _ => None,
40 }
41}
42
43impl Tokenizer {
44 pub fn from_dir(dir: impl AsRef<Path>) -> Result<Self> {
46 let dir = dir.as_ref();
47 let tokenizer_path = dir.join("tokenizer.json");
48 let mut backend =
49 Backend::from_file(&tokenizer_path).map_err(|error| Error::Tokenizer {
50 path: tokenizer_path,
51 message: error.to_string(),
52 })?;
53 backend.with_padding(None);
56 backend
57 .with_truncation(None)
58 .map_err(|error| Error::Tokenizer {
59 path: dir.join("tokenizer.json"),
60 message: error.to_string(),
61 })?;
62
63 let config_path = dir.join("tokenizer_config.json");
64 let config: Value = serde_json::from_str(&std::fs::read_to_string(&config_path)?)?;
65
66 let mut resolved = Vec::with_capacity(4);
67 for name in ["cls_token", "sep_token", "pad_token", "mask_token"] {
68 let token = special_token(&config, name).ok_or(Error::MissingSpecialToken(name))?;
69 let id = backend
70 .token_to_id(&token)
71 .ok_or(Error::MissingSpecialToken(name))?;
72 resolved.push((token, id));
73 }
74 let [cls, sep, pad, mask]: [(String, u32); 4] =
75 resolved.try_into().expect("exactly four special tokens");
76
77 Ok(Self {
78 backend,
79 cls_token: cls.0,
80 sep_token: sep.0,
81 pad_token: pad.0,
82 mask_token: mask.0,
83 cls_token_id: cls.1,
84 sep_token_id: sep.1,
85 pad_token_id: pad.1,
86 mask_token_id: mask.1,
87 })
88 }
89
90 pub fn encode(&self, text: &str) -> Result<Vec<u32>> {
92 self.backend
93 .encode_fast(text, false)
94 .map(|encoding| encoding.get_ids().to_vec())
95 .map_err(|error| Error::Tokenizer {
96 path: Default::default(),
97 message: error.to_string(),
98 })
99 }
100
101 pub fn vocab_size(&self) -> usize {
102 self.backend.get_vocab_size(true)
103 }
104}