1use anyhow::{Context, Result};
2use std::path::Path;
3use tokenizers::Tokenizer;
4
5pub struct ChatTokenizer {
6 pub inner: Tokenizer,
7 pub template: Option<crate::prompt::ChatTemplate>,
8 pub im_end: u32,
9 pub endoftext: u32,
10}
11
12impl ChatTokenizer {
13 pub fn load(model_dir: &Path) -> Result<Self> {
14 let path = model_dir.join("tokenizer.json");
15 let mut inner = Tokenizer::from_file(&path)
16 .map_err(|e| anyhow::anyhow!("loading {}: {e}", path.display()))?;
17
18 inner.with_padding(None);
21 inner
22 .with_truncation(None)
23 .map_err(|e| anyhow::anyhow!("tokenizer truncation: {e}"))?;
24
25 let tok = |s: &str| -> Result<u32> {
26 inner
27 .token_to_id(s)
28 .with_context(|| format!("special token {s:?} missing"))
29 };
30 let im_end = tok("<|im_end|>")?;
31 let endoftext = tok("<|endoftext|>")?;
32
33 Ok(ChatTokenizer {
34 inner,
35 template: crate::prompt::ChatTemplate::load(model_dir)?,
36 im_end,
37 endoftext,
38 })
39 }
40
41 pub fn encode(&self, text: &str) -> Result<Vec<u32>> {
42 let enc = self
44 .inner
45 .encode(text, false)
46 .map_err(|e| anyhow::anyhow!("encode: {e}"))?;
47
48 Ok(enc.get_ids().to_vec())
49 }
50
51 pub fn decode(&self, ids: &[u32]) -> Result<String> {
52 self.inner
53 .decode(ids, false)
54 .map_err(|e| anyhow::anyhow!("decode: {e}"))
55 }
56}