met 1.0.0-beta-preview

Metrale Engine: pure Rust LLM inference server (HTTP API, scheduler, TUI, CLI).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
// SPDX-License-Identifier: MIT OR Apache-2.0

//! 2026-09-26: The closed value sets of the enumerated `met serve` string
//! flags, and the parsers for `--kv-high-precision-layers` and the tristate
//! flags.
//!
//! `validate_serve_args` refuses a value outside a set (`check_enum`), and
//! [`options_for_flag`] offers the sets to the TUI option picker and the CLI
//! manifest, so what is offered and what is accepted come from one list.
//!
//! `--kv-cache-dtype` has no const here: its list is
//! `metrale_cache::kv_cache::KvCacheDtype::ALL`.
//!
//! The sets are not wired into clap as `PossibleValuesParser`s:
//! `validate_serve_args` reports every violation at once, where clap exits on
//! the first.
//!
//! Owner: server CLI (`met serve`).
//! Invariants: none beyond the types.

use metrale_config::{IndexerCacheDtype, MoeExpertLayout, WeightQuantization};
use metrale_gpu_runtime::prefill_tail_mode::PrefillTailMode;
use metrale_model_layers::layers::{DenseQuantization, DsaQuantization, ExpertQuantization};

/// 2026-09-26: What `--kv-high-precision-layers auto` resolves to. The flag's
/// help text states the same number as "recommended".
pub(crate) const AUTO_KV_HIGH_PRECISION_LAYERS: usize = 2;

/// 2026-09-26: The accepted forms of `--kv-high-precision-layers`: a count or
/// a keyword, so not a `check_enum` list. `validate_serve_args` and
/// `serve_phases::kv_cache` both parse through this type, so they accept the
/// same forms.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum KvHighPrecisionLayers {
    /// 2026-09-26: `max` / `all`: every attention layer stays BF16.
    All,
    /// 2026-09-26: `auto`: [`AUTO_KV_HIGH_PRECISION_LAYERS`].
    Auto,
    /// 2026-09-26: An explicit first-N-and-last-N count. `0`, the flag's
    /// default, defers to `auto_high_precision_layers` for the KV dtype.
    Count(usize),
}

impl std::str::FromStr for KvHighPrecisionLayers {
    type Err = String;

    fn from_str(raw: &str) -> Result<Self, String> {
        match raw.trim().to_lowercase().as_str() {
            "max" | "all" => Ok(Self::All),
            "auto" => Ok(Self::Auto),
            s => s.parse::<usize>().map(Self::Count).map_err(|_| {
                "expected a whole number of layers, or one of: auto, max, all".to_string()
            }),
        }
    }
}

impl KvHighPrecisionLayers {
    pub(crate) fn resolve(self, num_attn_layers: usize) -> usize {
        match self {
            Self::All => num_attn_layers,
            Self::Auto => AUTO_KV_HIGH_PRECISION_LAYERS,
            Self::Count(n) => n,
        }
    }
}

/// 2026-09-26: The value set of a lever whose default is decided off the
/// command line and which the command line may pin either way. `auto` pins
/// nothing. A presence flag could move it in one direction only.
pub(crate) const TRISTATES: &[&str] = &["auto", "on", "off"];

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Tristate {
    Auto,
    On,
    Off,
}

impl std::str::FromStr for Tristate {
    type Err = String;

    fn from_str(raw: &str) -> Result<Self, String> {
        match raw {
            "auto" => Ok(Self::Auto),
            "on" => Ok(Self::On),
            "off" => Ok(Self::Off),
            other => Err(format!("expected one of auto, on, off; got {other:?}")),
        }
    }
}

impl Tristate {
    /// 2026-09-26: Parse a value `validate_serve_args` has already accepted;
    /// panics on any other.
    pub(crate) fn validated(raw: &str) -> Self {
        raw.parse().expect("validated by validate_serve_args")
    }

    /// 2026-09-26: What the command line pins: `None` for `auto`, which leaves
    /// the lever's own default in charge.
    pub(crate) fn pinned(self) -> Option<bool> {
        match self {
            Self::Auto => None,
            Self::On => Some(true),
            Self::Off => Some(false),
        }
    }
}

/// 2026-10-08: `--kv-cache-dtype` parser: the canonical name for a value that has a
/// second spelling, so every consumer of the flag sees one spelling. `fp8_e4m3` is the
/// format `fp8` stores (E4M3, `metrale_cache::kv_cache::KvCacheDtype::Fp8`). Any other
/// value passes through unchanged and is checked by `validate_serve_args`.
pub(crate) fn canonical_kv_cache_dtype(raw: &str) -> Result<String, String> {
    let canonical = match raw {
        "fp8_e4m3" => "fp8",
        other => other,
    };
    Ok(canonical.to_string())
}

