pub fn count_tokens(text: &str) -> usize {
#[cfg(feature = "real-tokenizer")]
{
backend().count(text)
}
#[cfg(not(feature = "real-tokenizer"))]
{
heuristic_count_tokens(text)
}
}
#[cfg(feature = "real-tokenizer")]
enum Backend {
Hermes(Box<tokenizers::Tokenizer>),
O200k(tiktoken_rs::CoreBPE),
}
#[cfg(feature = "real-tokenizer")]
impl Backend {
fn count(&self, text: &str) -> usize {
match self {
Backend::Hermes(tok) => match tok.encode(text, false) {
Ok(enc) => enc.len(),
Err(_) => heuristic_count_tokens(text),
},
Backend::O200k(bpe) => bpe.encode_ordinary(text).len(),
}
}
}
#[cfg(feature = "real-tokenizer")]
fn backend() -> &'static Backend {
use std::sync::OnceLock;
static BACKEND: OnceLock<Backend> = OnceLock::new();
BACKEND.get_or_init(|| {
if let Some(path) = std::env::var_os("CLAWFT_HERMES_TOKENIZER") {
match tokenizers::Tokenizer::from_file(&path) {
Ok(tok) => {
tracing::info!(
path = ?path,
"tokenizer: using exact Hermes/Seed-OSS tokenizer.json for budgeting"
);
return Backend::Hermes(Box::new(tok));
}
Err(err) => {
tracing::warn!(
path = ?path,
error = %err,
"tokenizer: CLAWFT_HERMES_TOKENIZER failed to load; \
falling back to bundled o200k_base estimator"
);
}
}
}
Backend::O200k(tiktoken_rs::o200k_base().expect("bundled o200k_base vocab must load"))
})
}
pub(crate) fn heuristic_count_tokens(text: &str) -> usize {
if text.is_empty() {
return 0;
}
let chars = text.chars().count();
let by_chars = chars.div_ceil(4);
let words = text.split_whitespace().count();
let by_words = (words * 4).div_ceil(3);
by_chars.max(by_words).max(1)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_is_zero() {
assert_eq!(count_tokens(""), 0);
assert_eq!(heuristic_count_tokens(""), 0);
}
#[test]
fn nonempty_is_positive() {
assert!(count_tokens("hello world") >= 1);
assert!(heuristic_count_tokens("hello world") >= 1);
}
#[test]
fn heuristic_handles_code_without_undercount() {
let code = "fn main(){let x=vec![1,2,3];println!(\"{:?}\",x);}";
let words = code.split_whitespace().count(); let old_whitespace_estimate = (words * 4).div_ceil(3);
let est = heuristic_count_tokens(code);
assert!(
est > old_whitespace_estimate * 2,
"code estimate {est} should dwarf whitespace estimate {old_whitespace_estimate}"
);
}
#[cfg(feature = "real-tokenizer")]
#[test]
fn real_tokenizer_within_tolerance_on_fixtures() {
if std::env::var_os("CLAWFT_HERMES_TOKENIZER").is_some() {
return;
}
let fixtures: &[(&str, usize)] = &[
("The quick brown fox jumps over the lazy dog.", 10),
(
"Rust is a systems programming language focused on safety and performance.",
12,
),
(
"fn main() {\n let x = vec![1, 2, 3];\n println!(\"{:?}\", x);\n}",
26,
),
(
"ExoChain is the single source of truth; the agent gets a query/graft layer.",
19,
),
];
for (text, reference) in fixtures {
let actual = count_tokens(text);
let tol = (*reference as f64 * 0.05).ceil() as usize;
let lo = reference.saturating_sub(tol.max(1));
let hi = reference + tol.max(1);
assert!(
actual >= lo && actual <= hi,
"token count {actual} for {text:?} outside 5% of reference {reference} (allowed {lo}..={hi})"
);
}
}
}