use ahash::AHashMap;
use anyhow::{anyhow, bail, Result};
use mlx_native::gguf::{GgufFile, MetadataValue};
use tokenizers::decoders::byte_level::ByteLevel as ByteLevelDec;
use tokenizers::models::bpe::BPE;
use tokenizers::pre_tokenizers::byte_level::ByteLevel;
use tokenizers::pre_tokenizers::sequence::Sequence as PreSeq;
use tokenizers::pre_tokenizers::split::Split;
use tokenizers::pre_tokenizers::PreTokenizerWrapper;
use tokenizers::{AddedToken, SplitDelimiterBehavior, Tokenizer};
pub const QWEN35_PRE_REGEX: &str = "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+";
#[allow(dead_code)] mod token_type {
pub const UNDEFINED: i32 = 0;
pub const NORMAL: i32 = 1;
pub const UNKNOWN: i32 = 2;
pub const CONTROL: i32 = 3;
pub const USER_DEFINED: i32 = 4;
pub const UNUSED: i32 = 5;
pub const BYTE: i32 = 6;
}
pub fn build_tokenizer_from_gguf(gguf: &GgufFile) -> Result<Tokenizer> {
let pre = gguf
.metadata_string("tokenizer.ggml.pre")
.ok_or_else(|| anyhow!("GGUF missing `tokenizer.ggml.pre`"))?;
if pre != "qwen35" {
bail!(
"tokenizer.ggml.pre = {pre:?} is not supported by qwen35::tokenizer; \
this module only handles `qwen35` (see llama-vocab.cpp:381-387). \
Other pre-types need their own regex builder."
);
}
let tokens = read_string_array(gguf, "tokenizer.ggml.tokens")?;
if tokens.is_empty() {
bail!("`tokenizer.ggml.tokens` is empty");
}
let tokens: Vec<String> = tokens
.into_iter()
.enumerate()
.map(|(i, t)| {
if t.is_empty() {
format!("[EMPTY_{i}]")
} else {
t
}
})
.collect();
let vocab: AHashMap<String, u32> = tokens
.iter()
.enumerate()
.map(|(i, t)| (t.clone(), i as u32))
.collect();
if vocab.len() != tokens.len() {
bail!(
"duplicate tokens in `tokenizer.ggml.tokens` ({} unique vs {} entries) \
— GGUF is malformed",
vocab.len(),
tokens.len()
);
}
let merges_raw = read_string_array(gguf, "tokenizer.ggml.merges")?;
let merges: Vec<(String, String)> = merges_raw
.iter()
.enumerate()
.map(|(i, m)| {
let mut split = m.splitn(2, ' ');
let a = split
.next()
.ok_or_else(|| anyhow!("merge[{i}] = {m:?} has no space separator"))?;
let b = split
.next()
.ok_or_else(|| anyhow!("merge[{i}] = {m:?} has only one half (need 'a b')"))?;
Ok::<_, anyhow::Error>((a.to_string(), b.to_string()))
})
.collect::<Result<_>>()?;
let bpe = BPE::builder()
.vocab_and_merges(vocab, merges)
.build()
.map_err(|e| anyhow!("BPE::builder().build(): {e}"))?;
let split = Split::new(
tokenizers::pre_tokenizers::split::SplitPattern::Regex(QWEN35_PRE_REGEX.to_string()),
SplitDelimiterBehavior::Isolated,
false,
)
.map_err(|e| anyhow!("Split::new: {e}"))?;
let byte_level_pre = ByteLevel::new(
false, false,
false,
);
let pre_seq = PreSeq::new(vec![
PreTokenizerWrapper::Split(split),
PreTokenizerWrapper::ByteLevel(byte_level_pre),
]);
let decoder = ByteLevelDec::new(
false, false,
false,
);
let mut tok = Tokenizer::new(bpe);
tok.with_pre_tokenizer(Some(pre_seq));
tok.with_decoder(Some(decoder));
let token_types = read_i32_array(gguf, "tokenizer.ggml.token_type").ok();
if let Some(types) = &token_types {
if types.len() != tokens.len() {
bail!(
"tokenizer.ggml.token_type length {} != tokens length {}",
types.len(),
tokens.len()
);
}
let specials: Vec<AddedToken> = types
.iter()
.zip(tokens.iter())
.enumerate()
.filter_map(|(_id, (ttype, name))| {
let is_special = matches!(*ttype, token_type::CONTROL | token_type::USER_DEFINED);
if !is_special {
return None;
}
Some(AddedToken::from(name.clone(), true))
})
.collect();
tok.add_special_tokens(&specials);
} else {
let mut specials: Vec<AddedToken> = Vec::new();
for key in [
"tokenizer.ggml.bos_token_id",
"tokenizer.ggml.eos_token_id",
"tokenizer.ggml.padding_token_id",
"tokenizer.ggml.unknown_token_id",
"tokenizer.ggml.eot_token_id",
"tokenizer.ggml.eom_token_id",
] {
if let Some(id) = gguf.metadata_u32(key) {
if let Some(name) = tokens.get(id as usize) {
specials.push(AddedToken::from(name.clone(), true));
}
}
}
tok.add_special_tokens(&specials);
}
Ok(tok)
}
fn read_string_array(gguf: &GgufFile, key: &str) -> Result<Vec<String>> {
let v = gguf
.metadata(key)
.ok_or_else(|| anyhow!("GGUF missing `{key}`"))?;
let arr = match v {
MetadataValue::Array(a) => a,
other => bail!("`{key}` is not an array (got {other:?})"),
};
arr.iter()
.enumerate()
.map(|(i, e)| match e {
MetadataValue::String(s) => Ok(s.clone()),
other => Err(anyhow!("`{key}`[{i}] is not a string (got {other:?})")),
})
.collect()
}
fn read_i32_array(gguf: &GgufFile, key: &str) -> Result<Vec<i32>> {
let v = gguf
.metadata(key)
.ok_or_else(|| anyhow!("GGUF missing `{key}`"))?;
let arr = match v {
MetadataValue::Array(a) => a,
other => bail!("`{key}` is not an array (got {other:?})"),
};
arr.iter()
.enumerate()
.map(|(i, e)| match e {
MetadataValue::Int32(x) => Ok(*x),
MetadataValue::Uint32(x) => Ok(*x as i32),
MetadataValue::Int8(x) => Ok(*x as i32),
MetadataValue::Uint8(x) => Ok(*x as i32),
MetadataValue::Int16(x) => Ok(*x as i32),
MetadataValue::Uint16(x) => Ok(*x as i32),
other => Err(anyhow!("`{key}`[{i}] is not an integer (got {other:?})")),
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
#[test]
fn pre_regex_matches_llama_cpp_spec() {
let expected = "(?:'[sS]|'[tT]|'[rR][eE]|'[vV][eE]|'[mM]|'[lL][lL]|'[dD])|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+";
assert_eq!(QWEN35_PRE_REGEX, expected);
}
#[test]
fn apex_im_start_does_not_tokenize_to_oob_id() {
let path = Path::new(
"/opt/hf2q/models/qwen3.6-35b-a3b-abliterix-ega-abliterated-apex/\
APEX-Q5_K_M.gguf",
);
if !path.exists() {
eprintln!("apex GGUF not present at {path:?}; skipping");
return;
}
let gguf = GgufFile::open(path).expect("open apex GGUF");
let tok = build_tokenizer_from_gguf(&gguf).expect("build tokenizer");
let physical_vocab = read_string_array(&gguf, "tokenizer.ggml.tokens")
.expect("tokens array")
.len() as u32;
let prompt = "<|im_start|>user\nHow to make bread?<|im_end|>\n<|im_start|>assistant\n";
let enc = tok
.encode(prompt, false)
.expect("encode chat-templated prompt");
for &id in enc.get_ids() {
assert!(
id < physical_vocab,
"token id {id} >= physical vocab {physical_vocab} \
— pre-tokenizer let an OOB special through (regression \
of the iter61a/iter40 vocab-mismatch class)"
);
}
}
#[test]
fn build_succeeds_on_qwen35_gguf() {
let path = Path::new(
"/opt/hf2q/models/qwen3.6-35b-a3b-abliterix-ega-abliterated-apex/\
APEX-Q5_K_M.gguf",
);
if !path.exists() {
eprintln!("apex GGUF not present; skipping");
return;
}
let gguf = GgufFile::open(path).expect("open");
let _tok = build_tokenizer_from_gguf(&gguf).expect("build");
}
}