use rustc_hash::{FxHashMap, FxHashSet};
use super::any_tokenizer::{AnyTokenizer, Backend};
use super::policy::SpecialPolicy;
use super::spm::{SpmPrefixScheme, SpmTokenizer, NEVER_MERGE};
use super::tokenizer::{
Tokenizer, TokenizerError, CL100K_BASE_PATTERN, DEEPSEEK_V3_PATTERNS, GPT2_PATTERN,
KIMI_PATTERN, LLAMA3_PATTERN, MISTRAL_V3_PATTERN, O200K_BASE_PATTERN, QWEN2_PATTERN,
};
use super::vocab::{load_spm_vocab, place_special_pieces};
use super::whisper::{whisper_special_tokens, WhisperVariant};
#[cfg(feature = "vocab-cl100k")]
pub const CL100K_BASE_VOCAB_PACKED: &[u8] = include_bytes!("../../vocabs/cl100k_base.splv");
#[cfg(feature = "vocab-o200k")]
pub const O200K_BASE_VOCAB_PACKED: &[u8] = include_bytes!("../../vocabs/o200k_base.splv");
#[cfg(feature = "vocab-llama3")]
pub const LLAMA3_VOCAB_PACKED: &[u8] = include_bytes!("../../vocabs/llama3.splv");
#[cfg(feature = "vocab-deepseek")]
pub const DEEPSEEK_V3_VOCAB_PACKED: &[u8] = include_bytes!("../../vocabs/deepseek_v3.splv");
#[cfg(feature = "vocab-qwen")]
pub const QWEN3_VOCAB_PACKED: &[u8] = include_bytes!("../../vocabs/qwen3.splv");
#[cfg(feature = "vocab-glm")]
pub const GLM4_VOCAB_PACKED: &[u8] = include_bytes!("../../vocabs/glm4.splv");
#[cfg(feature = "vocab-kimi")]
pub const KIMI_VOCAB_PACKED: &[u8] = include_bytes!("../../vocabs/kimi.splv");
#[cfg(feature = "vocab-mistral")]
pub const MISTRAL_SPM_VOCAB: &[u8] = include_bytes!("../../vocabs/mistral.spm");
#[cfg(feature = "vocab-mistral")]
pub const MISTRAL_V2_SPM_VOCAB: &[u8] = include_bytes!("../../vocabs/mistral_v2.spm");
#[cfg(feature = "vocab-mistral")]
pub const MISTRAL_V3_VOCAB_PACKED: &[u8] = include_bytes!("../../vocabs/mistral_v3_tekken.splv");
#[cfg(feature = "vocab-whisper")]
pub const WHISPER_VOCAB_PACKED: &[u8] = include_bytes!("../../vocabs/whisper.splv");
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PretrainedVocab {
Cl100kBase,
O200kBase,
Llama3,
DeepseekV3,
Qwen3,
Glm4,
GptOss,
KimiK2,
KimiK3,
MistralV1,
MistralV2,
MistralV3,
WhisperV1,
WhisperV2,
WhisperV3,
}
impl PretrainedVocab {
pub fn from_name(name: &str) -> Option<Self> {
match name {
"cl100k_base" => Some(Self::Cl100kBase),
"o200k_base" => Some(Self::O200kBase),
"llama3" | "llama3.1" | "llama3.2" | "llama3.3" => Some(Self::Llama3),
"deepseek_v3" | "deepseek-v3" => Some(Self::DeepseekV3),
"qwen" | "qwen2" | "qwen3" | "qwen2.5" | "baichuan_m2" => Some(Self::Qwen3),
"glm" | "glm4" | "glm-4" | "glm4.5" | "glm-4.5" => Some(Self::Glm4),
"gpt-oss" | "gpt_oss" | "o200k_harmony" => Some(Self::GptOss),
"kimi" | "kimi_k2" | "kimi-k2" | "kimi_k2.5" | "kimi-k2.5" | "kimi_linear" => {
Some(Self::KimiK2)
}
"kimi_k3" | "kimi-k3" => Some(Self::KimiK3),
"mistral" | "mistral_v1" => Some(Self::MistralV1),
"mistral_v2" => Some(Self::MistralV2),
"mistral_v3" => Some(Self::MistralV3),
"whisper_v1" | "whisper-v1" | "whisper-multilingual-v1" => Some(Self::WhisperV1),
"whisper" | "whisper_v2" | "whisper-v2" | "whisper-multilingual" => {
Some(Self::WhisperV2)
}
"whisper_v3" | "whisper-v3" | "whisper-large-v3" => Some(Self::WhisperV3),
_ => None,
}
}
pub fn supported_names() -> &'static [&'static str] {
&[
"cl100k_base",
"o200k_base",
"llama3",
"llama3.1",
"llama3.2",
"llama3.3",
"deepseek_v3",
"deepseek-v3",
"qwen",
"qwen2",
"qwen2.5",
"qwen3",
"baichuan_m2",
"glm",
"glm4",
"glm-4",
"glm4.5",
"glm-4.5",
"gpt-oss",
"gpt_oss",
"o200k_harmony",
"kimi",
"kimi_k2",
"kimi-k2",
"kimi_k2.5",
"kimi-k2.5",
"kimi_linear",
"kimi_k3",
"kimi-k3",
"mistral",
"mistral_v1",
"mistral_v2",
"mistral_v3",
"whisper",
"whisper_v1",
"whisper_v2",
"whisper_v3",
]
}
}
pub fn from_pretrained(name: &str) -> Result<AnyTokenizer, TokenizerError> {
from_vocab(resolve_vocab(name)?)
}
fn resolve_vocab(name: &str) -> Result<PretrainedVocab, TokenizerError> {
PretrainedVocab::from_name(name).ok_or_else(|| {
TokenizerError::UnknownPretrained(format!(
"{}. Supported: {}",
name,
PretrainedVocab::supported_names().join(", ")
))
})
}
fn vocab_bytes(vocab: PretrainedVocab) -> Result<&'static [u8], TokenizerError> {
macro_rules! bundled {
($feature:literal, $konst:ident, $name:literal) => {{
#[cfg(feature = $feature)]
{
Ok($konst)
}
#[cfg(not(feature = $feature))]
{
Err(TokenizerError::VocabNotBundled($name, $feature))
}
}};
}
match vocab {
PretrainedVocab::Cl100kBase => {
bundled!("vocab-cl100k", CL100K_BASE_VOCAB_PACKED, "cl100k_base")
}
PretrainedVocab::O200kBase => {
bundled!("vocab-o200k", O200K_BASE_VOCAB_PACKED, "o200k_base")
}
PretrainedVocab::GptOss => bundled!("vocab-gpt-oss", O200K_BASE_VOCAB_PACKED, "gpt-oss"),
PretrainedVocab::Llama3 => bundled!("vocab-llama3", LLAMA3_VOCAB_PACKED, "llama3"),
PretrainedVocab::DeepseekV3 => {
bundled!("vocab-deepseek", DEEPSEEK_V3_VOCAB_PACKED, "deepseek_v3")
}
PretrainedVocab::Qwen3 => bundled!("vocab-qwen", QWEN3_VOCAB_PACKED, "qwen3"),
PretrainedVocab::Glm4 => bundled!("vocab-glm", GLM4_VOCAB_PACKED, "glm4"),
PretrainedVocab::KimiK2 => bundled!("vocab-kimi", KIMI_VOCAB_PACKED, "kimi_k2"),
PretrainedVocab::KimiK3 => bundled!("vocab-kimi", KIMI_VOCAB_PACKED, "kimi_k3"),
PretrainedVocab::MistralV1 => bundled!("vocab-mistral", MISTRAL_SPM_VOCAB, "mistral"),
PretrainedVocab::MistralV2 => bundled!("vocab-mistral", MISTRAL_V2_SPM_VOCAB, "mistral_v2"),
PretrainedVocab::MistralV3 => {
bundled!("vocab-mistral", MISTRAL_V3_VOCAB_PACKED, "mistral_v3")
}
PretrainedVocab::WhisperV1 | PretrainedVocab::WhisperV2 | PretrainedVocab::WhisperV3 => {
bundled!("vocab-whisper", WHISPER_VOCAB_PACKED, "whisper")
}
}
}
pub fn from_vocab(vocab: PretrainedVocab) -> Result<AnyTokenizer, TokenizerError> {
let special = special_tokens(vocab);
let named = special.clone();
let skipped = special_decode_ids(vocab, &special);
match vocab {
PretrainedVocab::MistralV1 | PretrainedVocab::MistralV2 => {
return spm_from_vocab(vocab_bytes(vocab)?, vocab, special, named, skipped)
}
_ => {}
}
let pats = patterns(vocab).unwrap_or(&[]);
let data = vocab_bytes(vocab)?;
let tokenizer = match vocab {
PretrainedVocab::Cl100kBase
| PretrainedVocab::O200kBase
| PretrainedVocab::GptOss
| PretrainedVocab::Llama3
| PretrainedVocab::Qwen3
| PretrainedVocab::Glm4
| PretrainedVocab::KimiK2
| PretrainedVocab::KimiK3 => Tokenizer::from_packed_chain(data, pats, special),
PretrainedVocab::DeepseekV3
| PretrainedVocab::MistralV3
| PretrainedVocab::WhisperV1
| PretrainedVocab::WhisperV2
| PretrainedVocab::WhisperV3 => {
Tokenizer::from_packed_byte_level_chain(data, pats, special)
}
PretrainedVocab::MistralV1 | PretrainedVocab::MistralV2 => {
return Err(TokenizerError::UnknownPretrained(
"Mistral V1/V2 take the SPM backend and are routed earlier".to_owned(),
))
}
}?;
Ok(AnyTokenizer::new(
Backend::Bpe(
tokenizer
.with_added_token_matching(true)
.with_special_decode_ids(skipped),
),
SpecialPolicy::boundary(None, None, Some(eos_token_id(vocab)), named),
))
}
fn spm_from_vocab(
data: &[u8],
vocab: PretrainedVocab,
special: FxHashMap<String, u32>,
named: FxHashMap<String, u32>,
skipped: FxHashSet<u32>,
) -> Result<AnyTokenizer, TokenizerError> {
let (mut pieces, mut scores) = load_spm_vocab(data)?;
place_special_pieces(&mut pieces, &special)?;
scores.resize(pieces.len(), NEVER_MERGE);
let eos = eos_token_id(vocab);
let tokenizer = SpmTokenizer::new(pieces, scores, bos_token_id(vocab), Some(eos))?
.with_prefix_scheme(spm_prefix_scheme(vocab))
.with_added_tokens(&special)?
.with_special_decode_ids(skipped);
Ok(AnyTokenizer::new(
Backend::Spm(tokenizer),
SpecialPolicy::boundary(None, None, Some(eos), named),
))
}
fn spm_prefix_scheme(vocab: PretrainedVocab) -> SpmPrefixScheme {
match vocab {
PretrainedVocab::MistralV1 => SpmPrefixScheme::AfterEachSpecial,
_ => SpmPrefixScheme::Once,
}
}
pub fn patterns(vocab: PretrainedVocab) -> Option<&'static [&'static str]> {
match vocab {
PretrainedVocab::Cl100kBase => Some(&[CL100K_BASE_PATTERN]),
PretrainedVocab::O200kBase => Some(&[O200K_BASE_PATTERN]),
PretrainedVocab::Llama3 => Some(&[LLAMA3_PATTERN]),
PretrainedVocab::DeepseekV3 => Some(DEEPSEEK_V3_PATTERNS),
PretrainedVocab::Qwen3 => Some(&[QWEN2_PATTERN]),
PretrainedVocab::Glm4 => Some(&[LLAMA3_PATTERN]),
PretrainedVocab::GptOss => Some(&[O200K_BASE_PATTERN]),
PretrainedVocab::KimiK2 | PretrainedVocab::KimiK3 => Some(&[KIMI_PATTERN]),
PretrainedVocab::MistralV1 | PretrainedVocab::MistralV2 => None,
PretrainedVocab::MistralV3 => Some(&[MISTRAL_V3_PATTERN]),
PretrainedVocab::WhisperV1 | PretrainedVocab::WhisperV2 | PretrainedVocab::WhisperV3 => {
Some(&[GPT2_PATTERN])
}
}
}
pub fn uses_byte_level(vocab: PretrainedVocab) -> bool {
matches!(
vocab,
PretrainedVocab::DeepseekV3
| PretrainedVocab::WhisperV1
| PretrainedVocab::WhisperV2
| PretrainedVocab::WhisperV3
)
}
pub fn eos_token_id(vocab: PretrainedVocab) -> u32 {
match vocab {
PretrainedVocab::Cl100kBase => 100257, PretrainedVocab::O200kBase => 199999, PretrainedVocab::Llama3 => 128001, PretrainedVocab::DeepseekV3 => 1, PretrainedVocab::Qwen3 => 151645, PretrainedVocab::Glm4 => 151329, PretrainedVocab::GptOss => 200002, PretrainedVocab::KimiK2 | PretrainedVocab::KimiK3 => 163585,
PretrainedVocab::MistralV1 | PretrainedVocab::MistralV2 | PretrainedVocab::MistralV3 => 2, PretrainedVocab::WhisperV1 => WhisperVariant::V1Multilingual.eos_token_id(),
PretrainedVocab::WhisperV2 => WhisperVariant::V2Multilingual.eos_token_id(),
PretrainedVocab::WhisperV3 => WhisperVariant::V3Multilingual.eos_token_id(),
}
}
pub fn eos_token_id_by_name(name: &str) -> u32 {
PretrainedVocab::from_name(name)
.map(eos_token_id)
.unwrap_or(0)
}
pub fn bos_token_id(vocab: PretrainedVocab) -> Option<u32> {
match vocab {
PretrainedVocab::Cl100kBase => None, PretrainedVocab::O200kBase => None, PretrainedVocab::Llama3 => Some(128000), PretrainedVocab::DeepseekV3 => Some(0), PretrainedVocab::Qwen3 | PretrainedVocab::Glm4 | PretrainedVocab::GptOss => None,
PretrainedVocab::KimiK2 | PretrainedVocab::KimiK3 => Some(163584),
PretrainedVocab::MistralV1 | PretrainedVocab::MistralV2 | PretrainedVocab::MistralV3 => {
Some(1)
} PretrainedVocab::WhisperV1 | PretrainedVocab::WhisperV2 | PretrainedVocab::WhisperV3 => {
None
}
}
}
pub fn bos_token_id_by_name(name: &str) -> Option<u32> {
PretrainedVocab::from_name(name).and_then(bos_token_id)
}
pub fn pad_token_id(vocab: PretrainedVocab) -> Option<u32> {
match vocab {
PretrainedVocab::Cl100kBase => Some(100316), PretrainedVocab::O200kBase => Some(200058), PretrainedVocab::Llama3 => Some(128339), PretrainedVocab::DeepseekV3 => Some(2), PretrainedVocab::Qwen3 => Some(QWEN3_BASE_VOCAB_SIZE + 39), PretrainedVocab::Glm4 => Some(GLM4_BASE_VOCAB_SIZE + 39), PretrainedVocab::GptOss => Some(200058), PretrainedVocab::KimiK2 | PretrainedVocab::KimiK3 => Some(163839),
PretrainedVocab::MistralV1 => Some(32039), PretrainedVocab::MistralV2 => Some(32807), PretrainedVocab::MistralV3 => Some(131111), PretrainedVocab::WhisperV1 | PretrainedVocab::WhisperV2 | PretrainedVocab::WhisperV3 => {
None
}
}
}
pub fn base_vocab_size(vocab: PretrainedVocab) -> u32 {
match vocab {
PretrainedVocab::Cl100kBase => CL100K_BASE_BASE_VOCAB_SIZE,
PretrainedVocab::O200kBase => O200K_BASE_BASE_VOCAB_SIZE,
PretrainedVocab::Llama3 => LLAMA3_BASE_VOCAB_SIZE,
PretrainedVocab::DeepseekV3 => DEEPSEEK_V3_BASE_VOCAB_SIZE,
PretrainedVocab::Qwen3 => QWEN3_BASE_VOCAB_SIZE,
PretrainedVocab::Glm4 => GLM4_BASE_VOCAB_SIZE,
PretrainedVocab::GptOss => O200K_BASE_BASE_VOCAB_SIZE,
PretrainedVocab::KimiK2 | PretrainedVocab::KimiK3 => KIMI_BASE_VOCAB_SIZE,
PretrainedVocab::MistralV1 => MISTRAL_V1_BASE_VOCAB_SIZE,
PretrainedVocab::MistralV2 => MISTRAL_V2_BASE_VOCAB_SIZE,
PretrainedVocab::MistralV3 => MISTRAL_V3_BASE_VOCAB_SIZE,
PretrainedVocab::WhisperV1 => WhisperVariant::V1Multilingual.vocab_size() as u32,
PretrainedVocab::WhisperV2 => WhisperVariant::V2Multilingual.vocab_size() as u32,
PretrainedVocab::WhisperV3 => WhisperVariant::V3Multilingual.vocab_size() as u32,
}
}
pub fn base_vocab_size_by_name(name: &str) -> Result<u32, TokenizerError> {
resolve_vocab(name).map(base_vocab_size)
}
fn special_decode_ids(vocab: PretrainedVocab, special: &FxHashMap<String, u32>) -> FxHashSet<u32> {
let all = || special.values().copied().collect::<FxHashSet<u32>>();
match vocab {
PretrainedVocab::Cl100kBase | PretrainedVocab::O200kBase => FxHashSet::default(),
PretrainedVocab::Llama3 => all(),
PretrainedVocab::MistralV1 | PretrainedVocab::MistralV2 => all(),
PretrainedVocab::MistralV3 => all(),
PretrainedVocab::DeepseekV3 => {
let rendered: FxHashSet<u32> = (128800..=128804).chain(128806..=128814).collect();
all().difference(&rendered).copied().collect()
}
PretrainedVocab::Qwen3 => {
let rendered: FxHashSet<u32> = (151657..=151668).collect();
all().difference(&rendered).copied().collect()
}
PretrainedVocab::Glm4 => {
let rendered: FxHashSet<u32> = (151350..=151359).chain(151361..=151364).collect();
all().difference(&rendered).copied().collect()
}
PretrainedVocab::GptOss => all(),
PretrainedVocab::KimiK2 | PretrainedVocab::KimiK3 => all(),
PretrainedVocab::WhisperV1 | PretrainedVocab::WhisperV2 | PretrainedVocab::WhisperV3 => {
let variant = match vocab {
PretrainedVocab::WhisperV2 => WhisperVariant::V2Multilingual,
PretrainedVocab::WhisperV3 => WhisperVariant::V3Multilingual,
_ => WhisperVariant::V1Multilingual,
};
let first_timestamp = variant.first_timestamp_token_id();
special
.values()
.copied()
.filter(|&id| id < first_timestamp)
.collect()
}
}
}
pub fn special_tokens(vocab: PretrainedVocab) -> FxHashMap<String, u32> {
match vocab {
PretrainedVocab::Cl100kBase => cl100k_base_special_tokens(),
PretrainedVocab::O200kBase => o200k_base_special_tokens(),
PretrainedVocab::Llama3 => llama3_special_tokens(),
PretrainedVocab::DeepseekV3 => deepseek_v3_special_tokens(),
PretrainedVocab::Qwen3 => qwen3_special_tokens(),
PretrainedVocab::Glm4 => glm4_special_tokens(),
PretrainedVocab::GptOss => gpt_oss_special_tokens(),
PretrainedVocab::KimiK2 => kimi_k2_special_tokens(),
PretrainedVocab::KimiK3 => kimi_k3_special_tokens(),
PretrainedVocab::MistralV1 => mistral_v1_special_tokens(),
PretrainedVocab::MistralV2 => mistral_v2_special_tokens(),
PretrainedVocab::MistralV3 => mistral_v3_special_tokens(),
PretrainedVocab::WhisperV1 => whisper_special_tokens(WhisperVariant::V1Multilingual),
PretrainedVocab::WhisperV2 => whisper_special_tokens(WhisperVariant::V2Multilingual),
PretrainedVocab::WhisperV3 => whisper_special_tokens(WhisperVariant::V3Multilingual),
}
}
const CL100K_BASE_BASE_VOCAB_SIZE: u32 = 100277;
const O200K_BASE_BASE_VOCAB_SIZE: u32 = 200019;
pub fn cl100k_base_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<|endoftext|>".to_string(), 100257);
special.insert("<|fim_prefix|>".to_string(), 100258);
special.insert("<|fim_middle|>".to_string(), 100259);
special.insert("<|fim_suffix|>".to_string(), 100260);
special.insert(
"<|endofprompt|>".to_string(),
CL100K_BASE_BASE_VOCAB_SIZE - 1,
);
insert_agent_tokens(&mut special, CL100K_BASE_BASE_VOCAB_SIZE);
special
}
pub fn o200k_base_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<|endoftext|>".to_string(), 199999);
special.insert(
"<|endofprompt|>".to_string(),
O200K_BASE_BASE_VOCAB_SIZE - 1,
);
insert_agent_tokens(&mut special, O200K_BASE_BASE_VOCAB_SIZE);
special
}
const LLAMA3_BASE_VOCAB_SIZE: u32 = 128256;
pub fn llama3_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<|begin_of_text|>".to_string(), 128000);
special.insert("<|end_of_text|>".to_string(), 128001);
special.insert("<|reserved_special_token_0|>".to_string(), 128002);
special.insert("<|reserved_special_token_1|>".to_string(), 128003);
special.insert("<|finetune_right_pad_id|>".to_string(), 128004);
special.insert("<|step_id|>".to_string(), 128005);
special.insert("<|start_header_id|>".to_string(), 128006);
special.insert("<|end_header_id|>".to_string(), 128007);
special.insert("<|eom_id|>".to_string(), 128008);
special.insert("<|eot_id|>".to_string(), 128009);
special.insert("<|python_tag|>".to_string(), 128010);
special.insert("<|image|>".to_string(), LLAMA3_BASE_VOCAB_SIZE);
special.insert("<|/image|>".to_string(), LLAMA3_BASE_VOCAB_SIZE + 1);
special.insert("<|audio|>".to_string(), LLAMA3_BASE_VOCAB_SIZE + 2);
special.insert("<|/audio|>".to_string(), LLAMA3_BASE_VOCAB_SIZE + 3);
special.insert("<|video|>".to_string(), LLAMA3_BASE_VOCAB_SIZE + 4);
special.insert("<|/video|>".to_string(), LLAMA3_BASE_VOCAB_SIZE + 5);
insert_agent_tokens_llama3(&mut special, 128300);
special
}
const DEEPSEEK_V3_BASE_VOCAB_SIZE: u32 = 128815;
pub fn deepseek_v3_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<|begin▁of▁sentence|>".to_string(), 0);
special.insert("<|end▁of▁sentence|>".to_string(), 1);
special.insert("<|▁pad▁|>".to_string(), 2);
special.insert("<think>".to_string(), 128798);
special.insert("</think>".to_string(), 128799);
special.insert("<|fim▁hole|>".to_string(), 128800);
special.insert("<|fim▁begin|>".to_string(), 128801);
special.insert("<|fim▁end|>".to_string(), 128802);
special.insert("<|User|>".to_string(), 128803);
special.insert("<|Assistant|>".to_string(), 128804);
special.insert("<|EOT|>".to_string(), 128805);
special.insert("<|tool▁calls▁begin|>".to_string(), 128806);
special.insert("<|tool▁calls▁end|>".to_string(), 128807);
special.insert("<|tool▁call▁begin|>".to_string(), 128808);
special.insert("<|tool▁call▁end|>".to_string(), 128809);
special.insert("<|tool▁outputs▁begin|>".to_string(), 128810);
special.insert("<|tool▁outputs▁end|>".to_string(), 128811);
special.insert("<|tool▁output▁begin|>".to_string(), 128812);
special.insert("<|tool▁output▁end|>".to_string(), 128813);
special.insert(
"<|tool▁sep|>".to_string(),
DEEPSEEK_V3_BASE_VOCAB_SIZE - 1,
);
insert_agent_tokens(&mut special, 128900);
special
}
const QWEN3_BASE_VOCAB_SIZE: u32 = 151669;
pub fn qwen3_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<|endoftext|>".to_string(), 151643);
special.insert("<|im_start|>".to_string(), 151644);
special.insert("<|im_end|>".to_string(), 151645);
special.insert("<|object_ref_start|>".to_string(), 151646);
special.insert("<|object_ref_end|>".to_string(), 151647);
special.insert("<|box_start|>".to_string(), 151648);
special.insert("<|box_end|>".to_string(), 151649);
special.insert("<|quad_start|>".to_string(), 151650);
special.insert("<|quad_end|>".to_string(), 151651);
special.insert("<|vision_start|>".to_string(), 151652);
special.insert("<|vision_end|>".to_string(), 151653);
special.insert("<|vision_pad|>".to_string(), 151654);
special.insert("<|image_pad|>".to_string(), 151655);
special.insert("<|video_pad|>".to_string(), 151656);
special.insert("<tool_call>".to_string(), 151657);
special.insert("</tool_call>".to_string(), 151658);
special.insert("<|fim_prefix|>".to_string(), 151659);
special.insert("<|fim_middle|>".to_string(), 151660);
special.insert("<|fim_suffix|>".to_string(), 151661);
special.insert("<|fim_pad|>".to_string(), 151662);
special.insert("<|repo_name|>".to_string(), 151663);
special.insert("<|file_sep|>".to_string(), 151664);
special.insert("<tool_response>".to_string(), 151665);
special.insert("</tool_response>".to_string(), 151666);
special.insert("<think>".to_string(), 151667);
special.insert("</think>".to_string(), QWEN3_BASE_VOCAB_SIZE - 1);
insert_agent_tokens(&mut special, QWEN3_BASE_VOCAB_SIZE);
special
}
const GLM4_BASE_VOCAB_SIZE: u32 = 151365;
pub fn glm4_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<|endoftext|>".to_string(), 151329);
special.insert("[MASK]".to_string(), 151330);
special.insert("[gMASK]".to_string(), 151331);
special.insert("[sMASK]".to_string(), 151332);
special.insert("<sop>".to_string(), 151333);
special.insert("<eop>".to_string(), 151334);
special.insert("<|system|>".to_string(), 151335);
special.insert("<|user|>".to_string(), 151336);
special.insert("<|assistant|>".to_string(), 151337);
special.insert("<|observation|>".to_string(), 151338);
special.insert("<|begin_of_image|>".to_string(), 151339);
special.insert("<|end_of_image|>".to_string(), 151340);
special.insert("<|begin_of_video|>".to_string(), 151341);
special.insert("<|end_of_video|>".to_string(), 151342);
special.insert("<|begin_of_audio|>".to_string(), 151343);
special.insert("<|end_of_audio|>".to_string(), 151344);
special.insert("<|begin_of_transcription|>".to_string(), 151345);
special.insert("<|end_of_transcription|>".to_string(), 151346);
special.insert("<|code_prefix|>".to_string(), 151347);
special.insert("<|code_middle|>".to_string(), 151348);
special.insert("<|code_suffix|>".to_string(), 151349);
special.insert("<think>".to_string(), 151350);
special.insert("</think>".to_string(), 151351);
special.insert("<tool_call>".to_string(), 151352);
special.insert("</tool_call>".to_string(), 151353);
special.insert("<tool_response>".to_string(), 151354);
special.insert("</tool_response>".to_string(), 151355);
special.insert("<arg_key>".to_string(), 151356);
special.insert("</arg_key>".to_string(), 151357);
special.insert("<arg_value>".to_string(), 151358);
special.insert("</arg_value>".to_string(), 151359);
special.insert("/nothink".to_string(), 151360);
special.insert("<|begin_of_box|>".to_string(), 151361);
special.insert("<|end_of_box|>".to_string(), 151362);
special.insert("<|image|>".to_string(), 151363);
special.insert("<|video|>".to_string(), GLM4_BASE_VOCAB_SIZE - 1);
insert_agent_tokens(&mut special, GLM4_BASE_VOCAB_SIZE);
special
}
pub fn gpt_oss_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<|startoftext|>".to_string(), 199998);
special.insert("<|endoftext|>".to_string(), 199999);
special.insert("<|reserved_200000|>".to_string(), 200000);
special.insert("<|reserved_200001|>".to_string(), 200001);
special.insert("<|return|>".to_string(), 200002);
special.insert("<|constrain|>".to_string(), 200003);
special.insert("<|reserved_200004|>".to_string(), 200004);
special.insert("<|channel|>".to_string(), 200005);
special.insert("<|start|>".to_string(), 200006);
special.insert("<|end|>".to_string(), 200007);
special.insert("<|message|>".to_string(), 200008);
special.insert("<|reserved_200009|>".to_string(), 200009);
special.insert("<|reserved_200010|>".to_string(), 200010);
special.insert("<|reserved_200011|>".to_string(), 200011);
special.insert("<|call|>".to_string(), 200012);
special.insert("<|reserved_200013|>".to_string(), 200013);
special.insert("<|reserved_200014|>".to_string(), 200014);
special.insert("<|reserved_200015|>".to_string(), 200015);
special.insert("<|reserved_200016|>".to_string(), 200016);
special.insert("<|reserved_200017|>".to_string(), 200017);
special.insert(
"<|endofprompt|>".to_string(),
O200K_BASE_BASE_VOCAB_SIZE - 1,
);
insert_agent_tokens(&mut special, O200K_BASE_BASE_VOCAB_SIZE);
special
}
const KIMI_BASE_VOCAB_SIZE: u32 = 163840;
const KIMI_SPECIAL_BASE: u32 = 163584;
fn insert_kimi_specials(special: &mut FxHashMap<String, u32>, named: &[(&str, u32)]) {
for (name, id) in named {
special.insert((*name).to_string(), *id);
}
let named_ids: FxHashSet<u32> = named.iter().map(|(_, id)| *id).collect();
for id in KIMI_SPECIAL_BASE..KIMI_BASE_VOCAB_SIZE {
if named_ids.contains(&id) {
continue;
}
special.insert(format!("<|reserved_token_{id}|>"), id);
}
insert_agent_tokens(special, KIMI_BASE_VOCAB_SIZE);
}
pub fn kimi_k2_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
insert_kimi_specials(
&mut special,
&[
("[BOS]", 163584),
("[EOS]", 163585),
("<|im_end|>", 163586),
("<|im_user|>", 163587),
("<|im_assistant|>", 163588),
("<|start_header_id|>", 163590),
("<|end_header_id|>", 163591),
("[EOT]", 163593),
("<|im_system|>", 163594),
("<|tool_calls_section_begin|>", 163595),
("<|tool_calls_section_end|>", 163596),
("<|tool_call_begin|>", 163597),
("<|tool_call_argument_begin|>", 163598),
("<|tool_call_end|>", 163599),
("<|im_middle|>", 163601),
("<|media_begin|>", 163602),
("<|media_content|>", 163603),
("<|media_end|>", 163604),
("<|media_pad|>", 163605),
("<think>", 163606),
("</think>", 163607),
("[UNK]", 163838),
("[PAD]", 163839),
],
);
special
}
pub fn kimi_k3_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
insert_kimi_specials(
&mut special,
&[
("[BOS]", 163584),
("[EOS]", 163585),
("<|end_of_msg|>", 163586),
("<|open|>", 163587),
("<|close|>", 163588),
("<|sep|>", 163589),
("[start_header_id]", 163590),
("[end_header_id]", 163591),
("[EOT]", 163593),
("<|media_begin|>", 163602),
("<|media_content|>", 163603),
("<|media_end|>", 163604),
("<|media_pad|>", 163605),
("<osagent_mode>", 163649),
("[UNK]", 163838),
("[PAD]", 163839),
],
);
special
}
const MISTRAL_V1_BASE_VOCAB_SIZE: u32 = 32000;
const MISTRAL_V2_BASE_VOCAB_SIZE: u32 = 32768;
const MISTRAL_V3_BASE_VOCAB_SIZE: u32 = 131072;
pub fn mistral_v1_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<unk>".to_string(), 0);
special.insert("<s>".to_string(), 1);
special.insert("</s>".to_string(), 2);
insert_agent_tokens(&mut special, MISTRAL_V1_BASE_VOCAB_SIZE);
special
}
pub fn mistral_v2_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<unk>".to_string(), 0);
special.insert("<s>".to_string(), 1);
special.insert("</s>".to_string(), 2);
special.insert("[INST]".to_string(), 3);
special.insert("[/INST]".to_string(), 4);
special.insert("[TOOL_CALLS]".to_string(), 5);
special.insert("[AVAILABLE_TOOLS]".to_string(), 6);
special.insert("[/AVAILABLE_TOOLS]".to_string(), 7);
special.insert("[TOOL_RESULTS]".to_string(), 8);
special.insert("[/TOOL_RESULTS]".to_string(), 9);
insert_agent_tokens(&mut special, MISTRAL_V2_BASE_VOCAB_SIZE);
special
}
pub fn mistral_v3_special_tokens() -> FxHashMap<String, u32> {
let mut special = FxHashMap::default();
special.insert("<unk>".to_string(), 0);
special.insert("<s>".to_string(), 1);
special.insert("</s>".to_string(), 2);
special.insert("[INST]".to_string(), 3);
special.insert("[/INST]".to_string(), 4);
special.insert("[AVAILABLE_TOOLS]".to_string(), 5);
special.insert("[/AVAILABLE_TOOLS]".to_string(), 6);
special.insert("[TOOL_RESULTS]".to_string(), 7);
special.insert("[/TOOL_RESULTS]".to_string(), 8);
special.insert("[TOOL_CALLS]".to_string(), 9);
insert_agent_tokens(&mut special, MISTRAL_V3_BASE_VOCAB_SIZE);
special
}
const AGENT_TOKENS: [(&str, u32); 54] = [
("<|system|>", 0),
("<|user|>", 1),
("<|assistant|>", 2),
("<|im_start|>", 3),
("<|im_end|>", 4),
("<|think|>", 5),
("<|/think|>", 6),
("<|plan|>", 7),
("<|/plan|>", 8),
("<|step|>", 9),
("<|/step|>", 10),
("<|act|>", 11),
("<|/act|>", 12),
("<|observe|>", 13),
("<|/observe|>", 14),
("<|function|>", 15),
("<|/function|>", 16),
("<|result|>", 17),
("<|/result|>", 18),
("<|error|>", 19),
("<|/error|>", 20),
("<|code|>", 21),
("<|/code|>", 22),
("<|output|>", 23),
("<|/output|>", 24),
("<|lang|>", 25),
("<|/lang|>", 26),
("<|context|>", 27),
("<|/context|>", 28),
("<|quote|>", 29),
("<|/quote|>", 30),
("<|cite|>", 31),
("<|/cite|>", 32),
("<|source|>", 33),
("<|/source|>", 34),
("<|memory|>", 35),
("<|/memory|>", 36),
("<|recall|>", 37),
("<|/recall|>", 38),
("<|pad|>", 39),
("<|stop|>", 40),
("<|sep|>", 41),
("<|image|>", 42),
("<|/image|>", 43),
("<|audio|>", 44),
("<|/audio|>", 45),
("<|video|>", 46),
("<|/video|>", 47),
("<|title|>", 48),
("<|/title|>", 49),
("<|section|>", 50),
("<|/section|>", 51),
("<|summary|>", 52),
("<|/summary|>", 53),
];
fn insert_agent_tokens(special: &mut FxHashMap<String, u32>, base: u32) {
insert_agent_tokens_except(special, base, &[]);
}
fn insert_agent_tokens_llama3(special: &mut FxHashMap<String, u32>, base: u32) {
insert_agent_tokens_except(special, base, &[42, 43, 44, 45, 46, 47]);
}
fn insert_agent_tokens_except(special: &mut FxHashMap<String, u32>, base: u32, skip: &[u32]) {
for (name, offset) in AGENT_TOKENS {
if skip.contains(&offset) {
continue;
}
special.entry(name.to_string()).or_insert(base + offset);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::policy::SpecialDecode;
use crate::core::Tokenize;
fn bpe(tokenizer: AnyTokenizer) -> Tokenizer {
match tokenizer.into_backend() {
Backend::Bpe(t) => t,
_ => panic!("this vocabulary does not load as byte-pair encoding"),
}
}
#[cfg(feature = "vocab-mistral")]
fn spm_piece_id(vocab_data: &[u8], piece: &str) -> u32 {
let (pieces, _) = load_spm_vocab(vocab_data).expect("vocabulary loads");
let id = pieces
.iter()
.position(|p| p == piece)
.unwrap_or_else(|| panic!("{piece:?} is not in the vocabulary"));
id as u32
}
#[test]
fn pretrained_special_decode_ids_follow_the_reference() {
let dropped: [(&str, u32); 8] = [
("mistral_v2", 3), ("mistral_v2", 4), ("llama3", 128000), ("llama3", 128009), ("deepseek_v3", 0), ("deepseek_v3", 128805), ("whisper", 50258), ("whisper", 50259), ];
for (name, id) in dropped {
let tokenizer = from_pretrained(name).expect("bundled vocabulary loads");
assert_eq!(
tokenizer.decode(&[id]).expect("a skipped id decodes"),
"",
"{name}: id {id} must render as nothing, as its reference does"
);
}
let rendered: [(&str, u32, &str); 4] = [
("deepseek_v3", 128803, "<|User|>"),
("deepseek_v3", 128804, "<|Assistant|>"),
("cl100k_base", 100257, "<|endoftext|>"),
("o200k_base", 199999, "<|endoftext|>"),
];
for (name, id, text) in rendered {
let tokenizer = from_pretrained(name).expect("bundled vocabulary loads");
assert_eq!(
tokenizer.decode(&[id]).expect("a rendered id decodes"),
text,
"{name}: id {id} must still render, as its reference does"
);
}
}
#[test]
fn mistral_v3_drops_its_markers_by_family_consistency_not_by_measurement() {
let tokenizer = from_pretrained("mistral_v3").expect("bundled vocabulary loads");
for (id, spelling) in [
(3u32, "[INST]"),
(4, "[/INST]"),
(1, "<s>"),
(131072, "<|system|>"),
] {
assert_eq!(
tokenizer.decode(&[id]).expect("a skipped id decodes"),
"",
"mistral_v3: id {id} ({spelling}) must render as nothing, as V1/V2's do"
);
assert_eq!(
tokenizer
.decode_with(&[id], SpecialDecode::Render)
.expect("a skipped id renders on request"),
spelling,
"mistral_v3: id {id} must still be reachable through Render"
);
}
}
#[test]
fn pretrained_markers_are_still_reachable_with_specials_rendered() {
for (name, id, spelling) in [
("mistral_v2", 3u32, "[INST]"),
("mistral_v2", 4, "[/INST]"),
("llama3", 128000, "<|begin_of_text|>"),
("llama3", 128009, "<|eot_id|>"),
("deepseek_v3", 0, "<|begin▁of▁sentence|>"),
("deepseek_v3", 128805, "<|EOT|>"),
("whisper", 50258, "<|startoftranscript|>"),
] {
let tokenizer = from_pretrained(name).expect("bundled vocabulary loads");
assert_eq!(
tokenizer.decode(&[id]).expect("a skipped id decodes"),
"",
"{name}: id {id} is dropped by default"
);
assert_eq!(
tokenizer
.decode_with(&[id], SpecialDecode::Render)
.expect("a rendered id decodes"),
spelling,
"{name}: id {id} must be reachable with specials rendered"
);
}
}
#[test]
fn rendering_specials_restores_the_marker_and_nothing_else() {
let spm = from_pretrained("mistral_v2").expect("bundled vocabulary loads");
let ids = [3, 7080, 29477, 2294, 4];
assert_eq!(spm.decode(&ids).expect("decodes"), "hello world");
assert_eq!(
spm.decode_with(&ids, SpecialDecode::Render)
.expect("decodes"),
"[INST] hello world[/INST]"
);
let bpe = from_pretrained("llama3").expect("bundled vocabulary loads");
let mut ids = vec![128000];
ids.extend(bpe.encode("hello"));
ids.push(128009);
assert_eq!(bpe.decode(&ids).expect("decodes"), "hello");
assert_eq!(
bpe.decode_with(&ids, SpecialDecode::Render)
.expect("decodes"),
"<|begin_of_text|>hello<|eot_id|>"
);
}
#[test]
fn whisper_timestamp_tokens_are_not_decode_skipped() {
for (name, variant) in [
("whisper_v1", WhisperVariant::V1Multilingual),
("whisper_v2", WhisperVariant::V2Multilingual),
("whisper_v3", WhisperVariant::V3Multilingual),
] {
let tokenizer = from_pretrained(name).expect("bundled vocabulary loads");
let first = variant.first_timestamp_token_id();
assert_eq!(
tokenizer.decode(&[first]).expect("a timestamp id decodes"),
"<|0.00|>",
"{name}: the first timestamp token must still render"
);
assert_eq!(
tokenizer
.decode(&[first + 1500])
.expect("a timestamp id decodes"),
"<|30.00|>",
"{name}: the last timestamp token must still render"
);
assert_eq!(
tokenizer
.decode(&[variant.notimestamps_token_id()])
.expect("a control id decodes"),
"",
"{name}: `<|notimestamps|>` is a control token and is dropped"
);
}
}
#[test]
fn test_from_pretrained_llama3() {
let tokenizer = from_pretrained("llama3").unwrap();
assert!(tokenizer.vocab_size() > 100000);
}
#[test]
fn test_from_pretrained_cl100k() {
let tokenizer = from_pretrained("cl100k_base").unwrap();
assert!(tokenizer.vocab_size() > 90000);
}
#[test]
fn test_from_pretrained_whisper_variants() {
for (name, variant) in [
("whisper_v1", WhisperVariant::V1Multilingual),
("whisper", WhisperVariant::V2Multilingual), ("whisper_v2", WhisperVariant::V2Multilingual),
("whisper-v3", WhisperVariant::V3Multilingual),
] {
let tok = from_pretrained(name).unwrap_or_else(|e| panic!("{name}: {e}"));
assert_eq!(tok.vocab_size(), variant.vocab_size(), "{name} vocab_size");
assert_eq!(bpe(tok).encoder().len(), 50257, "{name} base vocab size");
}
}
#[test]
fn test_policy_is_passthrough_but_knows_its_specials() {
let tok = from_pretrained("llama3").unwrap();
assert_eq!(tok.eos_token_id(), Some(128001));
assert!(tok.is_eos(128001));
assert_eq!(tok.special_token_id("<|eot_id|>"), Some(128009));
let text = "Hello, world!";
assert_eq!(tok.encode(text), tok.encode_raw(text));
}
#[test]
fn test_encode_batch_matches_individual() {
let tok = from_pretrained("llama3").unwrap();
let texts = ["Hello, world!", "", "<|eot_id|>after", "你好世界"];
let batch = tok.encode_batch(&texts);
assert_eq!(batch.len(), texts.len());
for (got, text) in batch.iter().zip(texts) {
assert_eq!(got, &tok.encode(text), "batch mismatch for {text:?}");
}
assert!(batch[2].starts_with(&[128009]));
}
#[test]
fn test_whisper_special_tokens_wired() {
let tok = from_pretrained("whisper_v3").unwrap();
assert_eq!(tok.encode("<|en|>"), vec![50259]);
assert_eq!(
tok.encode("<|transcribe|>"),
vec![WhisperVariant::V3Multilingual.transcribe_token_id()]
);
assert_eq!(tok.encode("<|yue|>"), vec![50259 + 99]);
}
#[test]
fn test_whisper_roundtrip() {
let tok = from_pretrained("whisper").unwrap();
let text = "Hello, world! 123 héllo";
assert_eq!(tok.decode(&tok.encode(text)).unwrap(), text);
}
#[test]
fn test_whisper_name_mapping() {
assert_eq!(
PretrainedVocab::from_name("whisper"),
Some(PretrainedVocab::WhisperV2)
);
assert_eq!(
PretrainedVocab::from_name("whisper-large-v3"),
Some(PretrainedVocab::WhisperV3)
);
assert_eq!(PretrainedVocab::from_name("whisper.en"), None);
}
#[test]
fn test_eos_token_ids() {
assert_eq!(eos_token_id(PretrainedVocab::Cl100kBase), 100257);
assert_eq!(eos_token_id(PretrainedVocab::O200kBase), 199999);
assert_eq!(eos_token_id(PretrainedVocab::Llama3), 128001);
assert_eq!(eos_token_id(PretrainedVocab::DeepseekV3), 1);
assert_eq!(eos_token_id(PretrainedVocab::MistralV1), 2);
}
#[test]
fn test_vocab_from_name() {
assert_eq!(
PretrainedVocab::from_name("llama3"),
Some(PretrainedVocab::Llama3)
);
assert_eq!(
PretrainedVocab::from_name("llama3.1"),
Some(PretrainedVocab::Llama3)
);
assert_eq!(
PretrainedVocab::from_name("deepseek_v3"),
Some(PretrainedVocab::DeepseekV3)
);
assert_eq!(
PretrainedVocab::from_name("mistral"),
Some(PretrainedVocab::MistralV1)
);
assert_eq!(PretrainedVocab::from_name("unknown"), None);
}
#[test]
fn test_from_pretrained_mistral() {
let tokenizer = from_pretrained("mistral").unwrap();
assert!(tokenizer.vocab_size() >= 31000);
}
#[test]
fn test_mistral_encode_decode() {
let tokenizer = from_pretrained("mistral").unwrap();
let text = "Hello, world!";
let tokens = tokenizer.encode(text);
assert!(!tokens.is_empty());
let decoded = tokenizer.decode(&tokens).unwrap();
assert_eq!(decoded, text, "Encoding should be reversible");
}
#[test]
#[cfg(feature = "vocab-mistral")]
fn test_mistral_never_shatters_the_word_boundary_marker() {
for (name, data) in [
("mistral", MISTRAL_SPM_VOCAB),
("mistral_v2", MISTRAL_V2_SPM_VOCAB),
] {
let shattered = [
spm_piece_id(data, "<0xE2>"),
spm_piece_id(data, "<0x96>"),
spm_piece_id(data, "<0x81>"),
];
let tokenizer = from_pretrained(name).unwrap();
let ids = tokenizer.encode("the sourdough starter rose overnight");
assert!(
!ids.windows(3).any(|w| w == shattered.as_slice()),
"{name}: word boundary shattered into byte tokens {shattered:?} in {ids:?}"
);
}
}
#[test]
#[cfg(feature = "vocab-mistral")]
fn test_mistral_reaches_whole_word_pieces() {
let the = spm_piece_id(MISTRAL_SPM_VOCAB, "▁the");
let sour = spm_piece_id(MISTRAL_SPM_VOCAB, "▁sour");
let ids = from_pretrained("mistral").unwrap().encode("the sourdough");
assert!(ids.contains(&the), "▁the ({the}) missing from {ids:?}");
assert!(ids.contains(&sour), "▁sour ({sour}) missing from {ids:?}");
}
#[test]
fn test_mistral_round_trips_a_sentence() {
let tokenizer = from_pretrained("mistral").unwrap();
let text = "The quick brown fox jumps over the lazy dog.";
let decoded = tokenizer.decode(&tokenizer.encode(text)).unwrap();
assert_eq!(decoded, text);
}
#[test]
fn test_base_vocab_size_matches_reference() {
assert_eq!(base_vocab_size(PretrainedVocab::Cl100kBase), 100277); assert_eq!(base_vocab_size(PretrainedVocab::O200kBase), 200019); assert_eq!(base_vocab_size(PretrainedVocab::Llama3), 128256); assert_eq!(base_vocab_size(PretrainedVocab::DeepseekV3), 128815); assert_eq!(base_vocab_size(PretrainedVocab::MistralV1), 32000); assert_eq!(base_vocab_size(PretrainedVocab::MistralV2), 32768); assert_eq!(base_vocab_size(PretrainedVocab::MistralV3), 131072); assert_eq!(
base_vocab_size(PretrainedVocab::WhisperV1),
WhisperVariant::V1Multilingual.vocab_size() as u32
);
assert_eq!(
base_vocab_size(PretrainedVocab::WhisperV2),
WhisperVariant::V2Multilingual.vocab_size() as u32
);
assert_eq!(
base_vocab_size(PretrainedVocab::WhisperV3),
WhisperVariant::V3Multilingual.vocab_size() as u32
);
}
#[test]
fn test_base_vocab_size_never_exceeds_extended_vocab_size() {
for (name, vocab) in [
("cl100k_base", PretrainedVocab::Cl100kBase),
("o200k_base", PretrainedVocab::O200kBase),
("llama3", PretrainedVocab::Llama3),
("deepseek_v3", PretrainedVocab::DeepseekV3),
("mistral_v1", PretrainedVocab::MistralV1),
("mistral_v2", PretrainedVocab::MistralV2),
("mistral_v3", PretrainedVocab::MistralV3),
("whisper_v1", PretrainedVocab::WhisperV1),
("whisper_v2", PretrainedVocab::WhisperV2),
("whisper_v3", PretrainedVocab::WhisperV3),
] {
let extended = from_vocab(vocab).unwrap().vocab_size() as u32;
let base = base_vocab_size(vocab);
assert!(
base <= extended,
"{name}: base_vocab_size {base} exceeds extended vocab_size {extended}"
);
}
}
#[test]
fn test_no_agent_token_id_below_base_vocab_size() {
for (name, vocab) in [
("cl100k_base", PretrainedVocab::Cl100kBase),
("o200k_base", PretrainedVocab::O200kBase),
("llama3", PretrainedVocab::Llama3),
("deepseek_v3", PretrainedVocab::DeepseekV3),
("mistral_v1", PretrainedVocab::MistralV1),
("mistral_v2", PretrainedVocab::MistralV2),
("mistral_v3", PretrainedVocab::MistralV3),
] {
let base = base_vocab_size(vocab);
for name_and_id in agent_token_ids_in(vocab) {
let (token, id) = name_and_id;
assert!(
id >= base,
"{name}: agent token {token:?} has id {id}, below base_vocab_size {base}"
);
}
}
}
const AGENT_TOKEN_NAMES: [&str; 54] = [
"<|system|>",
"<|user|>",
"<|assistant|>",
"<|im_start|>",
"<|im_end|>",
"<|think|>",
"<|/think|>",
"<|plan|>",
"<|/plan|>",
"<|step|>",
"<|/step|>",
"<|act|>",
"<|/act|>",
"<|observe|>",
"<|/observe|>",
"<|function|>",
"<|/function|>",
"<|result|>",
"<|/result|>",
"<|error|>",
"<|/error|>",
"<|code|>",
"<|/code|>",
"<|output|>",
"<|/output|>",
"<|lang|>",
"<|/lang|>",
"<|context|>",
"<|/context|>",
"<|quote|>",
"<|/quote|>",
"<|cite|>",
"<|/cite|>",
"<|source|>",
"<|/source|>",
"<|memory|>",
"<|/memory|>",
"<|recall|>",
"<|/recall|>",
"<|pad|>",
"<|stop|>",
"<|sep|>",
"<|image|>",
"<|/image|>",
"<|audio|>",
"<|/audio|>",
"<|video|>",
"<|/video|>",
"<|title|>",
"<|/title|>",
"<|section|>",
"<|/section|>",
"<|summary|>",
"<|/summary|>",
];
fn agent_token_ids_in(vocab: PretrainedVocab) -> Vec<(String, u32)> {
let all = special_tokens(vocab);
AGENT_TOKEN_NAMES
.iter()
.filter_map(|name| all.get(*name).map(|&id| (name.to_string(), id)))
.collect()
}
}