use std::collections::HashMap;
use std::ops::Range;
use std::sync::Arc;
use tokenizers::{AddedVocabulary, Tokenizer};
pub trait TokenizerEncoder: Send + Sync {
fn encode_ids(&self, text: &str, special_ranges: &[Range<usize>]) -> Result<Vec<u32>, String>;
}
#[derive(Clone)]
pub struct PromptTokenizer {
plain_tokenizer: Tokenizer,
added_vocabulary: HashMap<String, u32>,
}
impl PromptTokenizer {
pub fn new(tokenizer: &Tokenizer) -> Self {
let added_vocabulary = tokenizer
.get_added_vocabulary()
.get_vocab()
.iter()
.map(|(token, &id)| (token.clone(), id))
.collect();
let mut plain_tokenizer = tokenizer.clone();
plain_tokenizer.with_added_vocabulary(AddedVocabulary::new());
Self {
plain_tokenizer,
added_vocabulary,
}
}
fn special_token_id(&self, token: &str) -> Result<u32, String> {
self.added_vocabulary
.get(token)
.copied()
.ok_or_else(|| format!("special token {token:?} is not in the added vocabulary"))
}
}
impl TokenizerEncoder for PromptTokenizer {
fn encode_ids(&self, text: &str, special_ranges: &[Range<usize>]) -> Result<Vec<u32>, String> {
let mut regions = Vec::with_capacity(special_ranges.len() + 1);
let mut start = 0;
for range in special_ranges {
regions.push(&text[start..range.start]);
start = range.end;
}
regions.push(&text[start..]);
let encodings = self
.plain_tokenizer
.encode_batch_fast(regions, false)
.map_err(|error| error.to_string())?;
let mut ids = Vec::new();
let mut encodings = encodings.iter();
for range in special_ranges {
if let Some(encoding) = encodings.next() {
ids.extend_from_slice(encoding.get_ids());
}
ids.push(self.special_token_id(&text[range.clone()])?);
}
if let Some(encoding) = encodings.next() {
ids.extend_from_slice(encoding.get_ids());
}
Ok(ids)
}
}
impl<T: TokenizerEncoder + ?Sized> TokenizerEncoder for Arc<T> {
fn encode_ids(&self, text: &str, special_ranges: &[Range<usize>]) -> Result<Vec<u32>, String> {
(**self).encode_ids(text, special_ranges)
}
}