use crate::grammar::gbnf::parse_gbnf;
use crate::grammar::json_schema::compile;
use crate::grammar::pda::{
CompiledGrammar, GrammarState, SimResult, StepResult, advance_byte, initial_grammar_state,
simulate_token,
};
use crate::grammar::spec::GrammarSpec;
use crate::grammar::trie::ByteTrie;
use crate::grammar::vocab_partition::VocabPartition;
use std::fmt;
use std::sync::OnceLock;
use std::sync::atomic::{AtomicU64, Ordering};
thread_local! {
static MASK_PROFILING_ENABLED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
static MASK_PROFILE: std::cell::RefCell<MaskProfile> =
const { std::cell::RefCell::new(MaskProfile::new()) };
static CONTEXT_RECHECK_SIMULATED: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
static CONTEXT_RECHECK_CANDIDATES: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
static BUILD_PROFILE: std::cell::RefCell<BuildProfile> =
const { std::cell::RefCell::new(BuildProfile::new()) };
}
#[cfg(test)]
thread_local! {
static CONTEXT_RECHECK_CANDIDATES_FOR_TEST: std::cell::Cell<u64> =
const { std::cell::Cell::new(0) };
}
#[cfg(test)]
fn reset_context_recheck_candidates_for_test() {
CONTEXT_RECHECK_CANDIDATES_FOR_TEST.with(|count| count.set(0));
}
#[cfg(test)]
fn take_context_recheck_candidates_for_test() -> u64 {
CONTEXT_RECHECK_CANDIDATES_FOR_TEST.with(std::cell::Cell::get)
}
#[derive(Debug, Clone, Copy, Default)]
pub struct MaskProfile {
pub precomputed_calls: u64,
pub precomputed_ns: u64,
pub context_recheck_calls: u64,
pub context_recheck_ns: u64,
pub fallback_calls: u64,
pub fallback_ns: u64,
pub advance_calls: u64,
pub advance_ns: u64,
}
impl MaskProfile {
const fn new() -> Self {
Self {
precomputed_calls: 0,
precomputed_ns: 0,
context_recheck_calls: 0,
context_recheck_ns: 0,
fallback_calls: 0,
fallback_ns: 0,
advance_calls: 0,
advance_ns: 0,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct BuildProfile {
pub bfs_ns: u64,
pub partition_build_ns: u64,
pub reachable_states: usize,
pub capped_states: usize,
}
impl BuildProfile {
const fn new() -> Self {
Self {
bfs_ns: 0,
partition_build_ns: 0,
reachable_states: 0,
capped_states: 0,
}
}
}
pub fn enable_mask_profiling() {
MASK_PROFILING_ENABLED.with(|e| e.set(true));
MASK_PROFILE.with(|p| *p.borrow_mut() = MaskProfile::new());
CONTEXT_RECHECK_SIMULATED.with(|c| c.set(0));
CONTEXT_RECHECK_CANDIDATES.with(|c| c.set(0));
}
pub fn take_mask_profile() -> MaskProfile {
MASK_PROFILING_ENABLED.with(|e| e.set(false));
MASK_PROFILE.with(|p| *p.borrow())
}
pub fn context_recheck_simulated() -> u64 {
CONTEXT_RECHECK_SIMULATED.with(std::cell::Cell::get)
}
pub fn context_recheck_candidates() -> u64 {
CONTEXT_RECHECK_CANDIDATES.with(std::cell::Cell::get)
}
fn mask_profiling_enabled() -> bool {
MASK_PROFILING_ENABLED.with(std::cell::Cell::get)
}
pub fn last_build_profile() -> BuildProfile {
BUILD_PROFILE.with(|p| *p.borrow())
}
pub fn probe_reachable_states(
spec: &GrammarSpec,
vocab_bytes: &[Vec<u8>],
max_states: usize,
) -> Result<usize, GrammarError> {
let grammar = match spec {
GrammarSpec::JsonSchema(schema) => compile(schema)?,
GrammarSpec::Gbnf(gbnf) => parse_gbnf(gbnf)?,
};
Ok(enumerate_grammar_states(&grammar, vocab_bytes, max_states).len())
}
#[derive(Debug, Clone)]
pub struct GrammarError(pub String);
impl fmt::Display for GrammarError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "GrammarEngine error: {}", self.0)
}
}
impl std::error::Error for GrammarError {}
impl From<GrammarError> for crate::error::InferenceError {
fn from(error: GrammarError) -> Self {
Self::InvalidInput(error.0)
}
}
impl From<crate::grammar::json_schema::SchemaError> for GrammarError {
fn from(e: crate::grammar::json_schema::SchemaError) -> Self {
GrammarError(e.0)
}
}
impl From<crate::grammar::gbnf::GbnfError> for GrammarError {
fn from(e: crate::grammar::gbnf::GbnfError) -> Self {
GrammarError(e.0)
}
}
fn enumerate_grammar_states(
grammar: &CompiledGrammar,
vocab_bytes: &[Vec<u8>],
max_states: usize,
) -> Vec<GrammarState> {
let initial = initial_grammar_state(grammar);
let mut queue: Vec<GrammarState> = vec![initial.clone()];
let mut visited: Vec<GrammarState> = vec![initial];
let mut head = 0;
while head < queue.len() && visited.len() < max_states {
let state = queue[head].clone();
head += 1;
for token_bytes in vocab_bytes {
if token_bytes.is_empty() {
continue;
}
let (result, next_state) = simulate_token(&state, grammar, token_bytes);
if result == SimResult::Accept || result == SimResult::ContextDependent {
if !visited.iter().any(|s| states_equal(s, &next_state)) {
visited.push(next_state.clone());
if visited.len() < max_states {
queue.push(next_state);
}
}
}
}
}
visited
}
fn states_equal(a: &GrammarState, b: &GrammarState) -> bool {
a.stack == b.stack && a.complete == b.complete
}
pub struct GrammarEngine {
grammar: CompiledGrammar,
partition: VocabPartition,
vocab_size: usize,
vocab_bytes: Vec<Vec<u8>>,
state_limit_exceeded: bool,
trie: OnceLock<ByteTrie>,
trie_build_ns: AtomicU64,
}
impl GrammarEngine {
pub fn new(spec: &GrammarSpec, vocab_bytes: Vec<Vec<u8>>) -> Result<Self, GrammarError> {
let vocab_size = vocab_bytes.len();
let grammar = match spec {
GrammarSpec::JsonSchema(schema) => compile(schema)?,
GrammarSpec::Gbnf(gbnf) => parse_gbnf(gbnf)?,
};
let bfs_t0 = std::time::Instant::now();
let states = enumerate_grammar_states(
&grammar,
&vocab_bytes,
crate::grammar::vocab_partition::MAX_GRAMMAR_STATES,
);
let bfs_ns = bfs_t0.elapsed().as_nanos() as u64;
let state_limit_exceeded =
states.len() >= crate::grammar::vocab_partition::MAX_GRAMMAR_STATES;
if state_limit_exceeded {
tracing::warn!(
"grammar state count hit limit ({}); some states may fall back to context-dependent checks",
crate::grammar::vocab_partition::MAX_GRAMMAR_STATES
);
}
let reachable_states = states.len();
let partition_t0 = std::time::Instant::now();
let partition = VocabPartition::build(&grammar, states, &vocab_bytes);
let partition_build_ns = partition_t0.elapsed().as_nanos() as u64;
BUILD_PROFILE.with(|p| {
*p.borrow_mut() = BuildProfile {
bfs_ns,
partition_build_ns,
reachable_states,
capped_states: crate::grammar::vocab_partition::MAX_GRAMMAR_STATES,
}
});
Ok(Self {
grammar,
partition,
vocab_size,
vocab_bytes,
state_limit_exceeded,
trie: OnceLock::new(),
trie_build_ns: AtomicU64::new(0),
})
}
pub fn trie_build_ns(&self) -> u64 {
self.trie_build_ns.load(Ordering::Relaxed)
}
pub fn exceeds_state_budget(&self) -> bool {
self.state_limit_exceeded
}
pub fn initial_state(&self) -> GrammarState {
initial_grammar_state(&self.grammar)
}
pub fn mask_logits(
&self,
state: &mut GrammarState,
logits: &mut [f32],
) -> Result<(), GrammarError> {
self.validate_logits_len(logits)?;
let profiling = mask_profiling_enabled();
let find_t0 = profiling.then(std::time::Instant::now);
let found = self.find_state_id(state);
let find_ns = find_t0.map(|t| t.elapsed().as_nanos() as u64).unwrap_or(0);
match found {
Some(state_id) => {
let t0 = profiling.then(std::time::Instant::now);
self.partition.apply_mask(state_id, logits);
if let Some(t0) = t0 {
let ns = find_ns + t0.elapsed().as_nanos() as u64;
MASK_PROFILE.with(|p| {
let mut p = p.borrow_mut();
p.precomputed_calls += 1;
p.precomputed_ns += ns;
});
}
let t1 = profiling.then(std::time::Instant::now);
let mut simulated = 0u64;
let mut visited = 0u64;
for &token_id in self.partition.context_dependent_ids_for_state(state_id) {
#[cfg(test)]
CONTEXT_RECHECK_CANDIDATES_FOR_TEST.with(|count| count.set(count.get() + 1));
if profiling {
visited += 1;
}
if token_id >= self.vocab_size {
continue;
}
if logits[token_id] == f32::NEG_INFINITY {
continue;
}
let token_bytes = &self.vocab_bytes[token_id];
if token_bytes.is_empty() {
logits[token_id] = f32::NEG_INFINITY;
continue;
}
let (result, _) = simulate_token(state, &self.grammar, token_bytes);
if profiling {
simulated += 1;
}
match result {
SimResult::Reject | SimResult::ContextDependent => {
logits[token_id] = f32::NEG_INFINITY;
}
SimResult::Accept => {}
}
}
if let Some(t1) = t1 {
let ns = t1.elapsed().as_nanos() as u64;
MASK_PROFILE.with(|p| {
let mut p = p.borrow_mut();
p.context_recheck_calls += 1;
p.context_recheck_ns += ns;
});
CONTEXT_RECHECK_SIMULATED.with(|c| c.set(c.get() + simulated));
CONTEXT_RECHECK_CANDIDATES.with(|c| c.set(c.get() + visited));
}
}
None => {
let t0 = profiling.then(std::time::Instant::now);
self.mask_by_trie(state, logits);
if let Some(t0) = t0 {
let ns = find_ns + t0.elapsed().as_nanos() as u64;
MASK_PROFILE.with(|p| {
let mut p = p.borrow_mut();
p.fallback_calls += 1;
p.fallback_ns += ns;
});
}
}
}
Ok(())
}
fn validate_logits_len(&self, logits: &[f32]) -> Result<(), GrammarError> {
if logits.len() < self.vocab_size {
return Err(GrammarError(format!(
"logits length {} is shorter than vocabulary size {}",
logits.len(),
self.vocab_size
)));
}
Ok(())
}
pub fn mask_by_simulation(
&self,
state: &GrammarState,
logits: &mut [f32],
) -> Result<(), GrammarError> {
self.validate_logits_len(logits)?;
for token_id in 0..self.vocab_size {
if logits[token_id] == f32::NEG_INFINITY {
continue;
}
let token_bytes = &self.vocab_bytes[token_id];
if token_bytes.is_empty() {
logits[token_id] = f32::NEG_INFINITY;
continue;
}
let (result, _) = simulate_token(state, &self.grammar, token_bytes);
if result != SimResult::Accept {
logits[token_id] = f32::NEG_INFINITY;
}
}
Ok(())
}
fn mask_by_trie(&self, state: &GrammarState, logits: &mut [f32]) {
let trie = self.trie.get_or_init(|| {
let t0 = std::time::Instant::now();
let built = ByteTrie::build(&self.vocab_bytes);
self.trie_build_ns
.store(t0.elapsed().as_nanos() as u64, Ordering::Relaxed);
built
});
trie.mask(state, &self.grammar, self.vocab_size, logits);
}
pub fn advance(&self, state: &mut GrammarState, token_id: u32) -> bool {
let profiling = mask_profiling_enabled();
let t0 = profiling.then(std::time::Instant::now);
let result = self.advance_inner(state, token_id);
if let Some(t0) = t0 {
let ns = t0.elapsed().as_nanos() as u64;
MASK_PROFILE.with(|p| {
let mut p = p.borrow_mut();
p.advance_calls += 1;
p.advance_ns += ns;
});
}
result
}
fn advance_inner(&self, state: &mut GrammarState, token_id: u32) -> bool {
let token_id = token_id as usize;
if token_id >= self.vocab_size {
return false;
}
let token_bytes = &self.vocab_bytes[token_id];
if token_bytes.is_empty() {
return true;
}
for &b in token_bytes {
if advance_byte(state, &self.grammar, b) == StepResult::Rejected {
return false;
}
}
true
}
pub(crate) fn is_complete_without_continuation(&self, state: &GrammarState) -> bool {
if !state.is_complete() {
return false;
}
let has_continuation = match self.find_state_id(state) {
Some(state_id) => self.partition.any_allowed_token(state_id, |token_id| {
simulate_token(state, &self.grammar, &self.vocab_bytes[token_id]).0
== SimResult::Accept
}),
None => self.vocab_bytes.iter().any(|token_bytes| {
!token_bytes.is_empty()
&& simulate_token(state, &self.grammar, token_bytes).0 == SimResult::Accept
}),
};
!has_continuation
}
fn find_state_id(&self, state: &GrammarState) -> Option<usize> {
for sid in 0..self.partition.num_states() {
if let Some(ps) = self.partition.grammar_state(sid)
&& ps.stack == state.stack
&& ps.complete == state.complete
{
return Some(sid);
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
fn tiny_vocab() -> Vec<Vec<u8>> {
vec![b"t".to_vec(), b"f".to_vec(), b"x".to_vec()]
}
fn bool_spec() -> GrammarSpec {
GrammarSpec::JsonSchema(serde_json::json!({"type": "boolean"}))
}
#[test]
fn engine_new_from_json_schema() {
let vocab = tiny_vocab();
let spec = bool_spec();
let result = GrammarEngine::new(&spec, vocab);
assert!(result.is_ok(), "engine construction should succeed");
}
#[test]
fn engine_new_from_gbnf() {
let vocab = tiny_vocab();
let spec = GrammarSpec::Gbnf("root ::= \"t\" | \"f\"\n".to_string());
let result = GrammarEngine::new(&spec, vocab);
assert!(result.is_ok());
}
#[test]
fn cyclic_gbnf_does_not_hang() {
let vocab = tiny_vocab();
let spec = GrammarSpec::Gbnf("root ::= root\n".to_string());
let result = GrammarEngine::new(&spec, vocab);
assert!(result.is_ok(), "cyclic GBNF should construct, not hang");
}
#[test]
fn cyclic_ref_schema_does_not_hang() {
let vocab = tiny_vocab();
let spec = GrammarSpec::JsonSchema(serde_json::json!({
"$ref": "#/$defs/Node",
"$defs": { "Node": { "$ref": "#/$defs/Node" } }
}));
let result = GrammarEngine::new(&spec, vocab);
assert!(
result.is_ok(),
"cyclic $ref schema should construct, not hang"
);
}
#[test]
fn array_maxitems_overflow_rejected() {
let vocab = tiny_vocab();
let spec = GrammarSpec::JsonSchema(serde_json::json!({
"type": "array",
"items": { "type": "boolean" },
"maxItems": 18446744073709551615u64
}));
let err = match GrammarEngine::new(&spec, vocab) {
Ok(_) => panic!("absurd maxItems must be rejected, not overflow the stack"),
Err(e) => e.to_string(),
};
assert!(
err.contains("maxItems"),
"error should name the offending field: {err}"
);
}
#[test]
fn array_minitems_overflow_rejected() {
let vocab = tiny_vocab();
let spec = GrammarSpec::JsonSchema(serde_json::json!({
"type": "array",
"items": { "type": "boolean" },
"minItems": 18446744073709551615u64
}));
let err = match GrammarEngine::new(&spec, vocab) {
Ok(_) => panic!("absurd minItems must be rejected, not hang"),
Err(e) => e.to_string(),
};
assert!(
err.contains("minItems"),
"error should name the offending field: {err}"
);
}
#[test]
fn deep_ref_chain_rejected() {
let n = 2000usize;
let mut defs = serde_json::Map::new();
for i in 0..n {
let target = if i + 1 < n {
serde_json::json!({ "$ref": format!("#/$defs/N{}", i + 1) })
} else {
serde_json::json!({ "type": "boolean" })
};
defs.insert(format!("N{i}"), target);
}
let schema = serde_json::json!({ "$ref": "#/$defs/N0", "$defs": defs });
let spec = GrammarSpec::JsonSchema(schema);
let err = match GrammarEngine::new(&spec, tiny_vocab()) {
Ok(_) => panic!("deep $ref chain must be rejected, not overflow the stack"),
Err(e) => e.to_string(),
};
assert!(err.contains("depth"), "error should mention depth: {err}");
}
#[test]
fn shallow_ref_chain_accepted() {
let n = 64usize;
let mut defs = serde_json::Map::new();
for i in 0..n {
let target = if i + 1 < n {
serde_json::json!({ "$ref": format!("#/$defs/N{}", i + 1) })
} else {
serde_json::json!({ "type": "boolean" })
};
defs.insert(format!("N{i}"), target);
}
let schema = serde_json::json!({ "$ref": "#/$defs/N0", "$defs": defs });
let result = GrammarEngine::new(&GrammarSpec::JsonSchema(schema), tiny_vocab());
assert!(result.is_ok(), "a 64-link $ref chain should compile");
}
#[test]
fn array_maxitems_boundary() {
let over = GrammarSpec::JsonSchema(serde_json::json!({
"type": "array", "items": { "type": "boolean" }, "maxItems": 4097
}));
assert!(
GrammarEngine::new(&over, tiny_vocab()).is_err(),
"maxItems just past the cap must be rejected"
);
let ok = GrammarSpec::JsonSchema(serde_json::json!({
"type": "array", "items": { "type": "boolean" }, "maxItems": 8
}));
assert!(
GrammarEngine::new(&ok, tiny_vocab()).is_ok(),
"a small in-range maxItems must still compile"
);
}
#[test]
fn mask_logits_blocks_disallowed() {
let vocab = vec![b"true".to_vec(), b"false".to_vec(), b"other".to_vec()];
let spec = GrammarSpec::JsonSchema(serde_json::json!({"type": "boolean"}));
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let mut state = engine.initial_state();
let mut logits = vec![1.0f32, 2.0f32, 3.0f32];
engine
.mask_logits(&mut state, &mut logits)
.expect("matching vocab length");
assert_eq!(
logits[2],
f32::NEG_INFINITY,
"token 'other' should be blocked"
);
assert!(
logits[0] > f32::NEG_INFINITY || logits[1] > f32::NEG_INFINITY,
"at least one of 'true'/'false' must be allowed"
);
}
#[test]
fn advance_updates_state() {
let vocab = vec![b"n".to_vec(), b"u".to_vec(), b"l".to_vec(), b"l".to_vec()];
let spec = GrammarSpec::JsonSchema(serde_json::json!({"type": "null"}));
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let mut state = engine.initial_state();
assert!(engine.advance(&mut state, 0));
}
#[test]
fn advance_rejects_wrong_token() {
let vocab = vec![b"x".to_vec(), b"n".to_vec()];
let spec = GrammarSpec::JsonSchema(serde_json::json!({"type": "null"}));
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let mut state = engine.initial_state();
let result = engine.advance(&mut state, 0);
assert!(!result, "'x' should be rejected for null grammar");
}
#[test]
fn initial_state_not_complete() {
let vocab = vec![b"t".to_vec()];
let spec = GrammarSpec::Gbnf("root ::= \"test\"\n".to_string());
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let state = engine.initial_state();
assert!(!state.is_complete(), "initial state should not be complete");
}
#[test]
fn nullable_initial_state_is_complete_without_continuation() {
let vocab = vec![b"a".to_vec()];
let spec = GrammarSpec::Gbnf("root ::= \"\"\n".to_string());
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let mut state = engine.initial_state();
assert!(state.is_complete());
assert!(engine.is_complete_without_continuation(&state));
let mut logits = vec![0.0];
enable_mask_profiling();
engine
.mask_logits(&mut state, &mut logits)
.expect("locally constructed engine and matching logits length must mask");
let profile = take_mask_profile();
assert_eq!(logits, vec![f32::NEG_INFINITY]);
assert_eq!(profile.precomputed_calls, 1);
assert_eq!(profile.fallback_calls, 0);
}
#[test]
fn nullable_initial_state_with_continuation_is_not_terminal() {
let vocab = vec![b"a".to_vec()];
let spec = GrammarSpec::Gbnf("root ::= \"a\"?\n".to_string());
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let mut state = engine.initial_state();
assert!(state.is_complete());
assert!(!engine.is_complete_without_continuation(&state));
let mut logits = vec![0.0];
enable_mask_profiling();
engine
.mask_logits(&mut state, &mut logits)
.expect("locally constructed engine and matching logits length must mask");
let profile = take_mask_profile();
assert_eq!(logits, vec![0.0]);
assert_eq!(profile.precomputed_calls, 1);
assert_eq!(profile.fallback_calls, 0);
}
#[test]
fn complete_state_without_continuation_is_terminal() {
let vocab = vec![b"a".to_vec()];
let spec = GrammarSpec::Gbnf("root ::= \"a\"\n".to_string());
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let mut state = engine.initial_state();
assert!(engine.advance(&mut state, 0));
assert!(engine.is_complete_without_continuation(&state));
}
#[test]
fn complete_state_with_continuation_is_not_terminal() {
let vocab = vec![b"a".to_vec()];
let spec = GrammarSpec::Gbnf("root ::= \"a\"+\n".to_string());
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let mut state = engine.initial_state();
assert!(engine.advance(&mut state, 0));
assert!(state.is_complete());
assert!(!engine.is_complete_without_continuation(&state));
assert!(engine.advance(&mut state, 0));
}
#[test]
fn engine_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<GrammarEngine>();
}
#[test]
fn mask_logits_logits_shorter_returns_error() {
let vocab = vec![b"a".to_vec(), b"b".to_vec()];
let spec = GrammarSpec::JsonSchema(serde_json::json!({"type": "null"}));
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let mut state = engine.initial_state();
let mut logits = vec![0.0f32];
let error = engine
.mask_logits(&mut state, &mut logits)
.expect_err("logits shorter than vocab_size must return an error");
assert!(error.0.contains("logits length 1"));
assert!(error.0.contains("vocabulary size 2"));
let simulation_error = engine
.mask_by_simulation(&state, &mut logits)
.expect_err("the public simulation path must reject the same mismatch");
assert!(simulation_error.0.contains("logits length 1"));
assert!(simulation_error.0.contains("vocabulary size 2"));
}
#[test]
fn mask_logits_rechecks_only_the_current_states_candidates() {
let spec = GrammarSpec::Gbnf("root ::= \"abcd\"\n".to_string());
let vocab = vec![
b"a".to_vec(),
b"b".to_vec(),
b"c".to_vec(),
b"d".to_vec(),
b"ax".to_vec(),
b"bx".to_vec(),
b"cx".to_vec(),
b"dx".to_vec(),
];
let engine = GrammarEngine::new(&spec, vocab).expect("fixture grammar must compile");
let mut state = engine.initial_state();
assert!(engine.advance(&mut state, 0), "token 'a' must advance");
reset_context_recheck_candidates_for_test();
let mut actual = vec![0.0; 8];
engine
.mask_logits(&mut state, &mut actual)
.expect("fixture logits match the vocabulary");
assert_eq!(
take_context_recheck_candidates_for_test(),
1,
"state after 'a' must inspect only the 'bx' partial token"
);
let mut oracle = vec![0.0; 8];
engine
.mask_by_simulation(&state, &mut oracle)
.expect("fixture logits match the vocabulary");
assert_eq!(actual, oracle);
}
#[test]
fn unknown_state_fallback_is_sound() {
use crate::grammar::pda::{CompiledGrammar, Rule, Symbol};
let mut chain = vec![Symbol::Terminal(b'"')];
chain.extend(std::iter::repeat_n(Symbol::Terminal(b'a'), 300));
chain.push(Symbol::Terminal(b'"'));
let grammar = CompiledGrammar {
rules: vec![Rule {
name: "root".to_string(),
alts: vec![chain],
}],
};
let vocab = vec![b"\"".to_vec(), b"a".to_vec()];
let spec = GrammarSpec::Gbnf("root ::= \"placeholder\"\n".to_string());
let mut engine = GrammarEngine::new(&spec, vocab.clone()).unwrap();
let states = enumerate_grammar_states(
&grammar,
&vocab,
crate::grammar::vocab_partition::MAX_GRAMMAR_STATES,
);
assert_eq!(
states.len(),
crate::grammar::vocab_partition::MAX_GRAMMAR_STATES,
"grammar must exceed the state cap for this regression to bite"
);
engine.partition = VocabPartition::build(&grammar, states, &vocab);
engine.grammar = grammar;
let mut state = engine.initial_state();
assert!(engine.advance(&mut state, 0), "opening quote accepted");
for _ in 0..270 {
assert!(engine.advance(&mut state, 1), "mid-chain 'a' accepted");
}
assert!(
engine.find_state_id(&state).is_none(),
"deep state must be unknown to the capped partition (else the \
simulation fallback is not the path under test)"
);
let mut logits = vec![1.0f32, 1.0f32];
engine
.mask_logits(&mut state, &mut logits)
.expect("matching vocab length");
assert!(
logits[1] > f32::NEG_INFINITY,
"valid mid-chain token 'a' must remain allowed at the deep state"
);
assert_eq!(
logits[0],
f32::NEG_INFINITY,
"invalid closing-quote token must be blocked at the deep state \
(state-0 fallback would wrongly allow it)"
);
}
#[test]
fn bitmask_and_correctness_large_vocab() {
let mut vocab: Vec<Vec<u8>> = vec![b"true".to_vec(), b"false".to_vec()];
for i in 2..130 {
vocab.push(format!("tok{i}").into_bytes());
}
let spec = GrammarSpec::JsonSchema(serde_json::json!({"type": "boolean"}));
let engine = GrammarEngine::new(&spec, vocab).unwrap();
let mut state = engine.initial_state();
let mut logits = vec![1.0f32; 130];
engine
.mask_logits(&mut state, &mut logits)
.expect("matching vocab length");
assert!(
logits[0] > f32::NEG_INFINITY,
"token 'true' should be allowed"
);
assert!(
logits[1] > f32::NEG_INFINITY,
"token 'false' should be allowed"
);
for i in 2..130 {
assert_eq!(logits[i], f32::NEG_INFINITY, "token {i} should be blocked");
}
}
fn trie_diff_schema() -> serde_json::Value {
serde_json::json!({
"type": "object",
"properties": {
"level1": {
"type": "object",
"properties": {
"level2": {
"type": "object",
"properties": {
"level3": {
"type": "object",
"properties": {
"level4": {
"type": "object",
"properties": {
"status": {"type": "string", "enum": ["active", "inactive", "pending", "archived", "deleted", "draft"]},
"value": {"type": "integer"}
},
"required": ["status", "value"]
}
},
"required": ["level4"]
},
"category": {"type": "string", "enum": ["alpha", "beta", "gamma", "delta", "epsilon", "zeta"]}
},
"required": ["level3", "category"]
},
"tags": {"type": "array", "items": {"type": "string"}}
},
"required": ["level2", "tags"]
},
"items": {"type": "array", "items": {"type": "integer"}},
"flags": {"type": "array", "items": {"type": "boolean"}},
"priority": {"type": "string", "enum": ["low", "medium", "high", "urgent", "critical", "none"]},
"region": {"type": "string", "enum": ["us", "eu", "apac", "latam", "mea", "other"]},
"mode": {"type": "string", "enum": ["sync", "async", "batch", "stream", "manual", "auto"]},
"role": {"type": "string", "enum": ["admin", "user", "guest", "owner", "viewer", "editor"]}
},
"required": ["level1", "items", "flags", "priority", "region", "mode", "role"]
})
}
fn trie_diff_vocab() -> Vec<Vec<u8>> {
let mut vocab: Vec<Vec<u8>> = (0u16..256).map(|b| vec![b as u8]).collect();
let fragments: &[&str] = &[
"\"level1\"",
"\"level2\"",
"\"level3\"",
"\"level4\"",
"\"status\"",
"\"value\"",
"\"category\"",
"\"tags\"",
"\"items\"",
"\"flags\"",
"\"priority\"",
"\"region\"",
"\"mode\"",
"\"role\"",
"\"active\"",
"\"inactive\"",
"\"pending\"",
"\"archived\"",
"\"deleted\"",
"\"draft\"",
"\"alpha\"",
"\"beta\"",
"\"gamma\"",
"\"delta\"",
"\"epsilon\"",
"\"zeta\"",
"\"low\"",
"\"medium\"",
"\"high\"",
"\"urgent\"",
"\"critical\"",
"\"none\"",
"\"us\"",
"\"eu\"",
"\"apac\"",
"\"latam\"",
"\"mea\"",
"\"other\"",
"\"sync\"",
"\"async\"",
"\"batch\"",
"\"stream\"",
"\"manual\"",
"\"auto\"",
"\"admin\"",
"\"user\"",
"\"guest\"",
"\"owner\"",
"\"viewer\"",
"\"editor\"",
"true",
"false",
"null",
];
for f in fragments {
vocab.push(f.as_bytes().to_vec());
}
vocab
}
const TRIE_DIFF_INSTANCE: &str = r#"{"flags":[true,false],"items":[1,2,3],"level1":{"level2":{"category":"alpha","level3":{"level4":{"status":"active","value":42}}},"tags":["x","y"]},"mode":"sync","priority":"low","region":"us","role":"admin"}"#;
fn harvest_trajectory_states(engine: &GrammarEngine) -> Vec<GrammarState> {
let mut state = engine.initial_state();
let mut corpus = vec![state.clone()];
for (i, &b) in TRIE_DIFF_INSTANCE.as_bytes().iter().enumerate() {
let step = advance_byte(&mut state, &engine.grammar, b);
assert_eq!(
step,
StepResult::Accepted,
"trajectory instance rejected at byte {i} ({:?}); fixture is out of \
sync with the schema",
b as char
);
if !corpus.iter().any(|s| states_equal(s, &state)) {
corpus.push(state.clone());
}
}
assert!(
state.is_complete(),
"trajectory instance must fully complete the grammar"
);
corpus
}
#[test]
fn trie_mask_byte_identical_to_oracle_over_corpus() {
let vocab = trie_diff_vocab();
let spec = GrammarSpec::JsonSchema(trie_diff_schema());
let engine = GrammarEngine::new(&spec, vocab.clone()).unwrap();
assert!(
engine.exceeds_state_budget(),
"fixture must exceed the state cap for this differential to exercise \
the fallback path (the point of this test)"
);
let corpus = harvest_trajectory_states(&engine);
assert!(
corpus.len() >= 20,
"need at least 20 distinct harvested states, got {}",
corpus.len()
);
let mut over_cap_states = 0usize;
for (i, state) in corpus.iter().enumerate() {
let mut oracle_logits = vec![0.0f32; vocab.len()];
let mut trie_logits = vec![0.0f32; vocab.len()];
engine
.mask_by_simulation(state, &mut oracle_logits)
.expect("matching vocab length");
engine.mask_by_trie(state, &mut trie_logits);
if engine.find_state_id(state).is_none() {
over_cap_states += 1;
}
for tok in 0..vocab.len() {
assert_eq!(
trie_logits[tok],
oracle_logits[tok],
"state #{i}: token {tok} ({:?}) mismatch — trie={} oracle={}",
String::from_utf8_lossy(&vocab[tok]),
trie_logits[tok],
oracle_logits[tok]
);
}
}
assert!(
over_cap_states >= 15,
"corpus should mostly cover the over-cap fallback path (the profiling \
report measured a 98% fallback rate on this schema shape); got \
{over_cap_states}/{}",
corpus.len()
);
}
#[test]
fn trie_mask_never_over_accepts_vs_oracle() {
let vocab = trie_diff_vocab();
let spec = GrammarSpec::JsonSchema(trie_diff_schema());
let engine = GrammarEngine::new(&spec, vocab.clone()).unwrap();
let corpus = harvest_trajectory_states(&engine);
let mut over_accepts: Vec<(usize, Vec<u8>)> = Vec::new();
for state in &corpus {
let mut oracle_logits = vec![0.0f32; vocab.len()];
let mut trie_logits = vec![0.0f32; vocab.len()];
engine
.mask_by_simulation(state, &mut oracle_logits)
.expect("matching vocab length");
engine.mask_by_trie(state, &mut trie_logits);
for tok in 0..vocab.len() {
let oracle_blocked = oracle_logits[tok] == f32::NEG_INFINITY;
let trie_allowed = trie_logits[tok] != f32::NEG_INFINITY;
if oracle_blocked && trie_allowed {
over_accepts.push((tok, vocab[tok].clone()));
}
}
}
assert!(
over_accepts.is_empty(),
"trie over-accepted {} token(s) the oracle rejects (P0 soundness \
violation): {:?}",
over_accepts.len(),
over_accepts
.iter()
.take(5)
.map(|(id, bytes)| (id, String::from_utf8_lossy(bytes).to_string()))
.collect::<Vec<_>>()
);
}
#[test]
fn trie_routes_over_cap_states_through_mask_logits() {
let vocab = trie_diff_vocab();
let spec = GrammarSpec::JsonSchema(trie_diff_schema());
let engine = GrammarEngine::new(&spec, vocab.clone()).unwrap();
let corpus = harvest_trajectory_states(&engine);
let deep_state = corpus
.iter()
.find(|s| engine.find_state_id(s).is_none())
.expect("corpus must contain at least one over-cap state");
let mut via_mask_logits = vec![0.0f32; vocab.len()];
let mut oracle_logits = vec![0.0f32; vocab.len()];
let mut state_for_public_call = deep_state.clone();
engine
.mask_logits(&mut state_for_public_call, &mut via_mask_logits)
.expect("matching vocab length");
engine
.mask_by_simulation(deep_state, &mut oracle_logits)
.expect("matching vocab length");
assert_eq!(
via_mask_logits, oracle_logits,
"mask_logits on an over-cap state must match the oracle mask exactly"
);
}
#[test]
#[ignore = "loads a real tokenizer + full vocab oracle simulation; run with `real_vocab` filter"]
fn trie_mask_byte_identical_to_oracle_real_vocab() {
let home = std::env::var("HOME").expect("HOME must be set");
let tokenizer_dir_str = std::env::var("LATTICE_TOKENIZER_DIR")
.unwrap_or_else(|_| format!("{home}/.lattice/models/qwen3.5-0.8b"));
let tokenizer_dir = std::path::Path::new(&tokenizer_dir_str);
let config_path = tokenizer_dir.join("config.json");
let tokenizer_path = tokenizer_dir.join("tokenizer.json");
if !config_path.exists() || !tokenizer_path.exists() {
panic!(
"real-vocab differential test skipped: no model checkout at \
{tokenizer_dir_str} (expected config.json + tokenizer.json); \
set LATTICE_TOKENIZER_DIR to point at one"
);
}
let cfg = crate::model::qwen35_config::Qwen35Config::from_model_dir(tokenizer_dir)
.expect("config.json load");
let tokenizer = crate::tokenizer::BpeTokenizer::from_tokenizer_json(&tokenizer_path)
.expect("tokenizer.json load");
let vocab = tokenizer
.vocab_bytes(cfg.vocab_size)
.expect("vocab_bytes over real tokenizer");
assert!(
vocab.len() > 100_000,
"expected a production-scale vocab (~248K tokens for Qwen3.5), got {}",
vocab.len()
);
let spec = GrammarSpec::JsonSchema(trie_diff_schema());
let engine = GrammarEngine::new(&spec, vocab.clone()).unwrap();
assert!(
engine.exceeds_state_budget(),
"fixture must exceed the state cap for this differential to exercise \
the fallback path (the point of this test) — got a real vocab of {} \
tokens",
vocab.len()
);
let corpus = harvest_trajectory_states(&engine);
assert!(
corpus.len() >= 20,
"need at least 20 distinct harvested states, got {}",
corpus.len()
);
let mut over_cap_states = 0usize;
let mut mismatches: Vec<(usize, usize)> = Vec::new();
let mut over_accepts: Vec<(usize, usize)> = Vec::new();
for (i, state) in corpus.iter().enumerate() {
let mut oracle_logits = vec![0.0f32; vocab.len()];
let mut trie_logits = vec![0.0f32; vocab.len()];
engine
.mask_by_simulation(state, &mut oracle_logits)
.expect("matching vocab length");
engine.mask_by_trie(state, &mut trie_logits);
if engine.find_state_id(state).is_none() {
over_cap_states += 1;
}
for tok in 0..vocab.len() {
if trie_logits[tok] != oracle_logits[tok] {
mismatches.push((i, tok));
}
let oracle_blocked = oracle_logits[tok] == f32::NEG_INFINITY;
let trie_allowed = trie_logits[tok] != f32::NEG_INFINITY;
if oracle_blocked && trie_allowed {
over_accepts.push((i, tok));
}
}
}
assert!(
over_cap_states >= 10,
"corpus should mostly cover the over-cap fallback path; got \
{over_cap_states}/{}",
corpus.len()
);
assert!(
over_accepts.is_empty(),
"trie over-accepted {} token(s) the oracle rejects on the real \
vocab (P0 soundness violation): {:?}",
over_accepts.len(),
over_accepts
.iter()
.take(5)
.map(|(state_idx, tok)| (
state_idx,
tok,
String::from_utf8_lossy(&vocab[*tok]).to_string()
))
.collect::<Vec<_>>()
);
assert!(
mismatches.is_empty(),
"trie mask diverged from oracle mask on {} (state, token) pair(s) \
over the real vocab: {:?}",
mismatches.len(),
mismatches
.iter()
.take(5)
.map(|(state_idx, tok)| (
state_idx,
tok,
String::from_utf8_lossy(&vocab[*tok]).to_string()
))
.collect::<Vec<_>>()
);
eprintln!(
"real_vocab differential: vocab_size={} corpus_states={} \
over_cap_states={over_cap_states} over_accepts=0 mismatches=0",
vocab.len(),
corpus.len(),
);
}
}