#![doc = include_str!("../README.md")]
use std::borrow::Cow;
use self::added_tokens::{AddedTokens, Segment};
use self::bpe::Model;
use self::config::TokenizerJson;
use self::normalizer::Normalizer;
use self::pre_tokenized::{PreTokenizedString, Split};
use self::pre_tokenizer::PreTokenizer;
mod added_tokens;
mod bpe;
mod byte_level;
pub mod config;
mod normalizer;
mod pre_tokenized;
mod pre_tokenizer;
mod split;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("failed to parse JSON: {0}")]
Json(#[from] serde_json::Error),
#[error("normalizer error: {0}")]
Normalizer(#[from] normalizer::Error),
#[error("pre-tokenizer error: {0}")]
PreTokenizer(#[from] pre_tokenizer::Error),
#[error("model error: {0}")]
Model(String),
}
pub struct Tokenizer {
added_tokens: Option<AddedTokens>,
normalizer: Option<Normalizer>,
pre_tokenizer: Option<PreTokenizer>,
model: Model,
split_only: Option<PreTokenizer>,
}
impl Tokenizer {
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 model = Model::from_config(json.model).map_err(Error::Model)?;
let pre_tokenizer = json
.pre_tokenizer
.map(PreTokenizer::from_config)
.transpose()?;
let split_only = {
if let Some(pret) = pre_tokenizer.as_ref()
&& let PreTokenizer::Sequence(steps) = pret
&& steps.len() == 2
{
let is_split = matches!(&steps[0], PreTokenizer::Split(_));
let is_bulk_bl =
matches!(&steps[1], PreTokenizer::ByteLevel(bl) if bl.is_bulk_only());
(is_split && is_bulk_bl).then(|| steps[0].clone())
} else {
None
}
};
Ok(Self {
added_tokens,
normalizer,
pre_tokenizer,
model,
split_only,
})
}
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() >= u32::MAX as usize {
return Err(input.len() >> 2);
}
match self.count_tokens_internal(input) {
Ok(count) => Ok(count),
Err(_) => Err(input.len() >> 2),
}
}
fn count_tokens_internal(&self, input: &str) -> Result<usize, String> {
let mut pts = self.build_pre_tokenized(input);
if let Some(split) = &self.split_only {
split.pre_tokenize(&mut pts).map_err(|e| e.to_string())?;
return pts.count_tokens_batched(|buf, splits, count| {
self.model.count_tokens_batch_fused(buf, splits, count)
});
}
if let Some(pt) = &self.pre_tokenizer {
pt.pre_tokenize(&mut pts).map_err(|e| e.to_string())?;
}
pts.count_tokens(|text, count| {
*count += self.model.count_tokens(text)?;
Ok(())
})
}
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::Token(id) => {
splits.push(Split {
range: buffer.len()..buffer.len(),
token_id: Some(*id),
});
}
Segment::Text(text) => {
if text.is_empty() {
continue;
}
let start = buffer.len();
buffer.push_str(&self.normalize(text));
let end = buffer.len();
splits.push(Split {
range: start..end,
token_id: None,
});
}
}
}
PreTokenizedString::new(buffer, splits)
}
#[inline]
fn normalize_into_pts(&self, text: &str) -> PreTokenizedString {
match self.normalize(text) {
Cow::Borrowed(_) => PreTokenizedString::from_text(text),
Cow::Owned(s) => {
let len = s.len();
PreTokenizedString::new(
s,
vec![Split {
range: 0..len,
token_id: None,
}],
)
}
}
}
#[inline]
fn normalize<'a>(&self, text: &'a str) -> Cow<'a, str> {
match &self.normalizer {
Some(n) => n.normalize(text),
None => Cow::Borrowed(text),
}
}
}