#![doc = include_str!("../README.md")]
#![allow(clippy::upper_case_acronyms)]
mod added_tokens;
mod byte_level;
mod config;
mod models;
mod normalizers;
mod pre_tokenized;
mod pre_tokenizers;
use std::borrow::Cow;
use self::added_tokens::{AddedTokens, Segment};
use self::config::TokenizerJson;
use self::models::Model;
use self::normalizers::Normalizer;
use self::pre_tokenized::{PreTokenizedString, PtSplit};
use self::pre_tokenizers::PreTokenizer;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("failed to parse JSON: {0}")]
Json(#[from] serde_json::Error),
#[error("normalizer error: {0}")]
Normalizer(#[from] normalizers::Error),
#[error("pre-tokenizer error: {0}")]
PreTokenizer(#[from] pre_tokenizers::Error),
#[error("model error: {0}")]
Model(String),
}
pub struct Tokenizer {
added_tokens: Option<AddedTokens>,
normalizer: Option<Normalizer>,
pre_tokenizer: Option<PreTokenizer>,
model: Model,
}
impl Tokenizer {
#[rustfmt::skip]
fn build(json: TokenizerJson) -> Result<Self, Error> {
let added_tokens = AddedTokens::from_configs(&json.added_tokens).map_err(Error::Model)?;
let normalizer = json.normalizer.map(Normalizer::from_config).transpose()?;
let pre_tokenizer = json.pre_tokenizer.map(PreTokenizer::from_config).transpose()?;
let model = Model::from_config(json.model).map_err(Error::Model)?;
Ok(Self { added_tokens, normalizer, pre_tokenizer, model })
}
#[inline]
pub fn from_json(json: impl AsRef<[u8]>) -> Result<Self, Error> {
let json: TokenizerJson = serde_json::from_slice(json.as_ref())?;
Self::build(json)
}
pub fn encode_len(&self, input: &str) -> Result<usize, usize> {
if input.is_empty() {
return Ok(0);
}
if input.len() >= i32::MAX as usize {
return Err(input.len() >> 2);
}
match self.count_tokens_internal(input) {
Ok(count) => Ok(count),
Err(_) => Err(input.len() >> 2),
}
}
#[rustfmt::skip]
fn count_tokens_internal(&self, input: &str) -> Result<usize, String> {
let mut pts = self.build_pre_tokenized(input);
if let Some(pt) = &self.pre_tokenizer {
pt.pre_tokenize(&mut pts).map_err(|e| e.to_string())?;
}
let mut count = pts.added_token_count();
for text in pts.texts() {
count += self.model.count_tokens(text)?;
}
Ok(count)
}
#[rustfmt::skip]
fn build_pre_tokenized(&self, input: &str) -> PreTokenizedString {
let segments = match &self.added_tokens {
Some(at) => at.split(input),
None => return self.normalize_into_pts(input),
};
if segments.len() == 1 && let Segment::Text(text) = segments[0] {
return self.normalize_into_pts(text);
}
let mut buffer = String::with_capacity(input.len());
let mut splits = Vec::new();
for seg in &segments {
match seg {
Segment::Text("") => continue,
Segment::Text(text) => {
let start = buffer.len();
buffer.push_str(&self.normalize(text));
splits.push(PtSplit::from_range(start..buffer.len()));
}
Segment::Token(id) => {
splits.push(PtSplit::from_token(buffer.len(), *id));
}
}
}
PreTokenizedString::new(buffer, splits)
}
#[inline]
fn normalize_into_pts(&self, text: &str) -> PreTokenizedString {
match self.normalize(text) {
Cow::Borrowed("") => PreTokenizedString::EMPTY,
Cow::Owned(s) if s.is_empty() => PreTokenizedString::EMPTY,
Cow::Borrowed(s) => PreTokenizedString {
splits: vec![PtSplit::from_range(0..s.len())],
buffer: s.to_owned(),
},
Cow::Owned(s) => PreTokenizedString {
splits: vec![PtSplit::from_range(0..s.len())],
buffer: s,
},
}
}
#[inline]
fn normalize<'a>(&self, text: &'a str) -> Cow<'a, str> {
match &self.normalizer {
Some(n) => n.normalize(text),
None => Cow::Borrowed(text),
}
}
}