metrale_model_arch/weight_loader/dflash_loader/
config.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-25: The drafter `config.json` schema: [`DflashConfig`] with the
4//! nested [`DflashRopeScaling`] and [`DflashSubConfig`] blocks, and their
5//! serde defaults.
6//!
7//! Owner: model-arch weight loader.
8//! Invariants: none beyond the types.
9
10use serde::Deserialize;
11
12/// 2026-09-25: The fields of a drafter's `config.json` that the engine reads,
13/// under their `config.json` names. Unknown keys are ignored.
14#[derive(Debug, Clone, Deserialize)]
15pub struct DflashConfig {
16    pub hidden_size: usize,
17    pub num_hidden_layers: usize,
18    pub intermediate_size: usize,
19    pub num_attention_heads: usize,
20    pub num_key_value_heads: usize,
21    pub head_dim: usize,
22    pub vocab_size: usize,
23    #[serde(default)]
24    pub draft_vocab_size: Option<usize>,
25    #[serde(default)]
26    pub tie_word_embeddings: bool,
27    /// 2026-09-25: Top-level block size γ; 16 when absent. Read it through
28    /// [`DflashConfig::effective_block_size`].
29    #[serde(default = "default_block_size")]
30    pub block_size: usize,
31    #[serde(default)]
32    pub dflash_config: Option<DflashSubConfig>,
33    /// 2026-09-25: Drafter RoPE θ; 10,000,000 when absent.
34    #[serde(default = "default_rope_theta")]
35    pub rope_theta: f32,
36    /// 2026-09-25: The `rope_scaling` block, also read under the key
37    /// `rope_parameters`. `None` means plain RoPE. With `rope_type == "yarn"`
38    /// the head builds a YaRN inv_freq table; any other block falls back to
39    /// plain RoPE with a warning.
40    #[serde(default, alias = "rope_parameters")]
41    pub rope_scaling: Option<DflashRopeScaling>,
42    /// 2026-09-25: DSpark Markov head rank, a top-level key; 0 when absent,
43    /// which loads no Markov head.
44    #[serde(default)]
45    pub markov_rank: usize,
46    /// 2026-09-25: DSpark Markov head type. When the Markov head loads, any
47    /// value other than `"vanilla"` fails the load.
48    #[serde(default)]
49    pub markov_head_type: Option<String>,
50    /// 2026-09-25: Load the DSpark confidence head (`confidence_head.proj.*`)
51    /// when the checkpoint has it; false when absent.
52    #[serde(default)]
53    pub enable_confidence_head: bool,
54    /// 2026-09-25: When true the confidence head reads the hidden state and
55    /// then the Markov embedding (`hidden_size + markov_rank` inputs); when
56    /// false, the hidden state only. True when absent.
57    #[serde(default = "default_true")]
58    pub confidence_head_with_markov: bool,
59}
60
61fn default_true() -> bool {
62    true
63}
64
65fn default_rope_theta() -> f32 {
66    10_000_000.0
67}
68
69/// 2026-09-25: The `rope_scaling` fields the drafter head reads.
70#[derive(Debug, Clone, Deserialize)]
71pub struct DflashRopeScaling {
72    /// 2026-09-25: Only `"yarn"` is recognised; any other value, or none, falls
73    /// back to plain RoPE with a warning when the head is built.
74    #[serde(default)]
75    pub rope_type: Option<String>,
76    #[serde(default)]
77    pub factor: Option<f32>,
78    #[serde(default)]
79    pub beta_fast: Option<f32>,
80    #[serde(default)]
81    pub beta_slow: Option<f32>,
82    #[serde(default)]
83    pub original_max_position_embeddings: Option<f32>,
84}
85
86fn default_block_size() -> usize {
87    16
88}
89
90/// 2026-09-25: The nested `dflash_config` block of the drafter's `config.json`.
91#[derive(Debug, Clone, Deserialize)]
92pub struct DflashSubConfig {
93    /// 2026-09-25: Token id that fills the draft block after its anchor token.
94    pub mask_token_id: u32,
95    /// 2026-09-25: Target-model layers whose hidden states the drafter reads.
96    /// The head refuses to build when the list is empty.
97    pub target_layer_ids: Vec<usize>,
98    /// 2026-09-25: `"dspark"` marks a drafter whose row j predicts position
99    /// j + 1; the head then rotates the draft vector right by one, unless
100    /// `METRALE_DSPARK_SHIFT` overrides it.
101    #[serde(default)]
102    pub projector_type: Option<String>,
103
104    /// 2026-09-25: DFlash2 conv kernel size; 0 when absent. With
105    /// `conv_group_size`, a non-zero value lets the conv tensors load.
106    #[serde(default)]
107    pub conv_kernel_size: usize,
108    /// 2026-09-25: DFlash2 channels per conv group; 0 when absent.
109    #[serde(default)]
110    pub conv_group_size: usize,
111    /// 2026-09-25: DFlash2 selector codebook rank; 0 when absent. With
112    /// `selector_top_k`, a non-zero value lets the selector tensors load.
113    #[serde(default)]
114    pub selector_rank: usize,
115    /// 2026-09-25: DFlash2 candidates kept per position; 0 when absent.
116    #[serde(default)]
117    pub selector_top_k: usize,
118    /// 2026-09-25: The drafter's trained block size, when the checkpoint states
119    /// it here. `None` falls back to the top-level field. Read it through
120    /// [`DflashConfig::effective_block_size`].
121    #[serde(default)]
122    pub block_size: Option<usize>,
123}
124
125impl DflashConfig {
126    /// 2026-09-25: Resolved block size γ: `dflash_config.block_size` when set,
127    /// else the top-level field. The sub-config comes first because serde
128    /// fills the top-level default of 16 for a checkpoint that never states
129    /// it.
130    pub fn effective_block_size(&self) -> usize {
131        self.dflash_config
132            .as_ref()
133            .and_then(|c| c.block_size)
134            .unwrap_or(self.block_size)
135    }
136}