use clap::Parser;
use super::*;
fn drafter(dir: &std::path::Path) {
std::fs::write(
dir.join("config.json"),
r#"{"hidden_size": 5120, "num_hidden_layers": 5, "num_attention_heads": 32,
"num_key_value_heads": 8, "intermediate_size": 17408, "vocab_size": 248320,
"head_dim": 128,
"dflash_config": {"block_size": 8, "mask_token_id": 248070,
"target_layer_ids": [1, 10, 19, 28, 37]}}"#,
)
.unwrap();
}
fn args(extra: &[&str]) -> cli::ServeArgs {
let mut argv = vec!["serve", "some/model"];
argv.extend_from_slice(extra);
cli::ServeArgs::try_parse_from(argv).unwrap()
}
#[test]
fn a_pinned_gamma_wins_and_an_unpinned_one_is_the_drafters_block_plus_two() {
let dir = tempfile::tempdir().unwrap();
drafter(dir.path());
let d = dir.path().to_str().unwrap();
let mut pinned = args(&["--dflash", "--draft-model", d, "--dflash-gamma", "8"]);
apply_dflash_gamma(&mut pinned, None).unwrap();
assert_eq!(
pinned.serve_dflash_gamma(),
8,
"--dflash-gamma 8 is what the build sizes"
);
assert_eq!(pinned.dflash_gamma, Some(8), "the flag itself is untouched");
let mut free = args(&["--dflash", "--draft-model", d]);
apply_dflash_gamma(&mut free, None).unwrap();
assert_eq!(free.serve_dflash_gamma(), 10, "block 8 + 2");
assert_eq!(
free.dflash_gamma, None,
"an unpinned γ stays unpinned for the rung"
);
let cfg = read_dflash_config(dir.path()).unwrap();
for (flag, want) in [(Some(8), 8), (None, 10)] {
assert_eq!(
metrale_model_layers::layers::qwen3_ssm::resolve_dflash_gamma(
flag,
Some(cfg.effective_block_size())
),
want
);
}
}
#[test]
fn without_dflash_nothing_is_resolved() {
let mut a = args(&[]);
apply_dflash_gamma(&mut a, None).unwrap();
assert_eq!(a.dflash_gamma_resolved, None);
}
#[test]
fn a_draft_ladder_is_held_to_the_serves_gamma() {
let dir = tempfile::tempdir().unwrap();
drafter(dir.path());
let d = dir.path().to_str().unwrap();
let ladder = "1:7,2:5,3:4,4:3,5:2,6:7";
let mut ok = args(&[
"--dflash",
"--draft-model",
d,
"--dflash-gamma",
"8",
"--dflash-draft-ladder",
ladder,
]);
apply_dflash_gamma(&mut ok, None).unwrap();
assert_eq!(
ok.dflash_draft_ladder_resolved,
Some(metrale_speculative::dflash_ladder::DraftLadder::parse(ladder).unwrap())
);
let mut wide = args(&[
"--dflash",
"--draft-model",
d,
"--dflash-gamma",
"7",
"--dflash-draft-ladder",
ladder,
]);
assert!(apply_dflash_gamma(&mut wide, None).is_err());
}
#[test]
fn an_adaptive_k_table_is_held_to_the_serves_gamma() {
let dir = tempfile::tempdir().unwrap();
drafter(dir.path());
let d = dir.path().to_str().unwrap();
let table = "0:32,1:44,2:50,3:56";
let base = ["--dflash", "--draft-model", d, "--dflash-gamma"];
let mut ok = args(&[&base[..], &["4", "--dflash-adaptive-k", table]].concat());
apply_dflash_gamma(&mut ok, None).unwrap();
assert_eq!(
ok.dflash_args.dflash_adaptive_k_resolved,
Some(metrale_speculative::dflash_adaptive_k::CostModel::parse(table).unwrap())
);
let mut wide = args(&[&base[..], &["3", "--dflash-adaptive-k", table]].concat());
assert!(apply_dflash_gamma(&mut wide, None).is_err());
let mut bad = args(&[&base[..], &["4", "--dflash-adaptive-k", "0:32,2:50"]].concat());
assert!(apply_dflash_gamma(&mut bad, None).is_err());
assert!(
cli::ServeArgs::try_parse_from(["serve", "some/model", "--dflash-adaptive-k", table])
.is_err()
);
}
#[test]
fn draft_tp_size_resolves_and_refuses() {
use cli::serve_args_dflash::{DraftTpSize, parse_draft_tp_size};
let glm = || {
metrale_model_arch::weight_loader::dflash_loader::parse_dflash_config(
r#"{"hidden_size": 4096, "num_hidden_layers": 5, "num_attention_heads": 32,
"num_key_value_heads": 8, "intermediate_size": 12288, "vocab_size": 154880,
"head_dim": 128}"#,
)
};
assert_eq!(resolve_draft_tp_size(None, 3, glm).unwrap(), 1);
assert_eq!(
resolve_draft_tp_size(Some(DraftTpSize::Auto), 3, glm).unwrap(),
3
);
assert_eq!(
resolve_draft_tp_size(Some(DraftTpSize::Ranks(3)), 3, glm).unwrap(),
3
);
assert_eq!(
resolve_draft_tp_size(Some(DraftTpSize::Ranks(1)), 3, glm).unwrap(),
1
);
for n in [2, 4] {
assert!(resolve_draft_tp_size(Some(DraftTpSize::Ranks(n)), 3, glm).is_err());
}
assert!(resolve_draft_tp_size(Some(DraftTpSize::Auto), 9, glm).is_err());
assert_eq!(parse_draft_tp_size("auto"), Ok(DraftTpSize::Auto));
assert_eq!(parse_draft_tp_size("3"), Ok(DraftTpSize::Ranks(3)));
for bad in ["0", "-1", "", "two", "3.0"] {
assert!(parse_draft_tp_size(bad).is_err(), "{bad:?} accepted");
}
assert!(
cli::ServeArgs::try_parse_from(["serve", "some/model", "--draft-tp-size", "3"]).is_err(),
"--draft-tp-size without --dflash"
);
assert!(
cli::ServeArgs::try_parse_from(["serve", "some/model", "--dflash", "--draft-tp-size", "0"])
.is_err()
);
}
#[test]
fn draft_weight_quantization_parses_every_tier_and_refuses_junk() {
use metrale_model_arch::dflash_head::DraftWeightQuantization as Q;
let parse = |extra: &[&str]| {
let mut argv = vec!["serve", "some/model", "--dflash"];
argv.extend_from_slice(extra);
cli::ServeArgs::try_parse_from(argv)
};
assert_eq!(
parse(&[]).unwrap().dflash_args.draft_weight_quantization,
Q::Fp8
);
for t in Q::ALL {
assert_eq!(
parse(&["--draft-weight-quantization", t.name()])
.unwrap()
.dflash_args
.draft_weight_quantization,
t
);
}
for bad in ["", "FP8", "fp4", "bf16", "nvfp4-gate-up"] {
assert!(
parse(&["--draft-weight-quantization", bad]).is_err(),
"{bad:?} accepted"
);
}
assert!(
cli::ServeArgs::try_parse_from([
"serve",
"some/model",
"--draft-weight-quantization",
"nvfp4"
])
.is_err(),
"--draft-weight-quantization without --dflash"
);
}