pub(crate) const LM_HEAD_DTYPES: &[&str] = &["default", "bf16", "nvfp4", "fp8"];
pub(crate) const MTP_QUANTS: &[&str] = &["bf16", "fp8", "nvfp4"];
pub(crate) const SCHEDULERS: &[&str] = &["fifo", "slai"];
/// 2026-09-26: The device routers: `sync` settles every device step before
/// the next host decision; `async` lets one plain decode step run ahead of the
/// host, and falls back to `sync` with a warning at startup when the router
/// cannot be built, as on a model without a device token feed.
pub(crate) const SCHEDULER_CONFIGS: &[&str] = &["sync", "async"];
/// 2026-09-26: `--telemetry`: the telemetry crate's own level names.
pub(crate) const TELEMETRY_LEVELS: &[&str] = &metrale_telemetry::Level::NAMES;
pub(crate) const SSM_H_DTYPES: &[&str] = &["f32", "f16", "f16-pool"];
pub(crate) const MTP_GATES: &[&str] = &["auto", "force"];
pub(crate) const TOOL_CALL_PARSERS: &[&str] = &[
    "hermes",
    "qwen3_coder",
    "qwen3_xml",
    "gemma4",
    "mistral",
    "minimax_xml",
    "bare_json",
    "poolside_v1",
    "glm47",
];

/// 2026-09-27: `--expert-quantization`: a clap value enum over the model layer's tiers
/// (`metrale_model_layers::layers::ExpertQuantization`), which own the names. Unlike the string
/// sets above, clap parses and refuses it.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExpertQuantizationArg(pub ExpertQuantization);

impl clap::ValueEnum for ExpertQuantizationArg {
    fn value_variants<'a>() -> &'a [Self] {
        const VARIANTS: [ExpertQuantizationArg; 3] = [
            ExpertQuantizationArg(ExpertQuantization::ALL[0]),
            ExpertQuantizationArg(ExpertQuantization::ALL[1]),
            ExpertQuantizationArg(ExpertQuantization::ALL[2]),
        ];
        &VARIANTS
    }

    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
        // 2026-09-27: GB10, Qwen3.6-35B-A3B-FP8, canonical tiers, `--mtp-gate force`; the
        // same numbers as at `ExpertQuantization`.
        let help = match self.0 {
            ExpertQuantization::Fp8 => {
                "the checkpoint's FP8 experts (most stable, still fast). BFCL echolp N=1004 \
                 84.96/86.03 overall/normalized; agentic-webserver pass (535-546 s); C16 \
                 315.5 tok/s, 0.227 J/tok"
            }
            ExpertQuantization::Nvfp4GateUp => {
                "lowers the routed experts' gate and up projections to NVFP4 in decode; down, \
                 the shared expert and prefill stay FP8 (the stable speed lever). BFCL \
                 84.86/85.33; agentic-webserver pass 3/3 (504-533 s); C16 359.6 tok/s, \
                 0.211 J/tok"
            }
            ExpertQuantization::Nvfp4 => {
                "lowers every routed-expert projection to NVFP4 in decode; the shared expert and \
                 prefill stay FP8 (dangerous but fast). BFCL 85.46/86.57; agentic-webserver \
                 FAILS its 700 s ceiling (168 turns, 774 s); C16 383.1 tok/s, 0.201 J/tok"
            }
        };
        Some(clap::builder::PossibleValue::new(self.0.name()).help(help))
    }
}

/// 2026-10-09: `--dense-quantization`: a clap value enum over the model layer's tiers
/// (`metrale_model_layers::layers::DenseQuantization`), which own the names.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DenseQuantizationArg(pub DenseQuantization);

impl clap::ValueEnum for DenseQuantizationArg {
    fn value_variants<'a>() -> &'a [Self] {
        const VARIANTS: [DenseQuantizationArg; 3] = [
            DenseQuantizationArg(DenseQuantization::ALL[0]),
            DenseQuantizationArg(DenseQuantization::ALL[1]),
            DenseQuantizationArg(DenseQuantization::ALL[2]),
        ];
        &VARIANTS
    }

    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
        let help = match self.0 {
            DenseQuantization::Declared => "16-bit dense projections at the checkpoint's width",
            DenseQuantization::Fp8 => {
                "16-bit dense projections quantized at load to FP8 per-channel and decoded \
                 W8A8 with per-token FP8 activations: below the checkpoint's declared \
                 precision (glm5_next only; unmeasured)"
            }
            DenseQuantization::W4a16 => {
                "FURTHER BELOW the checkpoint's declared precision than fp8: the KDA q/k/v, \
                 f_a, b, g_a and o projections and the shared expert quantized at load to \
                 NVFP4 (4-bit weights) and decoded W4A16 with 16-bit activations; the rest of \
                 fp8's set stays fp8 (glm5_next only; unmeasured)"
            }
        };
        Some(clap::builder::PossibleValue::new(self.0.name()).help(help))
    }
}

