use std::sync::{Mutex, OnceLock, PoisonError};
use super::*;
use tokenizers::{Model, ModelWrapper, PaddingDirection, PaddingParams, PaddingStrategy};
fn table_cell(mt: &Tokenizer) -> &'static OnceLock<Option<MergeTable>> {
type Cell = &'static OnceLock<Option<MergeTable>>;
static NON_BPE: OnceLock<Option<MergeTable>> = OnceLock::new();
static CELLS: Mutex<Vec<(usize, Cell)>> = Mutex::new(Vec::new());
let ModelWrapper::BPE(bpe) = mt.get_model() else {
return &NON_BPE;
};
let key = bpe.get_vocab_size();
let mut cells = CELLS.lock().unwrap_or_else(PoisonError::into_inner);
if let Some((_, cell)) = cells.iter().find(|(k, _)| *k == key) {
return cell;
}
let cell: Cell = Box::leak(Box::new(OnceLock::new()));
cells.push((key, cell));
cell
}
fn merge_table() -> &'static MergeTable {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
table_cell(&mt)
.get_or_init(|| MergeTable::from_tokenizer(&mt))
.as_ref()
.expect("the artifact's BPE is mirrorable")
}
fn chunk_long(
mt: &Tokenizer,
text: &str,
opts: &WindowOptions,
) -> Result<Vec<windit::split::Chunk>> {
super::chunk_long(mt, LazyTable::new(table_cell(mt), mt), text, opts)
}
use super::test_artifact::{
GOLDEN_SOURCE_TOKENIZER_SHA256, tokenizer_bytes as artifact_tokenizer_bytes,
tokenizer_path as artifact_tokenizer_path_for_tests,
};
#[test]
fn options_default_equals_new() {
assert_eq!(TextEmbedderOptions::default(), TextEmbedderOptions::new());
assert_eq!(TextEmbedderOptions::new().compute(), DEFAULT_COMPUTE);
assert_eq!(DEFAULT_COMPUTE, ComputeUnits::All);
}
#[test]
fn options_with_and_set_compute() {
let opts = TextEmbedderOptions::new().with_compute(ComputeUnits::CpuAndNeuralEngine);
assert_eq!(opts.compute(), ComputeUnits::CpuAndNeuralEngine);
let mut opts = TextEmbedderOptions::new();
opts.set_compute(ComputeUnits::CpuOnly);
assert_eq!(opts.compute(), ComputeUnits::CpuOnly);
}
#[test]
fn describe_renders_shape_and_dtype() {
assert_eq!(describe(&[1, 512], Some(DataType::I32)), "[1, 512] int32");
assert_eq!(describe(&[1, 384], None), "[1, 384] none");
}
#[cfg(feature = "serde")]
#[test]
fn options_serde_roundtrip() {
let opts = TextEmbedderOptions::new().with_compute(ComputeUnits::CpuAndNeuralEngine);
let json = serde_json::to_string(&opts).unwrap();
assert!(json.contains("cpu_and_neural_engine"), "serialized: {json}");
let back: TextEmbedderOptions = serde_json::from_str(&json).unwrap();
assert_eq!(back, opts);
}
#[cfg(feature = "serde")]
#[test]
fn options_serde_missing_compute_defaults() {
let opts: TextEmbedderOptions = serde_json::from_str("{}").unwrap();
assert_eq!(opts.compute(), DEFAULT_COMPUTE);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn artifact_tokenizer_sha_matches_golden_source_pin() {
use sha2::{Digest, Sha256};
let sha: String = Sha256::digest(artifact_tokenizer_bytes())
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
sha, GOLDEN_SOURCE_TOKENIZER_SHA256,
"the staged tokenizer.json diverged from the granite tokenizer that cut the goldens"
);
}
#[test]
fn tokenizer_sha_pin_matches_golden_source_literal() {
assert_eq!(
contract::TOKENIZER_SHA256_HEX,
GOLDEN_SOURCE_TOKENIZER_SHA256,
"the tokenizer-identity contract const must equal the pinned golden-source SHA"
);
}
fn ids(text: &str) -> Vec<u32> {
let tok =
configured_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("configure tokenizer");
tok.encode(text, true).expect("encode").get_ids().to_vec()
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn token_ids_match_pinned_golden_subset() {
let cases: &[(&str, &[u32])] = &[
("hello world", &[179934, 24313, 2318, 179938]),
(
"how do I build a Rust CoreML inference library for text embeddings?",
&[
179934, 8775, 579, 317, 2966, 221, 54305, 15984, 4051, 86068, 11087, 355, 2145, 158816, 30,
179938,
],
),
];
for (text, expected) in cases {
let got = ids(text);
assert_eq!(&got, expected, "token-id drift for {text:?}");
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn long_input_truncation_keeps_the_right_directional_prefix() {
let long: String = (1..=1000)
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(" ");
let truncated = ids(&long);
assert_eq!(
truncated.len(),
MAX_TOKENS,
"truncation must cap ids at the window"
);
assert_eq!(truncated[0], 179934, "leading <|startoftext|> kept");
assert_eq!(
truncated[MAX_TOKENS - 1],
179938,
"trailing <|return|> kept"
);
let full = tokenizers::Tokenizer::from_bytes(artifact_tokenizer_bytes())
.expect("load tokenizer")
.encode(long.as_str(), true)
.expect("encode")
.get_ids()
.to_vec();
assert!(
full.len() > MAX_TOKENS,
"reference must actually overflow the window (got {})",
full.len()
);
assert_eq!(
&truncated[1..MAX_TOKENS - 1],
&full[1..MAX_TOKENS - 1],
"Right-truncation interior must equal the untruncated first-510 content tokens"
);
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
for id in &truncated {
hasher.update(id.to_le_bytes());
}
let sha: String = hasher
.finalize()
.iter()
.map(|b| format!("{b:02x}"))
.collect();
assert_eq!(
sha, "aec64c84fc8328d01b518a7cb4e63b42a00a659ba5d39789fc10a272667416af",
"truncated 512-id sequence drifted (tokenizer artifact or truncation config changed)"
);
}
const HELLO_WORLD_IDS: [u32; 4] = contract::SENTINEL_IDS;
fn artifact_tokenizer_with_padding(direction: PaddingDirection) -> Tokenizer {
let mut tok =
Tokenizer::from_bytes(artifact_tokenizer_bytes()).expect("load the artifact tokenizer");
tok.with_padding(Some(PaddingParams {
strategy: PaddingStrategy::Fixed(MAX_TOKENS),
direction,
..Default::default()
}));
tok
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn configured_tokenizer_disables_fixed_right_padding_mask_stays_correct() {
let mut tok = artifact_tokenizer_with_padding(PaddingDirection::Right);
let padded = tok
.encode("hello world", true)
.expect("encode")
.get_ids()
.to_vec();
assert_eq!(
padded.len(),
MAX_TOKENS,
"adversarial fixture must actually pad to the window"
);
configure_tokenizer(&mut tok).expect("configure");
let real = tok
.encode("hello world", true)
.expect("encode")
.get_ids()
.to_vec();
assert_eq!(
real, HELLO_WORLD_IDS,
"padding must be stripped, real ids only"
);
let (input_ids, mask) = build_window(&real, 0).expect("build window");
assert_eq!(
mask.iter().sum::<i32>(),
i32::try_from(real.len()).unwrap(),
"attention mask must count only the real tokens"
);
assert!(
mask[..real.len()].iter().all(|&m| m == 1),
"real tokens masked 1"
);
assert!(
mask[real.len()..].iter().all(|&m| m == 0),
"pad positions masked 0"
);
assert_eq!(input_ids[0], 179934, "CLS at position 0");
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn configured_tokenizer_disables_left_padding_keeps_cls_at_zero() {
let mut tok = artifact_tokenizer_with_padding(PaddingDirection::Left);
let padded = tok
.encode("hello world", true)
.expect("encode")
.get_ids()
.to_vec();
assert_eq!(padded.len(), MAX_TOKENS);
assert_ne!(
padded[0], 179934,
"left padding must push CLS off position 0 (the hazard being defended)"
);
configure_tokenizer(&mut tok).expect("configure");
let real = tok
.encode("hello world", true)
.expect("encode")
.get_ids()
.to_vec();
assert_eq!(real, HELLO_WORLD_IDS);
assert_eq!(
real[0], 179934,
"CLS must be at position 0 after the override"
);
let (input_ids, _mask) = build_window(&real, 0).expect("build window");
assert_eq!(
input_ids[0], 179934,
"CLS stays at position 0 in the window"
);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn overlong_input_truncates_and_fills_the_window_without_panic() {
let long: String = (1..=1000)
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(" ");
let real = ids(&long); assert_eq!(
real.len(),
MAX_TOKENS,
"over-long input truncates to the window"
);
let (input_ids, mask) = build_window(&real, 0).expect("full window must build, not panic");
assert!(
mask.iter().all(|&m| m == 1),
"a full window is entirely real tokens"
);
assert_eq!(input_ids[0], 179934, "CLS stays at position 0");
}
#[test]
fn build_window_rejects_overlong_ids_with_typed_error() {
let overlong = vec![7u32; MAX_TOKENS + 1];
match build_window(&overlong, 0) {
Err(Error::TokenCount(count)) => {
assert_eq!(count.got(), MAX_TOKENS + 1);
assert_eq!(count.max(), MAX_TOKENS);
}
other => panic!("expected Err(TokenCount), got {other:?}"),
}
}
#[test]
fn build_window_rejects_out_of_range_token_id() {
match build_window(&[u32::MAX], 0) {
Err(Error::TokenIdRange(id)) => assert_eq!(id, u32::MAX),
other => panic!("expected Err(TokenIdRange), got {other:?}"),
}
}
#[test]
fn build_window_masks_prefix_and_right_pads_remainder() {
let (input_ids, mask) = build_window(&[10, 20, 30], 7).expect("build");
assert_eq!(&input_ids[..3], &[10i32, 20, 30]);
assert!(
input_ids[3..].iter().all(|&x| x == 7),
"remainder is pad_id"
);
assert_eq!(&mask[..3], &[1i32, 1, 1]);
assert!(mask[3..].iter().all(|&m| m == 0), "pad positions masked 0");
}
#[test]
fn build_window_accepts_a_full_window() {
let (_input_ids, mask) = build_window(&vec![1u32; MAX_TOKENS], 0).expect("full window builds");
assert_eq!(mask.iter().sum::<i32>(), i32::try_from(MAX_TOKENS).unwrap());
}
fn long_doc() -> String {
(0..24)
.map(|p| {
(0..40)
.map(|w| format!("para{p}word{w}"))
.collect::<Vec<_>>()
.join(" ")
})
.collect::<Vec<_>>()
.join("\n\n")
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn measuring_tokenizer_reports_untruncated_counts() {
let long: String = (1..=1000)
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(" ");
let configured = configured_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("configure");
let measuring = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let configured_count = configured
.encode(long.as_str(), true)
.expect("encode")
.get_ids()
.len();
let measuring_count = measuring
.encode(long.as_str(), true)
.expect("encode")
.get_ids()
.len();
assert_eq!(
configured_count, MAX_TOKENS,
"the production tokenizer saturates a long input at the window"
);
assert!(
measuring_count > MAX_TOKENS,
"the measuring tokenizer must see the true (untruncated) count, got {measuring_count}"
);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn long_text_chunks_multi_window_within_budget() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let doc = long_doc();
let chunks = chunk_long(&mt, &doc, &WindowOptions::new(MAX_TOKENS)).expect("chunk");
assert!(
chunks.len() > 1,
"a document over several windows must split into multiple chunks, got {}",
chunks.len()
);
assert_eq!(chunks[0].start(), 0, "the first chunk starts at byte 0");
let mut prev_end = 0usize;
for chunk in &chunks {
let s = chunk
.as_str(&doc)
.expect("chunk falls on a char boundary of its own text");
let count = mt.encode(s, true).expect("encode").get_ids().len();
assert!(
count <= MAX_TOKENS,
"every chunk stays within the token budget, got {count}"
);
assert_eq!(
chunk.start(),
prev_end,
"each chunk begins where the previous ended (no gap, no overlap)"
);
prev_end = chunk.end();
}
assert_eq!(prev_end, doc.len(), "the last chunk ends at doc.len()");
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn boundary_separators_stay_in_the_token_stream() {
const PARAGRAPH_SEPARATOR_TOKEN: u32 = 239;
assert_eq!(
ids("\n\n"),
vec![179934, PARAGRAPH_SEPARATOR_TOKEN, 179938],
"the paragraph separator's token id is pinned"
);
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let doc = long_doc();
let chunks = chunk_long(&mt, &doc, &WindowOptions::new(MAX_TOKENS)).expect("chunk");
let concat: String = chunks
.iter()
.map(|c| {
c.as_str(&doc)
.expect("chunk falls on a char boundary of its own text")
})
.collect();
assert_eq!(
concat, doc,
"the chunks must concatenate back to the document byte-for-byte"
);
let separators: usize = chunks
.iter()
.map(|c| {
let s = c.as_str(&doc).expect("char boundary");
mt.encode(s, true)
.expect("encode")
.get_ids()
.iter()
.filter(|&&id| id == PARAGRAPH_SEPARATOR_TOKEN)
.count()
})
.sum();
assert_eq!(
separators,
doc.matches("\n\n").count(),
"every `\\n\\n` is tokenized exactly once across the chunks"
);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn word_fallback_punctuation_is_reattached() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let sentence = (0..400)
.map(|w| format!("term{w}"))
.collect::<Vec<_>>()
.join(", ");
let chunks = chunk_long(&mt, &sentence, &WindowOptions::new(128)).expect("chunk");
assert!(
chunks.len() > 1,
"a 400-term sentence must split into multiple chunks, got {}",
chunks.len()
);
assert_eq!(chunks[0].start(), 0, "the first chunk starts at byte 0");
let mut prev_end = 0usize;
for chunk in &chunks {
let s = chunk.as_str(&sentence).expect("char boundary");
assert_eq!(
chunk.start(),
prev_end,
"each chunk begins where the previous ended"
);
assert!(
mt.encode(s, true).expect("encode").get_ids().len() <= 128,
"every chunk stays within the 128-token budget"
);
prev_end = chunk.end();
}
assert_eq!(
prev_end,
sentence.len(),
"the last chunk ends at the text length"
);
let concat: String = chunks
.iter()
.map(|c| c.as_str(&sentence).expect("char boundary"))
.collect();
assert_eq!(
concat, sentence,
"the chunks reproduce the sentence byte-for-byte"
);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn leading_and_trailing_separators_are_covered() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let doc = format!("\n\n{}\n\n", long_doc());
let chunks = chunk_long(&mt, &doc, &WindowOptions::new(MAX_TOKENS)).expect("chunk");
assert!(
chunks.len() > 1,
"the wrapped document still splits, got {}",
chunks.len()
);
assert_eq!(
chunks[0].start(),
0,
"the first chunk starts at 0 despite the leading separator"
);
let mut prev_end = 0usize;
for chunk in &chunks {
assert_eq!(
chunk.start(),
prev_end,
"each chunk begins where the previous ended"
);
prev_end = chunk.end();
}
assert_eq!(
prev_end,
doc.len(),
"the last chunk ends at len despite the trailing separator"
);
let concat: String = chunks
.iter()
.map(|c| c.as_str(&doc).expect("char boundary"))
.collect();
assert_eq!(concat, doc, "the chunks reproduce the wrapped document");
}
#[test]
fn gap_attachment_falls_back_right_then_own_chunk() {
use windit::split::ContentAware;
let measure = |s: &str| -> usize { s.chars().count() };
let repair = |text: &str, window: usize| -> Vec<(usize, usize)> {
let measure_checked = |a: usize, b: usize| -> Result<usize> { Ok(text[a..b].chars().count()) };
let chunks = ContentAware::new(&measure)
.chunk(text, &WindowOptions::new(window))
.expect("chunk");
attach_gaps(text, chunks, &measure_checked, window)
.expect("own-chunks measure within MAX_TOKENS")
.iter()
.map(|c| (c.start(), c.end()))
.collect()
};
let cases: &[(&str, &[(usize, usize)])] = &[
("aaaaa\n\nbbb", &[(0, 5), (5, 10)]),
("aaaaa\n\nbbbbb", &[(0, 5), (5, 7), (7, 12)]),
("\n\naaaaa", &[(0, 2), (2, 7)]),
("aaaaa\n\n", &[(0, 5), (5, 7)]),
];
for &(text, expected) in cases {
let got = repair(text, 5);
assert_eq!(got.as_slice(), expected, "repaired ranges for {text:?}");
assert_eq!(got.first().unwrap().0, 0, "{text:?}: first start 0");
assert_eq!(
got.last().unwrap().1,
text.len(),
"{text:?}: last end == text length"
);
for w in got.windows(2) {
assert_eq!(w[0].1, w[1].0, "{text:?}: adjacent chunks tile");
}
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn gap_repair_cannot_exceed_max_windows() {
use windit::WinditError;
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
match chunk_long(&mt, "a\n\nb", &WindowOptions::new(3).with_max_windows(2)) {
Err(Error::Windowing(WinditError::TooManyWindows { got, max })) => {
assert_eq!(got, 3, "the full repaired chunk count is reported");
assert_eq!(max, 2);
}
other => panic!("expected Err(Windowing(TooManyWindows)), got {other:?}"),
}
let uncapped = chunk_long(&mt, "a\n\nb", &WindowOptions::new(3)).expect("uncapped");
let ranges: Vec<_> = uncapped.iter().map(|c| (c.start(), c.end())).collect();
assert_eq!(ranges, vec![(0, 1), (1, 3), (3, 4)]);
match chunk_long(
&mt,
"\n\na\n\nb\n\n",
&WindowOptions::new(3).with_max_windows(3),
) {
Err(Error::Windowing(WinditError::TooManyWindows { got, max })) => {
assert_eq!(
got, 5,
"leading + interior + trailing insertions all counted"
);
assert_eq!(max, 3);
}
other => panic!("expected Err(Windowing(TooManyWindows)), got {other:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn whitespace_only_text_counts_one_window_against_the_cap() {
use windit::WinditError;
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
match chunk_long(
&mt,
" ",
&WindowOptions::new(MAX_TOKENS).with_max_windows(0),
) {
Err(Error::Windowing(WinditError::TooManyWindows { got, max })) => {
assert_eq!(got, 1, "the whole-input fallback counts as one window");
assert_eq!(max, 0);
}
other => panic!("expected Err(Windowing(TooManyWindows)), got {other:?}"),
}
let capped = chunk_long(
&mt,
" ",
&WindowOptions::new(MAX_TOKENS).with_max_windows(1),
)
.expect("cap 1 admits the whole-input fallback");
assert_eq!(
capped
.iter()
.map(|c| (c.start(), c.end()))
.collect::<Vec<_>>(),
vec![(0, 3)],
"one chunk spanning the whole input"
);
let uncapped =
chunk_long(&mt, " ", &WindowOptions::new(MAX_TOKENS)).expect("uncapped whitespace");
assert_eq!(
uncapped
.iter()
.map(|c| (c.start(), c.end()))
.collect::<Vec<_>>(),
vec![(0, 3)],
"the fallback chunk is synthesized regardless of any cap"
);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn contentless_over_budget_input_is_refused_not_truncated() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let fixtures = [
" ".repeat(100_000),
"\t".repeat(100_000),
"\r\n".repeat(50_000),
"\u{00A0}".repeat(100_000),
" \t\r\n\u{00A0}\u{2003}\u{2009}".repeat(15_000),
];
for s in &fixtures {
let expected_tokens = mt.encode(s.as_str(), true).expect("encode").get_ids().len();
assert!(
expected_tokens > MAX_TOKENS,
"fixture must actually exceed the window (got {expected_tokens})"
);
match chunk_long(&mt, s, &WindowOptions::new(MAX_TOKENS)) {
Err(Error::ContentlessInputOverBudget(over)) => {
assert_eq!(over.start(), 0, "the whole input is the offending run");
assert_eq!(over.end(), s.len());
assert_eq!(
over.tokens(),
expected_tokens,
"reported count is the untruncated measure"
);
assert_eq!(over.max(), MAX_TOKENS);
}
other => panic!("expected ContentlessInputOverBudget, got {other:?}"),
}
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn contentless_input_at_or_under_budget_still_embeds_whole() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let measure = |n: usize| {
mt.encode(" ".repeat(n).as_str(), true)
.expect("encode")
.get_ids()
.len()
};
let mut lo = 1usize;
let mut hi = 100_000usize;
assert!(measure(lo) <= MAX_TOKENS, "one space fits");
assert!(measure(hi) > MAX_TOKENS, "100k spaces overflow");
while lo + 1 < hi {
let mid = (lo + hi) / 2;
if measure(mid) <= MAX_TOKENS {
lo = mid;
} else {
hi = mid;
}
}
let at_budget = " ".repeat(lo);
let chunks = chunk_long(&mt, &at_budget, &WindowOptions::new(MAX_TOKENS))
.expect("in-budget contentless input embeds whole");
assert_eq!(
chunks
.iter()
.map(|c| (c.start(), c.end()))
.collect::<Vec<_>>(),
vec![(0, at_budget.len())],
"in-budget contentless input is one whole-input chunk"
);
let over = " ".repeat(hi);
match chunk_long(&mt, &over, &WindowOptions::new(MAX_TOKENS)) {
Err(Error::ContentlessInputOverBudget(_)) => {}
other => panic!("expected ContentlessInputOverBudget just past the budget, got {other:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn separator_gap_over_budget_is_refused() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let text = format!("a{}b", " ".repeat(100_000));
match chunk_long(&mt, &text, &WindowOptions::new(3)) {
Err(Error::ContentlessInputOverBudget(over)) => {
assert_eq!(over.start(), 1, "the gap starts right after `a`");
assert_eq!(over.end(), 100_001, "the gap ends right before `b`");
assert!(
over.tokens() > MAX_TOKENS,
"the gap run measures over the window"
);
assert_eq!(over.max(), MAX_TOKENS);
}
other => panic!("expected ContentlessInputOverBudget, got {other:?}"),
}
let ok = chunk_long(&mt, "a\n\nb", &WindowOptions::new(3)).expect("in-budget own-chunk escape");
assert_eq!(
ok.iter().map(|c| (c.start(), c.end())).collect::<Vec<_>>(),
vec![(0, 1), (1, 3), (3, 4)]
);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn leading_and_trailing_over_budget_gaps_are_refused() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let leading = format!("{}a", " ".repeat(100_000));
match chunk_long(&mt, &leading, &WindowOptions::new(MAX_TOKENS)) {
Err(Error::ContentlessInputOverBudget(over)) => {
assert_eq!(over.start(), 0, "leading gap starts at byte 0");
assert_eq!(over.end(), 100_000, "leading gap ends right before `a`");
}
other => panic!("expected leading ContentlessInputOverBudget, got {other:?}"),
}
let trailing = format!("a{}", " ".repeat(100_000));
match chunk_long(&mt, &trailing, &WindowOptions::new(MAX_TOKENS)) {
Err(Error::ContentlessInputOverBudget(over)) => {
assert_eq!(over.start(), 1, "trailing gap starts right after `a`");
assert_eq!(over.end(), 100_001, "trailing gap ends at text length");
}
other => panic!("expected trailing ContentlessInputOverBudget, got {other:?}"),
}
}
#[test]
fn tokenizer_failure_on_fallback_measure_keeps_tokenize_identity() {
const TINY_NO_UNK: &[u8] = br#"{"version":"1.0","truncation":null,"padding":null,"added_tokens":[],"normalizer":null,"pre_tokenizer":null,"post_processor":null,"decoder":null,"model":{"type":"WordLevel","vocab":{"hello":0,"world":1},"unk_token":"<unk>"}}"#;
let mt = tokenizers::Tokenizer::from_bytes(TINY_NO_UNK).expect("load tiny WordLevel");
match chunk_long(&mt, " ", &WindowOptions::new(MAX_TOKENS)) {
Err(Error::Tokenize(_)) => {}
other => panic!("expected Err(Tokenize), got {other:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn empty_text_chunks_to_nothing_under_any_cap() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let chunks = chunk_long(&mt, "", &WindowOptions::new(MAX_TOKENS).with_max_windows(0))
.expect("empty text chunks to nothing under a cap of 0");
assert!(chunks.is_empty());
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn single_window_text_is_one_whole_chunk() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let text = "a compact sentence that fits comfortably inside one window";
let chunks = chunk_long(&mt, text, &WindowOptions::new(MAX_TOKENS)).expect("chunk");
assert_eq!(chunks.len(), 1, "short text is one chunk");
assert_eq!(chunks[0].start(), 0);
assert_eq!(chunks[0].end(), text.len());
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn chunk_geometry_adapts_by_window_options_alone() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let doc = long_doc();
let coarse = chunk_long(&mt, &doc, &WindowOptions::new(128)).expect("chunk @128");
let fine = chunk_long(&mt, &doc, &WindowOptions::new(64)).expect("chunk @64");
assert!(
fine.len() > coarse.len(),
"a smaller window yields more chunks: {} @64 vs {} @128",
fine.len(),
coarse.len()
);
for chunk in &coarse {
let s = chunk.as_str(&doc).expect("char boundary");
assert!(mt.encode(s, true).expect("encode").get_ids().len() <= 128);
}
for chunk in &fine {
let s = chunk.as_str(&doc).expect("char boundary");
assert!(mt.encode(s, true).expect("encode").get_ids().len() <= 64);
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn overlap_repeats_trailing_tokens_within_budget() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let doc = long_doc();
let opts = WindowOptions::new(128).with_overlap(16);
let chunks = chunk_long(&mt, &doc, &opts).expect("chunk");
assert!(chunks.len() > 1, "an overlapped long doc still splits");
for pair in chunks.windows(2) {
assert!(
pair[1].start() < pair[0].end(),
"consecutive chunks overlap: next start {} vs prev end {}",
pair[1].start(),
pair[0].end()
);
let repeated = &doc[pair[1].start()..pair[0].end()];
let n = mt.encode(repeated, true).expect("encode").get_ids().len();
assert!(
n <= 16,
"repeated region within the 16-token overlap budget, got {n}"
);
}
}
#[test]
fn window_over_budget_is_rejected() {
match validate_long_input(
"any text",
&LongTextOptions::from(WindowOptions::new(MAX_TOKENS + 1)),
) {
Err(Error::WindowOverBudget(budget)) => {
assert_eq!(budget.window(), MAX_TOKENS + 1);
assert_eq!(budget.max(), MAX_TOKENS);
}
other => panic!("expected Err(WindowOverBudget), got {other:?}"),
}
assert!(
validate_long_input(
"any text",
&LongTextOptions::from(WindowOptions::new(MAX_TOKENS)),
)
.is_ok()
);
}
#[test]
fn long_text_options_default_equals_new() {
assert_eq!(LongTextOptions::default(), LongTextOptions::new());
assert_eq!(
LongTextOptions::new().window_options(),
WindowOptions::new(MAX_TOKENS)
);
assert_eq!(LongTextOptions::new().max_input_bytes(), None);
let built = LongTextOptions::new()
.with_window_options(WindowOptions::new(64))
.with_max_input_bytes(4096);
assert_eq!(built.window_options(), WindowOptions::new(64));
assert_eq!(built.max_input_bytes(), Some(4096));
let mut set = LongTextOptions::new();
set.set_window_options(WindowOptions::new(32));
set.set_max_input_bytes(2048);
assert_eq!(set.window_options(), WindowOptions::new(32));
assert_eq!(set.max_input_bytes(), Some(2048));
let from = LongTextOptions::from(WindowOptions::new(64));
assert_eq!(from.window_options().window(), 64);
assert_eq!(from.max_input_bytes(), None);
}
#[test]
fn long_text_options_tail_policy_is_carried_through_the_geometry() {
assert_eq!(LongTextOptions::new().tail_policy(), TailPolicy::default());
assert_eq!(
LongTextOptions::new().tail_policy(),
TailPolicy::KeepWithCoverage
);
let built = LongTextOptions::new()
.with_window_options(WindowOptions::new(64).with_hop(32).with_max_windows(7))
.with_tail_policy(TailPolicy::DropBelowMin(8))
.with_max_input_bytes(4096);
assert_eq!(built.tail_policy(), TailPolicy::DropBelowMin(8));
assert_eq!(built.window_options().window(), 64);
assert_eq!(built.window_options().hop(), 32);
assert_eq!(built.window_options().max_windows(), Some(7));
assert_eq!(built.max_input_bytes(), Some(4096));
assert_eq!(
built.window_options(),
WindowOptions::new(64)
.with_hop(32)
.with_max_windows(7)
.with_tail(TailPolicy::DropBelowMin(8))
);
let mut set = LongTextOptions::new();
set.set_tail_policy(TailPolicy::PadFull);
assert_eq!(set.tail_policy(), TailPolicy::PadFull);
assert_eq!(set.window_options().window(), MAX_TOKENS);
}
#[test]
fn long_text_options_display_pins_the_composed_spelling() {
assert_eq!(
LongTextOptions::new().to_string(),
"window_options=(window=512,hop=512,tail=keep_with_coverage,max_windows=none),\
max_input_bytes=none"
);
let built = LongTextOptions::new()
.with_window_options(WindowOptions::new(64).with_hop(32).with_max_windows(7))
.with_tail_policy(TailPolicy::DropBelowMin(8))
.with_max_input_bytes(4096);
assert_eq!(
built.to_string(),
"window_options=(window=64,hop=32,tail=drop_below_min(8),max_windows=7),\
max_input_bytes=4096"
);
let mut pad_full = LongTextOptions::new();
pad_full.set_tail_policy(TailPolicy::PadFull);
assert_eq!(
pad_full.to_string(),
"window_options=(window=512,hop=512,tail=pad_full,max_windows=none),\
max_input_bytes=none"
);
}
#[cfg(feature = "serde")]
#[test]
fn long_text_options_document_form_is_pinned() {
let doc = concat!(
r#"{"window_options":{"window":512,"hop":512,"#,
r#""tail":{"kind":"keep_with_coverage"},"max_windows":null},"#,
r#""max_input_bytes":null}"#
);
let opts = LongTextOptions::new();
assert_eq!(serde_json::to_string(&opts).unwrap(), doc);
assert_eq!(serde_json::from_str::<LongTextOptions>(doc).unwrap(), opts);
let doc = concat!(
r#"{"window_options":{"window":256,"hop":192,"#,
r#""tail":{"kind":"drop_below_min","value":8},"max_windows":32},"#,
r#""max_input_bytes":4096}"#
);
let opts = LongTextOptions::new()
.with_window_options(
WindowOptions::new(256)
.with_hop(192)
.with_max_windows(32)
.with_tail(TailPolicy::DropBelowMin(8)),
)
.with_max_input_bytes(4096);
assert_eq!(serde_json::to_string(&opts).unwrap(), doc);
assert_eq!(serde_json::from_str::<LongTextOptions>(doc).unwrap(), opts);
assert_eq!(
serde_json::from_str::<LongTextOptions>("{}").unwrap(),
LongTextOptions::new()
);
assert_eq!(
serde_json::from_str::<LongTextOptions>(r#"{"max_input_bytes":16}"#).unwrap(),
LongTextOptions::new().with_max_input_bytes(16)
);
}
#[cfg(feature = "serde")]
#[test]
fn long_text_options_round_trip_through_postcard() {
for policy in [
TailPolicy::KeepWithCoverage,
TailPolicy::PadFull,
TailPolicy::DropBelowMin(0),
TailPolicy::DropBelowMin(8),
] {
let expected: &[u8] = match policy {
TailPolicy::KeepWithCoverage => &[0],
TailPolicy::DropBelowMin(0) => &[1, 0],
TailPolicy::DropBelowMin(_) => &[1, 8],
TailPolicy::PadFull => &[2],
};
let bytes = postcard::to_allocvec(&policy).unwrap();
assert_eq!(bytes, expected, "compact spelling of {policy:?} moved");
assert_eq!(
postcard::from_bytes::<TailPolicy>(&bytes).unwrap(),
policy,
"postcard round-trip lost {policy:?} (bytes {bytes:?})"
);
let opts = LongTextOptions::new()
.with_window_options(WindowOptions::new(256).with_hop(192).with_max_windows(32))
.with_tail_policy(policy)
.with_max_input_bytes(4096);
let bytes = postcard::to_allocvec(&opts).unwrap();
assert_eq!(
postcard::from_bytes::<LongTextOptions>(&bytes).unwrap(),
opts,
"postcard round-trip lost {opts:?} (bytes {bytes:?})"
);
}
let opts = LongTextOptions::new();
let bytes = postcard::to_allocvec(&opts).unwrap();
assert_eq!(
postcard::from_bytes::<LongTextOptions>(&bytes).unwrap(),
opts
);
}
#[cfg(feature = "serde")]
#[test]
fn a_misspelled_key_is_refused_rather_than_silently_unbounded() {
let err = serde_json::from_str::<LongTextOptions>(r#"{"max_input_byte":4096}"#).unwrap_err();
let text = err.to_string();
assert!(
text.contains("max_input_byte"),
"the refusal must name the unknown key, got {text:?}"
);
assert!(
serde_json::from_str::<LongTextOptions>(r#"{"max_input_bytes":4096,"windowoptions":{}}"#)
.is_err()
);
assert!(toml::from_str::<LongTextOptions>("max_input_byte = 4096\n").is_err());
}
#[test]
fn input_too_large_is_rejected_before_any_tokenizer_work() {
let big = "x".repeat(8 * 1024 * 1024);
let opts = LongTextOptions::new().with_max_input_bytes(1024 * 1024);
match validate_long_input(&big, &opts) {
Err(Error::InputTooLarge(large)) => {
assert_eq!(large.got(), big.len());
assert_eq!(large.max(), 1024 * 1024);
}
other => panic!("expected InputTooLarge, got {other:?}"),
}
let at_limit = "x".repeat(1024 * 1024);
assert!(validate_long_input(&at_limit, &opts).is_ok());
assert!(validate_long_input(&big, &LongTextOptions::new()).is_ok());
}
#[test]
fn input_too_large_takes_precedence_over_window_budget() {
let big = "x".repeat(2 * 1024 * 1024);
let opts =
LongTextOptions::from(WindowOptions::new(MAX_TOKENS + 1)).with_max_input_bytes(1024 * 1024);
match validate_long_input(&big, &opts) {
Err(Error::InputTooLarge(_)) => {}
other => panic!("expected InputTooLarge to win over WindowOverBudget, got {other:?}"),
}
}
const OVERHEAD_TINY_TOKENIZER: &str = r#"{"version":"1.0","truncation":null,"padding":null,"added_tokens":[],"normalizer":null,"pre_tokenizer":{"type":"Whitespace"},"post_processor":null,"decoder":null,"model":{"type":"WordLevel","vocab":{"<pad>":0,"a":1,"b":2},"unk_token":"<pad>"}}"#;
fn tokenizer_bytes_with_special_overhead(added: usize) -> Vec<u8> {
use tokenizers::processors::template::{SpecialToken, TemplateProcessing};
let mut tokenizer =
Tokenizer::from_bytes(OVERHEAD_TINY_TOKENIZER.as_bytes()).expect("load the tiny tokenizer");
let special = SpecialToken::new(
"<sp>".to_string(),
vec![0u32; added],
vec!["<pad>".to_string(); added],
)
.expect("ids and tokens are the same length");
let template = TemplateProcessing::builder()
.try_single("<sp> $A")
.expect("single template")
.try_pair("<sp> $A $B")
.expect("pair template")
.special_tokens(vec![special])
.build()
.expect("build the template post-processor");
tokenizer.with_post_processor(Some(template));
tokenizer
.to_string(false)
.expect("serialize the tokenizer")
.into_bytes()
}
#[test]
fn overhead_fixture_installs_the_claimed_special_token_count() {
use tokenizers::PostProcessor;
for added in [1usize, MAX_TOKENS - 1, MAX_TOKENS, MAX_TOKENS + 1] {
let bytes = tokenizer_bytes_with_special_overhead(added);
let tok = Tokenizer::from_bytes(&bytes).expect("reload the fixture");
let post = tok.get_post_processor().expect("the fixture has one");
assert_eq!(post.added_tokens(false), added, "single-sequence overhead");
}
}
#[test]
fn configure_tokenizer_refuses_overhead_over_the_window() {
let bytes = tokenizer_bytes_with_special_overhead(MAX_TOKENS + 1);
match configured_tokenizer_from_bytes(&bytes) {
Err(Error::SpecialTokenOverhead(overhead)) => {
assert_eq!(overhead.added(), MAX_TOKENS + 1);
assert_eq!(overhead.window(), MAX_TOKENS);
}
other => panic!("expected SpecialTokenOverhead, got {other:?}"),
}
}
#[test]
fn configure_tokenizer_refuses_overhead_equal_to_the_window() {
let bytes = tokenizer_bytes_with_special_overhead(MAX_TOKENS);
match configured_tokenizer_from_bytes(&bytes) {
Err(Error::SpecialTokenOverhead(overhead)) => {
assert_eq!(overhead.added(), MAX_TOKENS);
assert_eq!(overhead.window(), MAX_TOKENS);
}
other => panic!("expected SpecialTokenOverhead, got {other:?}"),
}
}
#[test]
fn configure_tokenizer_accepts_overhead_below_the_window() {
let bytes = tokenizer_bytes_with_special_overhead(MAX_TOKENS - 1);
let tok = configured_tokenizer_from_bytes(&bytes).expect("511 specials fit a 512-token window");
let ids = tok
.encode("a b a b", true)
.expect("encode")
.get_ids()
.to_vec();
assert_eq!(ids.len(), MAX_TOKENS, "specials plus one real token");
assert_eq!(ids[MAX_TOKENS - 1], 1, "the real token survives (`a`)");
assert!(
matches!(
validate_tokenizer_contract(&tok),
Err(Error::TokenizerContractMismatch(_))
),
"the overhead guard does not stand in for the contract check"
);
}
#[test]
fn configure_tokenizer_accepts_a_tokenizer_without_a_post_processor() {
configured_tokenizer_from_bytes(OVERHEAD_TINY_TOKENIZER.as_bytes())
.expect("no post-processor is zero overhead");
}
fn mutated_artifact_tokenizer_bytes(mutate: impl FnOnce(&mut serde_json::Value)) -> Vec<u8> {
let mut value: serde_json::Value =
serde_json::from_slice(artifact_tokenizer_bytes()).expect("parse the artifact tokenizer.json");
mutate(&mut value);
serde_json::to_vec(&value).expect("re-serialize mutated tokenizer.json")
}
fn mutated_artifact_tokenizer(mutate: impl FnOnce(&mut serde_json::Value)) -> Tokenizer {
configured_tokenizer_from_bytes(&mutated_artifact_tokenizer_bytes(mutate))
.expect("configure mutated tokenizer")
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn tokenizer_contract_accepts_the_artifact_tokenizer() {
let tok =
configured_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("configure the artifact");
validate_tokenizer_contract(&tok).expect("the artifact tokenizer must satisfy the contract");
}
#[test]
fn tokenizer_contract_rejects_missing_specials() {
const TINY: &[u8] = br#"{"version":"1.0","truncation":null,"padding":null,"added_tokens":[],"normalizer":null,"pre_tokenizer":null,"post_processor":null,"decoder":null,"model":{"type":"WordLevel","vocab":{"hello":0,"world":1},"unk_token":"<unk>"}}"#;
let tok = configured_tokenizer_from_bytes(TINY).expect("configure tiny");
match validate_tokenizer_contract(&tok) {
Err(Error::TokenizerContractMismatch(mismatch)) => {
let check = mismatch.check();
assert!(
check.contains("<|startoftext|>"),
"check names the missing special: {check}"
);
assert_eq!(mismatch.actual(), "missing");
}
other => panic!("expected TokenizerContractMismatch, got {other:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn tokenizer_contract_rejects_wrong_vocab_size() {
let tok = mutated_artifact_tokenizer(|value| {
let added = value["added_tokens"]
.as_array_mut()
.expect("added_tokens array");
added.pop().expect("added_tokens is non-empty");
});
match validate_tokenizer_contract(&tok) {
Err(Error::TokenizerContractMismatch(mismatch)) => {
let (expected, actual) = (mismatch.expected(), mismatch.actual());
assert_eq!(mismatch.check(), "vocab size");
assert!(
expected.contains("180000"),
"expected names the contract size: {expected}"
);
assert!(
actual.contains("179999"),
"actual names the reduced size: {actual}"
);
}
other => panic!("expected TokenizerContractMismatch on vocab size, got {other:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn tokenizer_contract_rejects_out_of_model_vocab_id() {
let tok = mutated_artifact_tokenizer(|value| {
let vocab = value["model"]["vocab"]
.as_object_mut()
.expect("model.vocab object");
let key = vocab
.iter()
.max_by_key(|(_, id)| id.as_u64().unwrap_or(0))
.map(|(k, _)| k.clone())
.expect("non-empty base vocab");
vocab.insert(key, serde_json::json!(180_000));
});
match validate_tokenizer_contract(&tok) {
Err(Error::TokenizerContractMismatch(mismatch)) => {
let actual = mismatch.actual();
assert_eq!(mismatch.check(), "max token id");
assert!(
actual.contains("180000"),
"actual carries the offending id: {actual}"
);
}
other => panic!("expected TokenizerContractMismatch on max token id, got {other:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn tokenizer_contract_rejects_divergent_encoding() {
let tok = mutated_artifact_tokenizer(|value| {
let vocab = value["model"]["vocab"]
.as_object_mut()
.expect("model.vocab object");
let mut key_a = None;
let mut key_b = None;
for (key, id) in vocab.iter() {
match id.as_u64() {
Some(24_313) => key_a = Some(key.clone()),
Some(2_318) => key_b = Some(key.clone()),
_ => {}
}
}
let key_a = key_a.expect("id 24313 present in base vocab");
let key_b = key_b.expect("id 2318 present in base vocab");
vocab.insert(key_a, serde_json::json!(2_318));
vocab.insert(key_b, serde_json::json!(24_313));
});
match validate_tokenizer_contract(&tok) {
Err(Error::TokenizerContractMismatch(mismatch)) => {
assert_eq!(mismatch.check(), "sentinel encoding");
}
other => panic!("expected TokenizerContractMismatch on sentinel encoding, got {other:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn tokenizer_identity_rejects_non_sentinel_vocab_corruption() {
const SWAP_A: u64 = 5_000;
const SWAP_B: u64 = 6_000;
const RESERVED: [u64; 5] = [24_313, 2_318, 179_934, 179_935, 179_938];
assert!(
!RESERVED.contains(&SWAP_A) && !RESERVED.contains(&SWAP_B),
"swap ids must avoid the specials and sentinel content ids"
);
let bytes = mutated_artifact_tokenizer_bytes(|value| {
let vocab = value["model"]["vocab"]
.as_object_mut()
.expect("model.vocab object");
let mut key_a = None;
let mut key_b = None;
for (key, id) in vocab.iter() {
match id.as_u64() {
Some(SWAP_A) => key_a = Some(key.clone()),
Some(SWAP_B) => key_b = Some(key.clone()),
_ => {}
}
}
let key_a = key_a.expect("id 5000 present in base vocab");
let key_b = key_b.expect("id 6000 present in base vocab");
vocab.insert(key_a, serde_json::json!(SWAP_B));
vocab.insert(key_b, serde_json::json!(SWAP_A));
});
let configured = configured_tokenizer_from_bytes(&bytes).expect("configure mutated");
validate_tokenizer_contract(&configured)
.expect("behavioral contract accepts non-sentinel vocab corruption");
assert_ne!(
bytes.as_slice(),
artifact_tokenizer_bytes(),
"the swap must actually change the bytes"
);
let actual = sha256_hex(&bytes);
match validate_tokenizer_identity(&TokenizerProvenance::Supplied(actual.clone())) {
Err(Error::TokenizerContractMismatch(mismatch)) => {
assert_eq!(mismatch.check(), "tokenizer identity (sha-256)");
assert_eq!(mismatch.expected(), contract::TOKENIZER_SHA256_HEX);
assert_eq!(mismatch.actual(), actual);
}
other => panic!("expected identity TokenizerContractMismatch, got {other:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn tokenizer_identity_rejects_reserialized_artifact_json() {
let bytes = mutated_artifact_tokenizer_bytes(|_| {});
let configured = configured_tokenizer_from_bytes(&bytes).expect("configure round-trip");
validate_tokenizer_contract(&configured).expect("a re-serialized bundle is behaviorally valid");
assert_ne!(
bytes.as_slice(),
artifact_tokenizer_bytes(),
"a serde_json round-trip must actually differ from the pinned bytes"
);
let actual = sha256_hex(&bytes);
match validate_tokenizer_identity(&TokenizerProvenance::Supplied(actual.clone())) {
Err(Error::TokenizerContractMismatch(mismatch)) => {
assert_eq!(mismatch.check(), "tokenizer identity (sha-256)");
assert_eq!(mismatch.actual(), actual);
}
other => panic!("expected identity TokenizerContractMismatch, got {other:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn tokenizer_identity_accepts_the_pinned_bytes_through_both_provenances() {
let sha256_hex = sha256_hex(artifact_tokenizer_bytes());
validate_tokenizer_identity(&TokenizerProvenance::Supplied(sha256_hex.clone()))
.expect("the pinned bytes are the identity, supplied");
validate_tokenizer_identity(&TokenizerProvenance::Artifact(Artifact::new(
artifact_tokenizer_path_for_tests(),
sha256_hex,
)))
.expect("the pinned bytes are the identity, read from the artifact");
}
#[test]
fn artifact_provenance_is_not_exempt_from_the_identity_pin() {
let err = validate_tokenizer_identity(&TokenizerProvenance::Artifact(Artifact::new(
std::path::PathBuf::from("/models/granite/tokenizer.json"),
"0".repeat(64),
)))
.expect_err("a sidecar that is not the pinned artifact must be refused");
match err {
Error::TokenizerContractMismatch(mismatch) => {
let actual = mismatch.actual();
assert_eq!(mismatch.check(), "artifact tokenizer identity (sha-256)");
assert_eq!(mismatch.expected(), contract::TOKENIZER_SHA256_HEX);
assert!(
actual.contains("/models/granite/tokenizer.json"),
"the diagnostic must name the offending file, got {actual}"
);
}
other => panic!("expected TokenizerContractMismatch, got {other:?}"),
}
}
#[test]
fn artifact_tokenizer_path_is_the_bundle_sibling() {
assert_eq!(
artifact_tokenizer_path(Path::new(
"/m/granite-97m-multilingual-r2/granite_97m_512.mlmodelc"
)),
Path::new("/m/granite-97m-multilingual-r2/tokenizer.json"),
);
assert_eq!(
artifact_tokenizer_path(Path::new("granite_97m_512.mlmodelc")),
Path::new("tokenizer.json"),
);
}
fn run_pipeline<W, R>(
text: &str,
opts: &WindowOptions,
win_measure: W,
range_measure: R,
) -> Result<Vec<windit::split::Chunk>>
where
W: Fn(&str) -> usize,
R: Fn(usize, usize) -> Result<usize>,
{
let chunks = windit::split::ContentAware::new(&win_measure)
.chunk(text, opts)
.map_err(Error::from)?;
let mut repaired = attach_gaps(text, chunks, &range_measure, opts.window())?;
if repaired.is_empty() && !text.is_empty() {
let tokens = range_measure(0, text.len())?;
if tokens > MAX_TOKENS {
return Err(Error::ContentlessInputOverBudget(
ContentlessInputOverBudget::new(0, text.len(), tokens, MAX_TOKENS),
));
}
repaired.push(windit::split::Chunk::new(0, text.len()));
}
if let Some(max) = opts.max_windows()
&& repaired.len() > max
{
return Err(Error::Windowing(windit::WinditError::TooManyWindows {
got: repaired.len(),
max,
}));
}
Ok(repaired)
}
fn chunk_long_slow(
mt: &Tokenizer,
text: &str,
opts: &WindowOptions,
) -> Result<Vec<windit::split::Chunk>> {
run_pipeline(
text,
opts,
|s: &str| {
mt.encode(s, true)
.map(|e| e.get_ids().len())
.unwrap_or(usize::MAX)
},
|a: usize, b: usize| {
mt.encode(&text[a..b], true)
.map(|e| e.get_ids().len())
.map_err(Error::Tokenize)
},
)
}
#[test]
fn a_drop_below_min_tail_moves_the_last_boundary_and_keeps_every_byte() {
let words = |s: &str| s.split_whitespace().count().max(1);
let run = |text: &'static str, opts: &WindowOptions| -> Vec<(usize, usize)> {
run_pipeline(text, opts, words, |a, b| Ok(words(&text[a..b])))
.unwrap_or_else(|e| panic!("chunk {text:?}: {e}"))
.iter()
.map(|c| (c.start(), c.end()))
.collect()
};
const DOC: &str = "a b c d e f g h\n\ni j";
let keep = WindowOptions::new(4);
assert_eq!(run(DOC, &keep), [(0, 8), (8, 17), (17, 20)]);
assert_eq!(keep.tail(), &TailPolicy::KeepWithCoverage);
assert_eq!(
run(DOC, &keep.with_tail(TailPolicy::PadFull)),
[(0, 8), (8, 17), (17, 20)]
);
assert_eq!(
run(DOC, &keep.with_tail(TailPolicy::DropBelowMin(2))),
[(0, 8), (8, 17), (17, 20)]
);
for min in [3usize, 4, 50] {
let moved = run(DOC, &keep.with_tail(TailPolicy::DropBelowMin(min)));
assert_eq!(
moved,
[(0, 8), (8, 15), (15, 20)],
"min={min} must move the boundary, not drop the tail"
);
assert_eq!(moved.len(), 3, "min={min} must not change the chunk count");
assert_eq!(
moved.iter().map(|&(a, b)| &DOC[a..b]).collect::<String>(),
DOC,
"min={min} must still cover every byte"
);
}
const EVEN: &str = "a b c d\n\ne f g h";
let even = run(EVEN, &keep);
for min in [5usize, 50] {
assert_eq!(
run(EVEN, &keep.with_tail(TailPolicy::DropBelowMin(min))),
even,
"a tail filling the whole window must survive min={min}"
);
}
const SHORT: &str = "one two three";
let one = WindowOptions::new(8);
assert_eq!(
windit::split::ContentAware::new(&words)
.chunk(SHORT, &one.with_tail(TailPolicy::DropBelowMin(4)))
.unwrap()
.len(),
0,
"windit must drop the only chunk for this to be the case it claims to be"
);
assert_eq!(
run(SHORT, &one.with_tail(TailPolicy::DropBelowMin(4))),
[(0, SHORT.len())]
);
assert_eq!(run(SHORT, &one), [(0, SHORT.len())]);
}
fn golden_texts() -> Vec<String> {
const CORPUS: &str = include_str!("../../../tests/granite/fixtures/goldens/corpus.json");
let v: serde_json::Value = serde_json::from_str(CORPUS).expect("parse corpus.json");
v["entries"]
.as_array()
.expect("entries array")
.iter()
.map(|e| e["text"].as_str().expect("entry text").to_string())
.collect()
}
fn differential_texts() -> Vec<String> {
let mut texts = golden_texts();
let doc: String = (0..8)
.map(|p| {
(0..16)
.map(|w| format!("para{p}word{w}"))
.collect::<Vec<_>>()
.join(" ")
})
.collect::<Vec<_>>()
.join("\n\n");
texts.push(format!("\n\n{doc}\n\n"));
texts.push(doc);
texts.push(
(0..120)
.map(|w| format!("term{w}"))
.collect::<Vec<_>>()
.join(", "),
);
texts.push("192.168.1.1 10.0.0.255 call 555-0142 order #A1234-99 on 2026-07-18. ".repeat(10));
texts.push(" \t \u{00A0}\u{2009}mixed ws\r\n\r\n runs\t here and there ".repeat(10));
texts.push("x".repeat(2048));
texts.push("café\u{0301} 你好 🍕 👨\u{200D}👩\u{200D}👧\u{200D}👦 tëst ".repeat(12));
texts
}
fn geometry_grid() -> Vec<WindowOptions> {
let mut g = Vec::new();
for &w in &[8usize, 32, 128, 512] {
g.push(WindowOptions::new(w));
if w > 16 {
g.push(WindowOptions::new(w).with_overlap(16));
}
}
g.push(WindowOptions::new(128).with_max_windows(4));
g.push(WindowOptions::new(512).with_max_windows(2));
g
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn chunk_long_matches_slow_twin_over_corpus_and_geometry() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let grid = geometry_grid();
for text in differential_texts() {
for opts in &grid {
let (window, overlap) = (opts.window(), opts.overlap());
match (
chunk_long(&mt, &text, opts),
chunk_long_slow(&mt, &text, opts),
) {
(Ok(fast), Ok(slow)) => assert_eq!(
fast, slow,
"fast/slow chunk mismatch (window={window}, overlap={overlap}) for {text:.40?}"
),
(Err(_), Err(_)) => {}
(fast, slow) => panic!(
"fast/slow Ok-vs-Err disagreement (window={window}, overlap={overlap}) for {text:.40?}: \
{fast:?} vs {slow:?}"
),
}
}
}
}
fn killer_chunk_texts() -> Vec<String> {
let units = [
"456 1 word ",
"a\u{00A0}\u{00A0}9 sep ",
"b\u{2009}\u{2009}🌿 emoji ",
"a!\r\n\r\n Next term ",
"end.\r\n\r\n\r\nStart here ",
" it'station end ",
"we'reunited now ",
"cafe\u{0301}s re\u{0301}sume\u{0301} ",
"192.168.100.254 ٠١٢٣ 0123 ",
"http://a/b/c/d /usr/local/bin ",
"👨\u{200D}👩\u{200D}👧\u{200D}👦 x ",
];
let mut texts: Vec<String> = units.iter().map(|u| u.repeat(24)).collect();
texts.push(units.concat().repeat(12));
texts
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn chunk_long_matches_slow_twin_over_killer_classes() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let grid = geometry_grid();
for text in killer_chunk_texts() {
for opts in &grid {
let (window, overlap) = (opts.window(), opts.overlap());
match (
chunk_long(&mt, &text, opts),
chunk_long_slow(&mt, &text, opts),
) {
(Ok(fast), Ok(slow)) => assert_eq!(
fast, slow,
"fast/slow killer chunk mismatch (window={window}, overlap={overlap}) for {text:.40?}"
),
(Err(_), Err(_)) => {}
(fast, slow) => panic!(
"fast/slow Ok-vs-Err disagreement (window={window}, overlap={overlap}) for {text:.40?}: \
{fast:?} vs {slow:?}"
),
}
}
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn chunk_differential_is_load_bearing_against_a_shifted_measure() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let doc: String = "a b c d e f g h i j k l m n o p q r s t u v w x y z "
.repeat(8)
.trim_end()
.to_string();
let opts = WindowOptions::new(16);
let exact_range = |a: usize, b: usize| {
mt.encode(&doc[a..b], true)
.map(|e| e.get_ids().len())
.map_err(Error::Tokenize)
};
let correct = run_pipeline(
&doc,
&opts,
|s: &str| {
mt.encode(s, true)
.map(|e| e.get_ids().len())
.unwrap_or(usize::MAX)
},
exact_range,
)
.expect("exact pipeline");
let shifted = run_pipeline(
&doc,
&opts,
|s: &str| {
mt.encode(s, true)
.map(|e| e.get_ids().len() + 1)
.unwrap_or(usize::MAX)
},
exact_range,
)
.expect("shifted pipeline");
assert_ne!(
correct, shifted,
"a one-token measure over-count must move a chunk boundary — the fast==slow gate is \
load-bearing"
);
}
fn natural_doc(target_bytes: usize) -> String {
const WORDS: &[&str] = &[
"the",
"quantum",
"entanglement",
"system",
"provides",
"native",
"on-device",
"inference",
"for",
"text",
"embeddings",
"and",
"retrieval",
"across",
"many",
"languages",
"with",
"stable",
"latency",
"under",
"load",
"because",
"model",
"compiles",
"efficiently",
"into",
"a",
"fixed",
"graph",
];
let mut s = String::with_capacity(target_bytes + 64);
let mut r: u64 = 0x9E37_79B9_7F4A_7C15;
let mut step = || {
r = r
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
(r >> 33) as usize
};
let mut sentences_in_para = 0u32;
while s.len() < target_bytes {
let words = 8 + step() % 9;
for k in 0..words {
if k > 0 {
s.push(' ');
}
s.push_str(WORDS[step() % WORDS.len()]);
}
if step() % 4 == 0 {
s.push_str(&format!(" {}", 1000 + step() % 90_000));
}
s.push('.');
sentences_in_para += 1;
if sentences_in_para >= 5 {
s.push_str("\n\n");
sentences_in_para = 0;
} else {
s.push(' ');
}
}
s
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn measure_path_reencodes_at_most_1_5x_input() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let doc = natural_doc(256 * 1024);
super::token_index::encode_meter::reset();
let chunks = chunk_long(&mt, &doc, &WindowOptions::new(MAX_TOKENS)).expect("chunk");
let encoded = super::token_index::encode_meter::get();
let ratio = encoded as f64 / doc.len() as f64;
println!(
"[byte-ratio] input={} bytes, encoded={} bytes, ratio={ratio:.3}x, chunks={}",
doc.len(),
encoded,
chunks.len()
);
assert!(
ratio <= 1.5,
"measure path re-encoded {ratio:.3}x the input (> 1.5x) — the single-pass index regressed \
toward the old per-range re-encode"
);
}
fn cjk_doc(target_bytes: usize) -> String {
const RUN: &str = "你好世界模型推理文本嵌入检索";
let mut s = String::with_capacity(target_bytes + RUN.len());
while s.len() < target_bytes {
s.push_str(RUN);
}
s
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn separatorless_text_is_one_pretoken() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
for (label, doc) in [("cjk", cjk_doc(4096)), ("ascii-letters", "x".repeat(4096))] {
let enc = mt.encode(doc.as_str(), false).expect("encode");
let pretokens = enc
.get_word_ids()
.iter()
.flatten()
.copied()
.max()
.map_or(0, |m| m as usize + 1);
assert_eq!(
pretokens,
1,
"{label}: expected one document-spanning pre-token over {} bytes, got {pretokens} — the \
separatorless measure-cost characterization rests on this",
doc.len()
);
assert!(
enc.get_ids().len() > 1,
"{label}: the one pre-token must still carry many BPE tokens"
);
}
}
#[test]
#[ignore = "quadratic slow twin, and the staged granite tokenizer.json (run locally); \
the separatorless re-encode gate"]
fn separatorless_measure_path_reencodes_at_most_8x_input() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let opts = WindowOptions::new(MAX_TOKENS);
for (label, doc) in [
("cjk", cjk_doc(15_666)),
("ascii-letters", "x".repeat(7_833)),
] {
let _ = merge_table();
super::token_index::encode_meter::reset();
super::token_index::build_meter::reset();
let t0 = std::time::Instant::now();
let fast = chunk_long(&mt, &doc, &opts).expect("fast chunk");
let fast_ms = t0.elapsed().as_secs_f64() * 1e3;
let ratio = super::token_index::encode_meter::get() as f64 / doc.len() as f64;
let builds = super::token_index::build_meter::builds();
let mut sizes = super::token_index::encode_meter::sizes();
sizes.sort_unstable_by(|a, b| b.cmp(a));
let big: Vec<usize> = sizes.iter().copied().filter(|&n| n > 512).collect();
println!(
"[separatorless:{label}:encodes] calls={} over512={} over512_bytes={} top={:?}",
sizes.len(),
big.len(),
big.iter().sum::<usize>(),
&sizes[..sizes.len().min(12)]
);
let t1 = std::time::Instant::now();
let slow = chunk_long_slow(&mt, &doc, &opts).expect("slow chunk");
let slow_ms = t1.elapsed().as_secs_f64() * 1e3;
assert_eq!(fast, slow, "{label}: fast/slow chunk mismatch");
println!(
"[separatorless:{label}] bytes={} chunks={} fast={fast_ms:.1}ms slow={slow_ms:.1}ms \
speedup={:.1}x reencode_ratio={ratio:.1}x sessions={} session_bytes={}",
doc.len(),
fast.len(),
slow_ms / fast_ms,
builds.len(),
builds.iter().map(|&(s, e)| e - s).sum::<usize>(),
);
assert!(
ratio <= 8.0,
"{label}: measure path re-encoded {ratio:.1}x the input (> 8x) — the separatorless fast \
lane stopped engaging"
);
}
}
#[test]
#[ignore = "4 MiB fast-vs-slow chunk differential + timing, and the staged granite \
tokenizer.json (run locally for PR notes)"]
fn big_document_fast_matches_slow_with_timing() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let opts = WindowOptions::new(MAX_TOKENS);
for size in [1usize << 20, 4usize << 20] {
let doc = natural_doc(size);
let _ = merge_table();
super::token_index::encode_meter::reset();
let t0 = std::time::Instant::now();
let fast = chunk_long(&mt, &doc, &opts).expect("fast chunk");
let fast_ms = t0.elapsed().as_secs_f64() * 1e3;
let fast_bytes = super::token_index::encode_meter::get();
let t1 = std::time::Instant::now();
let slow = chunk_long_slow(&mt, &doc, &opts).expect("slow chunk");
let slow_ms = t1.elapsed().as_secs_f64() * 1e3;
assert_eq!(fast, slow, "fast/slow chunk mismatch at {size} bytes");
let ratio = fast_bytes as f64 / doc.len() as f64;
println!(
"[big-diff] size={} chunks={} fast={fast_ms:.1}ms slow={slow_ms:.1}ms \
speedup={:.1}x reencode_ratio={ratio:.3}x",
doc.len(),
fast.len(),
slow_ms / fast_ms,
);
assert!(
ratio <= 1.5,
"reencode ratio {ratio:.3}x > 1.5x at {size} bytes"
);
}
}
const DEFECTIVE_TEMPLATE_TOKENIZER: &[u8] = br#"{"version":"1.0","truncation":null,"padding":null,"added_tokens":[],"normalizer":null,"pre_tokenizer":{"type":"Whitespace"},"post_processor":{"type":"TemplateProcessing","single":[{"SpecialToken":{"id":"<s>","type_id":0}},{"Sequence":{"id":"A","type_id":0}}],"pair":[{"Sequence":{"id":"A","type_id":0}}],"special_tokens":{}},"decoder":null,"model":{"type":"WordLevel","vocab":{"<pad>":0,"a":1,"b":2},"unk_token":"<pad>"}}"#;
#[test]
fn from_memory_hashes_the_bytes_before_parsing_them() {
for bytes in [
b"this is not json at all".as_slice(),
DEFECTIVE_TEMPLATE_TOKENIZER,
] {
match TextEmbedder::from_memory(
"/nonexistent/model.mlmodelc",
bytes,
TextEmbedderOptions::new(),
) {
Err(Error::TokenizerContractMismatch(mismatch)) => {
assert_eq!(mismatch.check(), "tokenizer identity (sha-256)");
assert_eq!(mismatch.expected(), contract::TOKENIZER_SHA256_HEX);
}
other => panic!("expected the identity gate to fire before the parse, got {other:?}"),
}
}
}
#[test]
fn load_hashes_the_sidecar_before_parsing_it() {
let dir = tempfile::tempdir().expect("tempdir");
let model_path = dir.path().join("granite_97m_512.mlmodelc");
let tokenizer_path = dir.path().join(TOKENIZER_FILE_NAME);
std::fs::write(&tokenizer_path, b"this is not json at all").expect("write sidecar");
match TextEmbedder::load(&model_path, TextEmbedderOptions::new()) {
Err(Error::TokenizerContractMismatch(mismatch)) => {
assert_eq!(mismatch.check(), "artifact tokenizer identity (sha-256)");
assert!(
mismatch
.actual()
.contains(&tokenizer_path.display().to_string()),
"the diagnostic names the file: {}",
mismatch.actual()
);
}
other => panic!("expected the identity gate to fire before the parse, got {other:?}"),
}
}
#[test]
fn the_defective_template_fixture_really_panics_at_encode() {
let tokenizer =
Tokenizer::from_bytes(DEFECTIVE_TEMPLATE_TOKENIZER).expect("a defective template still parses");
let panicked = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _ = tokenizer.encode("a b", true);
}))
.is_err();
assert!(
panicked,
"applying the template panics inside the dependency"
);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn dropped_byte_text_chunks_like_the_slow_twin_at_an_exact_fit_window() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
assert_eq!(
mt.encode("\0\0", true).expect("encode").get_ids().len(),
2,
"premise: both NULs are dropped, only the specials remain"
);
let opts = WindowOptions::new(2).with_max_windows(1);
let fast = chunk_long(&mt, "\0\0", &opts);
let slow = chunk_long_slow(&mt, "\0\0", &opts);
match (&fast, &slow) {
(Ok(f), Ok(s)) => assert_eq!(f, s, "fast/slow chunk mismatch on dropped bytes"),
(f, s) => panic!("fast {f:?} vs slow {s:?}"),
}
assert_eq!(fast.expect("one chunk").len(), 1);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn a_document_of_dropped_bytes_stays_one_window() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let text = "\0".repeat(65_281);
let opts = WindowOptions::new(MAX_TOKENS);
let fast = chunk_long(&mt, &text, &opts).expect("fast");
let slow = chunk_long_slow(&mt, &text, &opts).expect("slow");
assert_eq!(fast, slow);
assert_eq!(fast.len(), 1);
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn dropped_bytes_interleaved_in_cjk_chunk_like_the_slow_twin() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let nul_run = "\0".repeat(300);
let dominated: String = "你好世界模型推理"
.chars()
.map(|c| format!("{c}{nul_run}"))
.collect();
assert_eq!(
mt.encode(&dominated[..306], true)
.expect("encode")
.get_ids()
.len(),
4,
"premise: two ideographs and the dropped run between them are two content tokens"
);
for window in [4usize, 5, 8] {
let opts = WindowOptions::new(window);
let fast = chunk_long(&mt, &dominated, &opts);
let slow = chunk_long_slow(&mt, &dominated, &opts);
match (&fast, &slow) {
(Ok(f), Ok(s)) => assert_eq!(f, s, "dominated, window {window}: fast/slow chunk mismatch"),
(f, s) => panic!("dominated, window {window}: fast {f:?} vs slow {s:?}"),
}
}
let single: String = "你好世界模型推理文本嵌入检索"
.chars()
.flat_map(|c| [c, '\0'])
.collect::<String>()
.repeat(40);
for window in [2usize, 3, 7, 32, 128, MAX_TOKENS] {
let opts = WindowOptions::new(window);
let fast = chunk_long(&mt, &single, &opts);
let slow = chunk_long_slow(&mt, &single, &opts);
match (&fast, &slow) {
(Ok(f), Ok(s)) => assert_eq!(f, s, "single, window {window}: fast/slow chunk mismatch"),
(f, s) => panic!("single, window {window}: fast {f:?} vs slow {s:?}"),
}
}
}
#[track_caller]
fn assert_twin(mt: &Tokenizer, what: &str, text: &str, opts: &WindowOptions) {
let fast = chunk_long(mt, text, opts);
let slow = chunk_long_slow(mt, text, opts);
match (&fast, &slow) {
(Ok(f), Ok(s)) => assert_eq!(f, s, "{what}: fast/slow chunk mismatch"),
(f, s) => panic!("{what}: fast {f:?} vs slow {s:?}"),
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn an_uppercase_run_inside_cjk_chunks_like_the_slow_twin() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let cycle = "你好世界模型推理";
let tail = cycle.repeat(200);
for (window, ks) in [
(MAX_TOKENS, &[813usize, 814, 815][..]),
(32, &[45][..]),
(16, &[20][..]),
] {
for &k in ks {
let head: String = cycle.chars().cycle().take(k).collect();
let doc = format!("{head}UCCESS{tail}");
assert_twin(
&mt,
&format!("window {window}, k {k}"),
&doc,
&WindowOptions::new(window),
);
}
}
for pat in [
"你好世界UCCESSa模型推理",
"你UCCESSa",
"你好UCCESSa模",
"你好世WARRANTIESa模",
] {
let doc = pat.repeat(3_000 / pat.len() + 1);
for window in [8usize, 11, 16, 24, 32, 45, 64, 100, 128, 160] {
assert_twin(
&mt,
&format!("{pat:?} at window {window}"),
&doc,
&WindowOptions::new(window),
);
}
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn mixed_case_scripts_chunk_like_the_slow_twin() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let pieces: [&str; 28] = [
"你好世界",
"模型推理",
"文本嵌入",
"检索",
"返回",
"hello",
"world",
"granite",
"embedding",
"UCCESS",
"WARRANTIES",
"API",
"ABC",
"Windows",
"SUCCESS",
"Dž",
"Lj",
"ᾈ",
"Dz",
"\u{301}",
"\u{5bf}",
" ",
" ",
"12",
"。",
"'s",
"\n",
"αβγ",
];
let mut seed = 0x9E37_79B9_7F4A_7C15u64;
let mut next = move || {
seed = seed
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
(seed >> 33) as usize
};
for round in 0..6 {
let mut doc = String::new();
while doc.len() < 2_500 {
let stretch = 1 + next() % 40;
for _ in 0..stretch {
doc.push_str(pieces[next() % pieces.len()]);
}
if next() % 3 == 0 {
doc.push(' ');
}
}
for window in [16usize, 32, 128, MAX_TOKENS] {
assert_twin(
&mt,
&format!("round {round}, window {window}"),
&doc,
&WindowOptions::new(window),
);
}
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn runs_that_cannot_qualify_never_build_the_table() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let opts = WindowOptions::new(64);
for (label, doc) in [
("uppercase-4000", format!("intro {} body", "A".repeat(4000))),
(
"equals-rule-4000",
format!("intro\n{}\nbody\n", "=".repeat(4000)),
),
(
"dash-rule-200",
format!("intro text\n{}\nbody text\n", "-".repeat(200)),
),
(
"newline-run-4000",
format!("intro{}body", "\n".repeat(4000)),
),
("space-run-4000", format!("intro{}body", " ".repeat(4000))),
(
"titlecase-then-upper",
format!("x {}{} y", "Dž", "B".repeat(300)),
),
] {
let cell: OnceLock<Option<MergeTable>> = OnceLock::new();
let fast = super::chunk_long(&mt, LazyTable::new(&cell, &mt), &doc, &opts);
assert!(
cell.get().is_none(),
"{label}: a pre-token that cannot qualify must not build the table"
);
let slow = chunk_long_slow(&mt, &doc, &opts);
match (&fast, &slow) {
(Ok(f), Ok(s)) => assert_eq!(f, s, "{label}: fast/slow chunk mismatch"),
(f, s) => panic!("{label}: fast {f:?} vs slow {s:?}"),
}
}
let cell: OnceLock<Option<MergeTable>> = OnceLock::new();
let doc = "你好世界模型推理文本嵌入检索".repeat(60);
let fast = super::chunk_long(&mt, LazyTable::new(&cell, &mt), &doc, &opts).expect("fast");
assert!(
cell.get().is_some_and(Option::is_some),
"a qualifying separatorless pre-token engages the lane and builds the table"
);
assert_eq!(fast, chunk_long_slow(&mt, &doc, &opts).expect("slow"));
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn an_added_token_spelled_like_a_word_chunks_like_the_slow_twin() {
let base = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
let spelled = merge_table().spell(b" zzqxjkw");
let mut value: serde_json::Value =
serde_json::from_slice(artifact_tokenizer_bytes()).expect("parse");
let next_id = u64::try_from(base.get_vocab_size(true)).expect("id");
value["added_tokens"]
.as_array_mut()
.expect("array")
.push(serde_json::json!({
"id": next_id, "content": spelled, "single_word": false, "lstrip": false,
"rstrip": false, "normalized": false, "special": false
}));
let hacked = Tokenizer::from_bytes(serde_json::to_vec(&value).expect("serialize")).expect("load");
let cell: OnceLock<Option<MergeTable>> = OnceLock::new();
let doc = format!(
"{} zzqxjkw{}",
"你好世界模型推理".repeat(30),
"你好世界".repeat(30)
);
for window in [16usize, 64, MAX_TOKENS] {
let opts = WindowOptions::new(window);
let fast = super::chunk_long(&hacked, LazyTable::new(&cell, &hacked), &doc, &opts);
let slow = chunk_long_slow(&hacked, &doc, &opts);
match (&fast, &slow) {
(Ok(f), Ok(s)) => assert_eq!(f, s, "window {window}: fast/slow chunk mismatch"),
(f, s) => panic!("window {window}: fast {f:?} vs slow {s:?}"),
}
}
assert!(cell.get().is_some_and(Option::is_some), "the lane engaged");
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn a_small_probe_inside_a_long_word_never_builds_the_table() {
let mt = measuring_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("measuring");
for (label, doc, window, max_windows) in [
("129 a's, window 3", "a".repeat(129), 3usize, None),
(
"129 a's, window 3, no windows allowed",
"a".repeat(129),
3,
Some(0usize),
),
("400 a's, window 8", "a".repeat(400), 8, None),
(
"CJK 129 bytes, window 4",
"你好世界模型推理".repeat(6),
4,
None,
),
] {
let mut opts = WindowOptions::new(window);
if let Some(cap) = max_windows {
opts = opts.with_max_windows(cap);
}
let cell: OnceLock<Option<MergeTable>> = OnceLock::new();
let fast = super::chunk_long(&mt, LazyTable::new(&cell, &mt), &doc, &opts);
assert!(
cell.get().is_none(),
"{label}: no probe longer than a token, so no table"
);
let slow = chunk_long_slow(&mt, &doc, &opts);
match (&fast, &slow) {
(Ok(f), Ok(s)) => assert_eq!(f, s, "{label}: fast/slow chunk mismatch"),
(Err(f), Err(s)) => assert_eq!(format!("{f:?}"), format!("{s:?}"), "{label}: error shape"),
(f, s) => panic!("{label}: fast {f:?} vs slow {s:?}"),
}
}
let cell: OnceLock<Option<MergeTable>> = OnceLock::new();
let doc = "你好世界模型推理文本嵌入检索".repeat(60);
let opts = WindowOptions::new(64);
let fast = super::chunk_long(&mt, LazyTable::new(&cell, &mt), &doc, &opts).expect("fast");
assert!(
cell.get().is_some_and(Option::is_some),
"a long probe engages the lane"
);
assert_eq!(fast, chunk_long_slow(&mt, &doc, &opts).expect("slow"));
}
#[test]
fn window_embedding_accessors_report_the_planned_geometry() {
let mut v = [0.0f32; EMBEDDING_DIM];
v[7] = 2.0;
let embedding = Embedding::from_slice_normalizing(&v).expect("unit vector");
let w = WindowEmbedding {
ordinal: 3,
byte_range: 17..42,
token_span: windit::plan::Span::new(1_024, 300, MAX_TOKENS),
embedding: embedding.clone(),
};
assert_eq!(w.ordinal(), 3);
assert_eq!(w.byte_start(), 17);
assert_eq!(w.byte_end(), 42);
assert_eq!(w.byte_range(), 17..42);
assert_eq!(
w.token_span(),
windit::plan::Span::new(1_024, 300, MAX_TOKENS)
);
assert_eq!(w.token_span().start(), 1_024);
assert_eq!(w.token_count(), 300);
assert_eq!(w.token_span().window(), MAX_TOKENS);
assert!(w.embedding().is_close(&embedding, 0.0), "by reference");
assert!(
w.clone().into_embedding().is_close(&embedding, 0.0),
"by value"
);
let text = "x".repeat(64);
assert_eq!(text[w.byte_range()].len(), 25);
}
#[test]
fn content_tokens_per_window_is_the_measured_truncation_budget() {
const CONTENT_ID: u32 = 1;
let bytes = tokenizer_bytes_with_special_overhead(SPECIAL_TOKENS_PER_WINDOW);
let tok = configured_tokenizer_from_bytes(&bytes).expect("the artifact's overhead configures");
for (content, kept) in [
(1, 1),
(CONTENT_TOKENS_PER_WINDOW - 1, CONTENT_TOKENS_PER_WINDOW - 1),
(CONTENT_TOKENS_PER_WINDOW, CONTENT_TOKENS_PER_WINDOW),
(CONTENT_TOKENS_PER_WINDOW + 1, CONTENT_TOKENS_PER_WINDOW),
(MAX_TOKENS * 2, CONTENT_TOKENS_PER_WINDOW),
] {
let text = "a ".repeat(content);
let ids = tok.encode(text.as_str(), true).expect("encode");
let ids = ids.get_ids();
assert_eq!(
ids.iter().filter(|&&id| id == CONTENT_ID).count(),
kept,
"{content} content tokens through a {MAX_TOKENS}-token window"
);
assert!(
ids.len() <= MAX_TOKENS,
"{content} content tokens overran the window: {} ids",
ids.len()
);
}
}
#[test]
#[ignore = "requires the granite tokenizer.json staged beside the model bundle (EMBEDKIT_TEST_MODELS)"]
fn special_token_overhead_matches_the_pinned_template() {
use tokenizers::PostProcessor;
let tok = configured_tokenizer_from_bytes(artifact_tokenizer_bytes()).expect("configure");
let post = tok
.get_post_processor()
.expect("the artifact declares a TemplateProcessing");
assert_eq!(
post.added_tokens(false),
SPECIAL_TOKENS_PER_WINDOW,
"the post-processor's single-sequence overhead"
);
let empty = tok.encode("", true).expect("encode the empty string");
assert_eq!(
empty.get_ids().len(),
SPECIAL_TOKENS_PER_WINDOW,
"the template applied to no content is the specials alone"
);
assert_eq!(
empty.get_ids(),
[contract::CLS_ID, contract::EOS_ID],
"the single-sequence template is <|startoftext|> A <|return|>"
);
let text = "hello world";
let with = tok.encode(text, true).expect("with specials");
let without = tok.encode(text, false).expect("without specials");
assert_eq!(
with.get_ids().len() - without.get_ids().len(),
SPECIAL_TOKENS_PER_WINDOW,
"the specials are the whole difference"
);
assert_eq!(with.get_ids().first(), Some(&contract::CLS_ID));
assert_eq!(with.get_ids().last(), Some(&contract::EOS_ID));
assert_eq!(
MAX_TOKENS - post.added_tokens(false),
CONTENT_TOKENS_PER_WINDOW,
"the artifact's own content budget"
);
}
fn lcg_row(seed: u64) -> [f32; EMBEDDING_DIM] {
let mut state = seed;
std::array::from_fn(|_| {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
let top24 = (state >> 40) as u32;
(top24 as f32 / (1u32 << 24) as f32) * 2.0 - 1.0
})
}
#[test]
fn single_window_is_the_bit_exact_identity() {
for (i, &len) in [1usize, 2, 511, 512].iter().enumerate() {
let row = lcg_row(0x9E37_79B9_7F4A_7C15 ^ (i as u64 + 1));
let embedding = Embedding::from_slice_normalizing(&row).expect("normalize a random row");
let windowed = windit::windowed::Windowed::new(
embedding.clone(),
windit::plan::Span::new(0, len, MAX_TOKENS),
);
let agg = windit::aggregate::aggregate(
&windit::aggregate::CoverageWeightedMean,
std::slice::from_ref(&windowed),
)
.expect("aggregate a single window");
assert_eq!(
agg.as_slice(),
embedding.as_slice(),
"len={len} (coverage {len}/{MAX_TOKENS}): single-window aggregation must be bit-exact"
);
}
}