use super::*;
use clap::Parser;
fn parse(extra: &[&str]) -> ServeArgs {
let mut argv = vec!["met", "serve", "dummy/model", "--model-name", "dummy"];
if !extra.contains(&"--activation-quantization") {
argv.extend_from_slice(&["--activation-quantization", "adaptive"]);
}
argv.extend_from_slice(extra);
match super::super::Cli::parse_from(argv).command {
super::super::Command::Serve(a) => a,
super::super::Command::Benchmark(_)
| super::super::Command::DumpServeOptions
| super::super::Command::SyncRecipes
| super::super::Command::Doctor
| super::super::Command::Circuit(_) => {
unreachable!("this test parses a serve command")
}
}
}
#[test]
fn flagship_recipe_is_accepted() {
assert!(
validate_serve_args(&parse(&[
"--kv-cache-dtype",
"bf16",
"--lm-head-dtype",
"nvfp4",
"--kv-high-precision-layers",
"auto",
"--scheduler",
"slai",
"--speculative",
"--num-drafts",
"1",
"--mtp-quantization",
"bf16",
"--enable-prefix-caching",
]))
.is_ok()
);
}
#[test]
fn enum_typos_are_rejected() {
let err = validate_serve_args(&parse(&["--scheduler", "fifoo"])).unwrap_err();
assert!(err.contains("--scheduler"));
assert!(err.contains("fifo, slai"));
}
#[test]
fn multiple_violations_all_reported() {
let err = validate_serve_args(&parse(&[
"--require-auth",
"--num-drafts",
"3",
"--rank",
"5",
"--world-size",
"2",
]))
.unwrap_err();
assert!(err.contains("[1]"));
assert!(err.contains("[2]"));
assert!(err.contains("[3]"));
}
#[test]
fn gpu_mem_util_range_enforced() {
assert!(validate_serve_args(&parse(&["--gpu-memory-utilization", "1.5"])).is_err());
assert!(validate_serve_args(&parse(&["--gpu-memory-utilization", "0.0"])).is_err());
assert!(validate_serve_args(&parse(&["--gpu-memory-utilization", "0.9"])).is_ok());
}
#[test]
fn model_toml_backed_flags_distinguish_omitted_from_explicit() {
let omitted = parse(&[]);
assert_eq!(omitted.num_drafts, None);
assert_eq!(omitted.kv_cache_dtype, None);
assert_eq!(omitted.fp8_kv_calibration_tokens, None);
let explicit = parse(&[
"--num-drafts",
"1",
"--kv-cache-dtype",
"fp8",
"--fp8-kv-calibration-tokens",
"0",
]);
assert_eq!(explicit.num_drafts, Some(1));
assert_eq!(explicit.kv_cache_dtype.as_deref(), Some("fp8"));
assert_eq!(explicit.fp8_kv_calibration_tokens, Some(0));
}
#[test]
fn f16_pool_is_a_published_dtype_and_inherits_every_f16_rule() {
use metrale_model_layers::layers::qwen3_ssm::ssm_h_dtype_bits;
assert_eq!(ssm_h_dtype_bits(None), (false, false));
assert_eq!(ssm_h_dtype_bits(Some("f32")), (false, false));
assert_eq!(ssm_h_dtype_bits(Some("f16")), (true, false));
assert_eq!(ssm_h_dtype_bits(Some("f16-pool")), (true, true));
assert!(
validate_serve_args(&parse(&["--ssm-h-dtype", "f16-pool", "--gdn-fused-norm"])).is_ok(),
"the supported stage-3 pairing"
);
assert!(validate_serve_args(&parse(&["--ssm-h-dtype", "f16-pool"])).is_err());
assert!(
validate_serve_args(&parse(&[
"--exact-verify",
"--ssm-h-dtype",
"f16-pool",
"--gdn-fused-norm",
]))
.is_ok()
);
assert!(validate_serve_args(&parse(&["--ssm-h-dtype", "f16pool"])).is_err());
assert_eq!(ssm_h_dtype_bits(Some("f16pool")), (false, false));
}
#[test]
fn dflash_refuses_the_f16_h_state_only_at_the_untwinned_width() {
for dtype in ["f16", "f16-pool"] {
let err = validate_serve_args(&parse(&[
"--dflash",
"--dflash-gamma",
"16",
"--ssm-h-dtype",
dtype,
"--gdn-fused-norm",
]))
.unwrap_err();
assert!(err.contains("dflash-gamma"), "{dtype}: {err}");
assert!(
validate_serve_args(&parse(&[
"--dflash",
"--dflash-gamma",
"10",
"--ssm-h-dtype",
dtype,
"--gdn-fused-norm",
]))
.is_ok(),
"{dtype}: gamma 10 (width 11) has a twin and must be allowed"
);
}
assert!(validate_serve_args(&parse(&["--dflash"])).is_ok());
assert!(validate_serve_args(&parse(&["--ssm-h-dtype", "f16", "--gdn-fused-norm"])).is_ok());
assert!(
validate_serve_args(&parse(&["--dflash", "--ssm-h-dtype", "f32"])).is_ok(),
"the FP32 h-state has always been DFlash's supported pairing"
);
}
fn f16_dflash(gamma: Option<&str>) -> ServeArgs {
let mut argv = vec![
"--ssm-h-dtype",
"f16-pool",
"--gdn-fused-norm",
"--dflash",
"--draft-model",
"some/drafter",
];
if let Some(g) = gamma {
argv.push("--dflash-gamma");
argv.push(g);
}
parse(&argv)
}
#[test]
fn dflash_under_the_f16_pool_is_allowed_up_to_the_last_twinned_width() {
for g in ["4", "8", "10", "15"] {
assert!(
validate_serve_args(&f16_dflash(Some(g))).is_ok(),
"gamma {g} (verify width {}) has an FP16 twin and must be allowed",
g.parse::<usize>().unwrap() + 1
);
}
}
#[test]
fn dflash_gamma_16_is_refused_because_width_17_has_no_twin() {
let v = validate_serve_args(&f16_dflash(Some("16")));
let msg = format!(
"{:#}",
v.expect_err("gamma 16 is verify width 17 — no FP16 twin")
);
assert!(
msg.contains("f16") && msg.contains("dflash-gamma"),
"the refusal must name both halves of the incompatible pair: {msg}"
);
}
#[test]
fn an_unset_gamma_is_left_to_the_runtime_backstop() {
assert!(validate_serve_args(&f16_dflash(None)).is_ok());
}
#[test]
fn hermetic_closes_both_channels_through_the_resolvers() {
let a = parse(&["--hermetic"]);
assert!(
!a.prefix_caching_enabled(),
"--hermetic must close the radix KV prefix cache"
);
assert_eq!(
a.mtp_gate_force(),
Some(true),
"--hermetic must PIN the MTP gate rather than leave it to the environment"
);
}
#[test]
fn without_hermetic_neither_channel_is_touched() {
let a = parse(&[]);
assert!(!a.prefix_caching_enabled(), "the flag's own default is off");
assert_eq!(
a.mtp_gate_force(),
None,
"absent means the documented METRALE_MTP_GATE_FORCE fallback decides"
);
let on = parse(&["--enable-prefix-caching"]);
assert!(
on.prefix_caching_enabled(),
"asking for the cache without --hermetic must still get it"
);
}
#[test]
fn hermetic_beside_a_flag_it_closes_is_refused() {
let err = validate_serve_args(&parse(&["--hermetic", "--enable-prefix-caching"])).unwrap_err();
assert!(err.contains("--hermetic"), "{err}");
assert!(err.contains("--enable-prefix-caching"), "{err}");
assert!(
err.contains("known-answer test"),
"the message must say WHY, not just that: {err}"
);
let err = validate_serve_args(&parse(&["--hermetic", "--mtp-gate", "auto"])).unwrap_err();
assert!(err.contains("--mtp-gate auto"), "{err}");
}
#[test]
fn hermetic_beside_an_agreeing_flag_is_accepted() {
assert!(validate_serve_args(&parse(&["--hermetic", "--mtp-gate", "force"])).is_ok());
assert!(validate_serve_args(&parse(&["--hermetic"])).is_ok());
}
#[test]
fn the_serve_override_spelling_of_hermetic_round_trips() {
use crate::recipe::schema::argv_for;
assert_eq!(
argv_for("hermetic", "true"),
Some(vec!["--hermetic".into()])
);
assert_eq!(argv_for("hermetic", "false"), None);
assert!(parse(&["--hermetic"]).hermetic, "the bare flag enables it");
assert_eq!(parse(&["--hermetic"]).mtp_gate_force(), Some(true));
assert_eq!(parse(&[]).mtp_gate_force(), None);
}
#[test]
fn both_scheduler_routers_are_accepted_and_the_default_stays_sync() {
assert!(validate_serve_args(&parse(&["--scheduler-config", "async"])).is_ok());
assert!(validate_serve_args(&parse(&["--scheduler-config", "sync"])).is_ok());
assert_eq!(parse(&[]).scheduler_config, "sync", "the default router");
let err = validate_serve_args(&parse(&["--scheduler-config", "nonsense"])).unwrap_err();
assert!(err.contains("--scheduler-config"), "{err}");
}
#[test]
fn adaptive_levers_need_the_adaptive_routing() {
let aq = |v: &'static str, extra: &[&'static str]| {
let mut a = vec!["--activation-quantization", v];
a.extend_from_slice(extra);
validate_serve_args(&parse(&a))
};
let w4a4 = ["--weight-quantization", "nvfp4", "--w4a4-downcast"];
assert!(aq("adaptive", &w4a4).is_ok());
assert!(aq("adaptive,lm_head:bf16", &w4a4).is_ok());
let e = aq("declared", &w4a4).unwrap_err();
assert!(
e.contains("--w4a4-downcast") && e.contains("gdn, attn, ffn"),
"{e}"
);
assert!(aq("adaptive,ffn:1-4=nvfp4;5-=adaptive", &w4a4).is_err());
let f16 = ["--ssm-h-dtype", "f16", "--gdn-fused-norm"];
assert!(aq("adaptive,attn:bf16", &f16).is_ok());
assert!(aq("declared", &f16).is_ok());
assert!(
aq(
"adaptive,gdn:1=bf16;2-=adaptive",
&["--ssm-h-dtype", "f16-pool", "--gdn-fused-norm"],
)
.is_ok()
);
assert!(aq("adaptive", &["--no-canonical-tiers"]).is_ok());
let e = aq("bf16", &["--no-canonical-tiers"]).unwrap_err();
assert!(e.contains("--no-canonical-tiers"), "{e}");
}
#[test]
fn hermetic_refuses_the_shared_prompt_lookup_cache() {
let pl = ["--speculative", "--prompt-lookup-decoding"];
let cache = ["--prompt-lookup-shared-cache-mb", "64"];
let both = [&pl[..], &cache[..], &["--hermetic"]].concat();
let err =
validate_serve_args(&parse(&both)).expect_err("a cross-request channel under --hermetic");
assert!(
err.contains("--hermetic with --prompt-lookup-shared-cache-mb"),
"{err}"
);
assert!(validate_serve_args(&parse(&[&pl[..], &cache[..]].concat())).is_ok());
assert!(validate_serve_args(&parse(&[&pl[..], &["--hermetic"]].concat())).is_ok());
}
#[test]
fn a_tokenizer_trim_threshold_above_max_seq_len_is_refused() {
let at = |n: &str| parse(&["--max-seq-len", "65536", "--tokenizer-trim-min-tokens", n]);
let err = validate_serve_args(&at("65537")).expect_err("no prompt can reach 65537");
assert!(
err.contains("--tokenizer-trim-min-tokens 65537 is above --max-seq-len 65536"),
"{err}"
);
assert!(validate_serve_args(&at("0")).is_ok(), "0 is off");
assert_eq!(parse(&[]).chat_surface.tokenizer_trim_min_tokens, 0);
#[cfg(all(target_os = "linux", target_env = "gnu"))]
assert!(validate_serve_args(&at("65536")).is_ok());
#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
assert!(
validate_serve_args(&at("65536"))
.expect_err("no malloc_trim off glibc")
.contains("not glibc's")
);
}