metrale_config/kv_completeness.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-26: The two KV-completeness gates: whether the radix prefix cache and the
4//! `--swap-space-gb` swap-out may treat a sequence's KV blocks as its whole state.
5//!
6//! Owner: config.
7//! Invariants:
8//! - Without `METRALE_GLM53_PREFIX_CACHE_UNPROVEN`, the two gates give the same answer.
9//! - That switch opens only the prefix-cache gate, and only for `glm5_next` / `glm5_next_text`.
10
11use super::ModelConfig;
12
13impl ModelConfig {
14 /// 2026-09-26: Whether every byte of a sequence's per-layer state is held in its KV blocks.
15 ///
16 /// False for `glm5_next` / `glm5_next_text` (the DSA indexer rows, `Glm5NextDsaState`) and
17 /// for `deepseek_v4` when any `compress_ratios` entry is non-zero.
18 fn per_sequence_state_is_kv_complete(&self) -> bool {
19 match self.model_type.as_str() {
20 "glm5_next" | "glm5_next_text" => false,
21 "deepseek_v4" => self.compress_ratios.iter().all(|&ratio| ratio == 0),
22 _ => true,
23 }
24 }
25
26 /// 2026-09-26: Whether the radix prefix cache captures every state needed to resume this
27 /// model exactly. `preflight_reserve` and `build_prefix_cache` both read it.
28 ///
29 /// `METRALE_GLM53_PREFIX_CACHE_UNPROVEN` (any value, `0` included) also opens it for
30 /// `glm5_next`. `kv_only_swap_out_is_safe` ignores that switch: the swap image cannot
31 /// carry the DSA rows (see there).
32 pub fn kv_only_prefix_cache_is_safe(&self) -> bool {
33 self.kv_only_prefix_cache_is_safe_with(Self::glm53_prefix_cache_validation_env())
34 }
35
36 /// 2026-09-26: Env-free core of [`Self::kv_only_prefix_cache_is_safe`]; the override opens
37 /// only `glm5_next` / `glm5_next_text`.
38 pub(crate) fn kv_only_prefix_cache_is_safe_with(&self, validation_override: bool) -> bool {
39 self.per_sequence_state_is_kv_complete()
40 || (validation_override
41 && matches!(self.model_type.as_str(), "glm5_next" | "glm5_next_text"))
42 }
43
44 fn glm53_prefix_cache_validation_env() -> bool {
45 std::env::var_os("METRALE_GLM53_PREFIX_CACHE_UNPROVEN").is_some()
46 }
47
48 /// 2026-09-26: Whether a sequence may be swapped out to the `--swap-space-gb` pool and
49 /// restored from it; `resolve_swap_space_gb` reads it. `save_sequence_state_dispatch`
50 /// writes the KV blocks and the `SsmLayerState` of each `LayerType::LinearAttention`
51 /// layer and nothing else, so any other per-sequence state would not survive the swap.
52 pub fn kv_only_swap_out_is_safe(&self) -> bool {
53 self.per_sequence_state_is_kv_complete()
54 }
55}