/// 2026-10-11: `--dsa-quantization`: a clap value enum over the model layer's DSA tiers
/// (`metrale_model_layers::layers::DsaQuantization`), which own the names.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DsaQuantizationArg(pub DsaQuantization);

impl clap::ValueEnum for DsaQuantizationArg {
    fn value_variants<'a>() -> &'a [Self] {
        const VARIANTS: [DsaQuantizationArg; 3] = [
            DsaQuantizationArg(DsaQuantization::ALL[0]),
            DsaQuantizationArg(DsaQuantization::ALL[1]),
            DsaQuantizationArg(DsaQuantization::ALL[2]),
        ];
        &VARIANTS
    }

    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
        let help = match self.0 {
            DsaQuantization::Declared => {
                "the DSA q_a, absorbed q and absorbed o projections at the checkpoint's BF16"
            }
            DsaQuantization::W8a8 => {
                "those three quantized at load to FP8 per-channel and decoded W8A8 with \
                 per-token FP8 activations, as the reference engine runs them: below the \
                 checkpoint's declared precision (glm5_next only)"
            }
            DsaQuantization::W4a16 => {
                "FURTHER BELOW declared than w8a8: those three quantized at load to NVFP4 \
                 (4-bit weights) and decoded W4A16 with 16-bit activations (glm5_next only)"
            }
        };
        Some(clap::builder::PossibleValue::new(self.0.name()).help(help))
    }
}

/// 2026-10-14: `--prefill-tail-mode`: a clap value enum over gpu-runtime's tail modes
/// (`metrale_gpu_runtime::prefill_tail_mode::PrefillTailMode`), which own the names.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PrefillTailModeArg(pub PrefillTailMode);

impl clap::ValueEnum for PrefillTailModeArg {
    fn value_variants<'a>() -> &'a [Self] {
        const VARIANTS: [PrefillTailModeArg; 2] = [
            PrefillTailModeArg(PrefillTailMode::ALL[0]),
            PrefillTailModeArg(PrefillTailMode::ALL[1]),
        ];
        &VARIANTS
    }

    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
        Some(clap::builder::PossibleValue::new(self.0.name()).help(self.0.describe()))
    }
}

/// 2026-10-16: `--indexer-cache-dtype`: a clap value enum over the config crate's values
/// (`metrale_config::IndexerCacheDtype`), which own the names.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IndexerCacheDtypeArg(pub IndexerCacheDtype);

impl clap::ValueEnum for IndexerCacheDtypeArg {
    fn value_variants<'a>() -> &'a [Self] {
        const VARIANTS: [IndexerCacheDtypeArg; 2] = [
            IndexerCacheDtypeArg(IndexerCacheDtype::ALL[0]),
            IndexerCacheDtypeArg(IndexerCacheDtype::ALL[1]),
        ];
        &VARIANTS
    }

    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
        let help = match self.0 {
            IndexerCacheDtype::Bf16 => "a bf16 key row and gate row per token",
            IndexerCacheDtype::Fp8 => {
                "one FP8 E4M3 compressed key per complete pool plus a bf16 ring for the pool \
                 being filled; more context per KV byte (glm5_next only)"
            }
        };
        Some(clap::builder::PossibleValue::new(self.0.name()).help(help))
    }
}

/// 2026-10-09: `--moe-expert-layout`: a clap value enum over the config crate's layouts
/// (`metrale_config::MoeExpertLayout`), which own the names.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoeExpertLayoutArg(pub MoeExpertLayout);

impl clap::ValueEnum for MoeExpertLayoutArg {
    fn value_variants<'a>() -> &'a [Self] {
        const VARIANTS: [MoeExpertLayoutArg; 2] = [
            MoeExpertLayoutArg(MoeExpertLayout::ALL[0]),
            MoeExpertLayoutArg(MoeExpertLayout::ALL[1]),
        ];
        &VARIANTS
    }

    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
        let help = match self.0 {
            MoeExpertLayout::Ep => "each routed expert owned whole by one --ep-size rank",
            MoeExpertLayout::Tp => {
                "every routed expert's intermediate width split over the --tp-size ranks, all \
                 slots local on every rank; needs --ep-size 1 (glm5_next only; unmeasured)"
            }
        };
        Some(clap::builder::PossibleValue::new(self.0.name()).help(help))
    }
}

/// 2026-09-28: `--weight-quantization`: a clap value enum over the config crate's tiers
/// (`metrale_config::WeightQuantization`), which own the names.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WeightQuantizationArg(pub WeightQuantization);

