mod common;
use modelc::generate::{GenerationConfig, generate_token_ids, generate_token_ids_with_cache};
use modelc::prefix_cache::PrefixCache;
use modelc::runtime::serve::Runtime;
use modelc::tokenizer::BpeTokenizer;
fn config(max_tokens: usize) -> GenerationConfig {
GenerationConfig {
max_tokens,
temperature: 0.0,
..GenerationConfig::default()
}
}
#[test]
fn cache_is_transparent_for_identical_prompt() {
let model = common::create_gpt2_test_model();
let runtime = Runtime::from_raw(&model.tensors);
let hidden = model
.metadata
.get("hidden")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(12);
let tokenizer = BpeTokenizer::byte_fallback();
let prompt = "system: you are helpful. user: hello";
let cfg = config(8);
let baseline = generate_token_ids(&runtime, "gpt2", hidden, &tokenizer, prompt, &cfg, None);
let mut cache = PrefixCache::new(8);
let cold = generate_token_ids_with_cache(
&runtime,
"gpt2",
hidden,
&tokenizer,
prompt,
&cfg,
Some(&mut cache),
None,
);
assert_eq!(
cold, baseline,
"cold (miss) output must match the no-cache baseline"
);
assert_eq!(cache.len(), 1, "cold call should populate the cache");
let warm = generate_token_ids_with_cache(
&runtime,
"gpt2",
hidden,
&tokenizer,
prompt,
&cfg,
Some(&mut cache),
None,
);
assert_eq!(
warm, baseline,
"warm (hit) output must match the no-cache baseline"
);
assert_eq!(cache.len(), 1, "exact repeat should not grow the cache");
}
#[test]
fn cache_reuses_shared_prefix() {
let model = common::create_gpt2_test_model();
let runtime = Runtime::from_raw(&model.tensors);
let hidden = model
.metadata
.get("hidden")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(12);
let tokenizer = BpeTokenizer::byte_fallback();
let cfg = config(6);
let prompt_a = "system: you are helpful.";
let prompt_b = "system: you are helpful. user: what is 2+2?";
let baseline_b = generate_token_ids(&runtime, "gpt2", hidden, &tokenizer, prompt_b, &cfg, None);
let mut cache = PrefixCache::new(8);
let _ = generate_token_ids_with_cache(
&runtime,
"gpt2",
hidden,
&tokenizer,
prompt_a,
&cfg,
Some(&mut cache),
None,
);
assert_eq!(cache.len(), 1);
let b_cached = generate_token_ids_with_cache(
&runtime,
"gpt2",
hidden,
&tokenizer,
prompt_b,
&cfg,
Some(&mut cache),
None,
);
assert_eq!(
b_cached, baseline_b,
"prefix-reuse output must match the cold baseline for the same prompt"
);
assert_eq!(cache.len(), 2);
}
#[test]
fn cache_ignores_non_prefix_entries() {
let model = common::create_gpt2_test_model();
let runtime = Runtime::from_raw(&model.tensors);
let hidden = model
.metadata
.get("hidden")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(12);
let tokenizer = BpeTokenizer::byte_fallback();
let cfg = config(4);
let unrelated = "completely different prompt xyz";
let baseline = generate_token_ids(&runtime, "gpt2", hidden, &tokenizer, unrelated, &cfg, None);
let mut cache = PrefixCache::new(8);
let _ = generate_token_ids_with_cache(
&runtime,
"gpt2",
hidden,
&tokenizer,
"system prompt one",
&cfg,
Some(&mut cache),
None,
);
let got = generate_token_ids_with_cache(
&runtime,
"gpt2",
hidden,
&tokenizer,
unrelated,
&cfg,
Some(&mut cache),
None,
);
assert_eq!(
got, baseline,
"unrelated prompt must produce its cold baseline"
);
assert_eq!(cache.len(), 2);
}
#[test]
fn neural_draft_model_speculative_matches_non_speculative() {
let model = common::create_gpt2_test_model();
let runtime = Runtime::from_raw(&model.tensors);
let hidden = model
.metadata
.get("hidden")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(12);
let tokenizer = BpeTokenizer::byte_fallback();
let baseline_cfg = config(6);
let baseline = generate_token_ids(
&runtime,
"gpt2",
hidden,
&tokenizer,
"hello",
&baseline_cfg,
None,
);
let draft = modelc::draft::MlpDraftModel::from_runtime(
&runtime,
tokenizer.vocab_size(),
hidden,
2,
0.0,
0.0,
);
let spec_cfg = GenerationConfig {
gamma: 3,
..baseline_cfg
};
let spec = generate_token_ids_with_cache(
&runtime,
"gpt2",
hidden,
&tokenizer,
"hello",
&spec_cfg,
None,
draft.as_ref().map(|d| d as &dyn modelc::draft::DraftModel),
);
assert_eq!(
spec, baseline,
"speculative with draft must match non-speculative baseline"
);
}
#[test]
fn cancellation_flag_stops_generation() {
let model = common::create_gpt2_test_model();
let runtime = Runtime::from_raw(&model.tensors);
let hidden = model
.metadata
.get("hidden")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(12);
let tokenizer = BpeTokenizer::byte_fallback();
let prompt = "system: you are helpful. user: hello";
let cfg = config(8);
let baseline = generate_token_ids(&runtime, "gpt2", hidden, &tokenizer, prompt, &cfg, None);
assert!(
!baseline.is_empty(),
"uncancelled generation should produce tokens"
);
let cancel = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
let cancelled_cfg = GenerationConfig {
cancel: Some(cancel),
..config(8)
};
let ids = generate_token_ids(
&runtime,
"gpt2",
hidden,
&tokenizer,
prompt,
&cancelled_cfg,
None,
);
assert!(
ids.is_empty(),
"cancelled generation should produce no tokens, got {}",
ids.len()
);
}