use std::path::{Path, PathBuf};
use tokenizers::models::bpe::{BpeTrainerBuilder, BPE};
use tokenizers::models::TrainerWrapper;
use tokenizers::pre_tokenizers::byte_level::ByteLevel;
use tokenizers::{AddedToken, Result, Tokenizer};
fn collect_rs(dir: &Path, out: &mut Vec<String>) {
if let Ok(rd) = std::fs::read_dir(dir) {
for e in rd.flatten() {
let p = e.path();
if p.is_dir() {
collect_rs(&p, out);
} else if p.extension().and_then(|x| x.to_str()) == Some("rs") {
out.push(p.to_string_lossy().to_string());
}
}
}
}
fn main() -> Result<()> {
let repo = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("crate parent")
.to_path_buf();
let vocab: usize = std::env::var("AXIOM_BPE_VOCAB")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(16000);
let out = std::env::var("AXIOM_BPE_OUT").unwrap_or_else(|_| {
repo.join("checkpoints/axiom_bpe.json")
.to_string_lossy()
.to_string()
});
if let Some(parent) = Path::new(&out).parent() {
let _ = std::fs::create_dir_all(parent);
}
let corpus_dir = std::env::var("AXIOM_CORPUS_OUT")
.unwrap_or_else(|_| repo.join("checkpoints/corpus").to_string_lossy().into());
let mut files = Vec::new();
if std::path::Path::new(&corpus_dir).exists() {
for e in std::fs::read_dir(&corpus_dir).into_iter().flatten().flatten() {
let p = e.path();
if p.extension().and_then(|x| x.to_str()) == Some("txt") {
files.push(p.to_string_lossy().to_string());
}
}
}
if files.is_empty() {
collect_rs(&repo.join("axiom_engine_rs/src"), &mut files);
collect_rs(&repo.join("tests"), &mut files);
}
eprintln!("[bpe] corpus: {} file(s); target vocab={vocab}", files.len());
if files.is_empty() {
return Err("no .rs corpus files found".into());
}
let mut tokenizer = Tokenizer::new(BPE::default());
tokenizer.with_pre_tokenizer(ByteLevel::new(false, true, true));
tokenizer.with_decoder(ByteLevel::new(false, true, true));
let bpe_trainer = BpeTrainerBuilder::new()
.vocab_size(vocab)
.min_frequency(2)
.initial_alphabet(ByteLevel::alphabet())
.special_tokens(vec![
AddedToken::from("<unk>", true),
AddedToken::from("<pad>", true),
AddedToken::from("<eos>", true),
])
.build();
let mut trainer: TrainerWrapper = bpe_trainer.into();
tokenizer.train_from_files(&mut trainer, files)?;
let real_vocab = tokenizer.get_vocab_size(true);
tokenizer.save(&out, true)?;
eprintln!("[bpe] DONE: trained vocab_size={real_vocab}; saved -> {out}");
println!("{real_vocab}");
Ok(())
}