impl clap::ValueEnum for WeightQuantizationArg {
    fn value_variants<'a>() -> &'a [Self] {
        const VARIANTS: [WeightQuantizationArg; 2] = [
            WeightQuantizationArg(WeightQuantization::ALL[0]),
            WeightQuantizationArg(WeightQuantization::ALL[1]),
        ];
        &VARIANTS
    }

    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
        let help = match self.0 {
            WeightQuantization::Declared => WEIGHT_QUANT_DECLARED_HELP,
            WeightQuantization::Nvfp4 => WEIGHT_QUANT_NVFP4_HELP,
        };
        Some(clap::builder::PossibleValue::new(self.0.name()).help(help))
    }
}

/// 2026-09-28: The `declared` tier's one-line help (`met serve --help`).
const WEIGHT_QUANT_DECLARED_HELP: &str = "each layer at the precision its checkpoint's \
     quantization_config declares, weights and activations (W4A4 where it declares FP4 \
     activations, W4A16 where weight-only). Until the W8A8 decode kernels land, FP8-declared \
     layers run 16-bit activations (above declared), and the dense loader still requantizes \
     per-channel FP8 attention/GDN/MLP to NVFP4";

/// 2026-09-28: The `nvfp4` tier's one-line help.
const WEIGHT_QUANT_NVFP4_HELP: &str = "the engine before the tiers: FP8-declared layers \
     requantized to NVFP4 at load (below declared), 16-bit decode activations unless \
     --w4a4-downcast, FP4 MMQ prefill on every NVFP4 FFN; the certified recipes' tier";

/// 2026-09-26: The closed value set for a `met serve` flag, by its long name,
/// or `None` for a free-form flag. For `--kv-cache-dtype` it lists each
/// dtype's canonical name only; parse aliases such as `fp8k2v` for
/// `fp8k_turbo2v` are not offered.
pub(crate) fn options_for_flag(flag: &str) -> Option<Vec<String>> {
    let owned = |list: &[&str]| list.iter().map(|s| s.to_string()).collect();
    match flag {
        "lm-head-dtype" => Some(owned(LM_HEAD_DTYPES)),
        "mtp-quantization" => Some(owned(MTP_QUANTS)),
        "scheduler" => Some(owned(SCHEDULERS)),
        "scheduler-config" => Some(owned(SCHEDULER_CONFIGS)),
        "ssm-h-dtype" => Some(owned(SSM_H_DTYPES)),
        "telemetry" => Some(owned(TELEMETRY_LEVELS)),
        "mtp-gate" => Some(owned(MTP_GATES)),
        "tool-call-parser" => Some(owned(TOOL_CALL_PARSERS)),
        "ssm-batched-recurrent" | "content-loop-watchdog" | "tool-grammar" => {
            Some(owned(TRISTATES))
        }
        "weight-quantization" => Some(
            WeightQuantization::ALL
                .iter()
                .map(|q| q.name().to_string())
                .collect(),
        ),
        "dense-quantization" => Some(
            DenseQuantization::ALL
                .iter()
                .map(|q| q.name().to_string())
                .collect(),
        ),
        "moe-expert-layout" => Some(
            MoeExpertLayout::ALL
                .iter()
                .map(|l| l.name().to_string())
                .collect(),
        ),
        "indexer-cache-dtype" => Some(
            IndexerCacheDtype::ALL
                .iter()
                .map(|d| d.name().to_string())
                .collect(),
        ),
        "expert-quantization" => Some(
            ExpertQuantization::ALL
                .iter()
                .map(|q| q.name().to_string())
                .collect(),
        ),
        "kv-cache-dtype" => Some(
            metrale_cache::kv_cache::KvCacheDtype::ALL
                .iter()
                .map(|d| d.name().to_string())
                .collect(),
        ),
        _ => None,
    }
}

#[cfg(test)]
#[path = "flag_values_tests.rs"]
mod tests;

/// 2026-09-28: `--forward`: which forward decode runs. `legacy` is the hand-written layer
/// loops; `circuit` runs the program compiled from the model's circuit plan
/// (`metrale_model_layers::circuit_exec`), and boot refuses a model the circuit does not model.
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum ForwardArg {
    #[value(help = "the hand-written layer loops")]
    Legacy,
    #[value(help = "the program compiled from the model's circuit plan")]
    Circuit,
    #[value(
        help = "the circuit plan with reference rules only (no bit-identical fusions), for A/B"
    )]
    CircuitReference,
}

impl ForwardArg {
    /// 2026-09-28: The spelling on the command line and in `GET /forward`.
    pub fn name(self) -> &'static str {
        match self {
            ForwardArg::Legacy => "legacy",
            ForwardArg::Circuit => "circuit",
            ForwardArg::CircuitReference => "circuit-reference",
        }
    }
}