spark_runtime/kv_cache/catalog.rs
1// SPDX-License-Identifier: AGPL-3.0-only
2
3//! The exhaustive [`KvCacheDtype`] catalogue: every variant and its canonical
4//! CLI spelling, in one place the compiler defends.
5//!
6//! `--kv-cache-dtype` is a plain `String` in clap, so anything that wants to
7//! LIST the valid values — the CLI validator's error text, the dashboard's
8//! option picker — has historically had to copy them by hand, and a copied
9//! list drifts: it offers a value the server refuses, or hides one that
10//! works. Both consumers now read [`KvCacheDtype::ALL`], and the `name` match
11//! below is what keeps `ALL` honest — it has no wildcard arm, so adding a
12//! variant fails THIS build until the new name is written here, next to the
13//! two-line comment telling you to extend `ALL` with it.
14
15use super::KvCacheDtype;
16
17impl KvCacheDtype {
18 /// Every variant, in the order the enum declares them.
19 ///
20 /// Extend this together with [`KvCacheDtype::name`] below — the
21 /// non-exhaustive-match error a new variant raises there points here.
22 pub const ALL: [KvCacheDtype; 16] = [
23 KvCacheDtype::Bf16,
24 KvCacheDtype::Fp8,
25 KvCacheDtype::Nvfp4,
26 KvCacheDtype::Turbo4,
27 KvCacheDtype::Turbo3,
28 KvCacheDtype::Turbo2,
29 KvCacheDtype::Turbo8,
30 KvCacheDtype::Turbo4KTurbo3V,
31 KvCacheDtype::Turbo4KTurbo8V,
32 KvCacheDtype::Turbo3KTurbo8V,
33 KvCacheDtype::Bf16KTurbo4V,
34 KvCacheDtype::Bf16KTurbo3V,
35 KvCacheDtype::Fp8KTurbo4V,
36 KvCacheDtype::Fp8KTurbo3V,
37 KvCacheDtype::Bf16KTurbo2V,
38 KvCacheDtype::Fp8KTurbo2V,
39 ];
40
41 /// The canonical `--kv-cache-dtype` spelling. `Display` delegates here,
42 /// so the string a picker offers is byte-for-byte the string the flag
43 /// parser reads back — the round trip the tests below pin.
44 pub const fn name(self) -> &'static str {
45 match self {
46 KvCacheDtype::Bf16 => "bf16",
47 KvCacheDtype::Fp8 => "fp8",
48 KvCacheDtype::Nvfp4 => "nvfp4",
49 KvCacheDtype::Turbo4 => "turbo4",
50 KvCacheDtype::Turbo3 => "turbo3",
51 KvCacheDtype::Turbo2 => "turbo2",
52 KvCacheDtype::Turbo8 => "turbo8",
53 KvCacheDtype::Turbo4KTurbo3V => "turbo4k_turbo3v",
54 KvCacheDtype::Turbo4KTurbo8V => "turbo4k_turbo8v",
55 KvCacheDtype::Turbo3KTurbo8V => "turbo3k_turbo8v",
56 KvCacheDtype::Bf16KTurbo4V => "bf16k_turbo4v",
57 KvCacheDtype::Bf16KTurbo3V => "bf16k_turbo3v",
58 KvCacheDtype::Fp8KTurbo4V => "fp8k_turbo4v",
59 KvCacheDtype::Fp8KTurbo3V => "fp8k_turbo3v",
60 KvCacheDtype::Bf16KTurbo2V => "bf16k_turbo2v",
61 KvCacheDtype::Fp8KTurbo2V => "fp8k_turbo2v",
62 }
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::KvCacheDtype;
69
70 #[test]
71 fn every_listed_name_parses_back_to_the_variant_that_produced_it() {
72 // The catalogue's whole promise: a value copied out of `ALL` is a
73 // value `FromStr` accepts, and it means the same dtype. A name that
74 // fails either half is an option a picker would offer and the server
75 // would refuse.
76 for dtype in KvCacheDtype::ALL {
77 let parsed: KvCacheDtype = dtype
78 .name()
79 .parse()
80 .unwrap_or_else(|e| panic!("{} does not parse: {e:#}", dtype.name()));
81 assert_eq!(
82 parsed,
83 dtype,
84 "{} parses to a different dtype",
85 dtype.name()
86 );
87 assert_eq!(
88 dtype.to_string(),
89 dtype.name(),
90 "Display and the catalogue disagree"
91 );
92 }
93 }
94
95 #[test]
96 fn the_catalogue_has_no_duplicates() {
97 // A duplicated entry is a picker row that looks like a choice and is
98 // not one; with sixteen hand-ordered entries it is an easy slip.
99 for (i, a) in KvCacheDtype::ALL.iter().enumerate() {
100 for b in &KvCacheDtype::ALL[i + 1..] {
101 assert_ne!(a, b, "{} is listed twice", a.name());
102 }
103 }
104 }
105}