ModelConfig

Struct ModelConfig 

Source
pub struct ModelConfig {
Show 122 fields pub hidden_size: usize, pub num_hidden_layers: usize, pub intermediate_size: usize, pub vocab_size: usize, pub num_attention_heads: usize, pub num_attention_heads_per_layer: Vec<usize>, pub num_key_value_heads: usize, pub head_dim: usize, pub partial_rotary_factor: f64, pub linear_num_key_heads: usize, pub linear_key_head_dim: usize, pub linear_num_value_heads: usize, pub linear_value_head_dim: usize, pub linear_conv_kernel_dim: usize, pub num_experts: usize, pub zero_expert_num: usize, pub num_experts_per_tok: usize, pub moe_intermediate_size: usize, pub shared_expert_intermediate_size: usize, pub norm_topk_prob: bool, pub decoder_sparse_step: usize, pub layer_types: Vec<LayerType>, pub mtp_layer_types: Vec<LayerType>, pub full_attention_interval: usize, pub sliding_window: u32, pub max_position_embeddings: usize, pub rope_theta: f64, pub rms_norm_eps: f64, pub bos_token_id: u32, pub glm5next_router_mode: Glm5NextRouterMode, pub eos_token_id: u32, pub eos_token_ids: Vec<u32>, pub tie_word_embeddings: bool, pub lm_head_bf16_override: Option<bool>, pub lm_head_fp8: bool, pub model_type: String, pub mtp_num_hidden_layers: usize, pub dspark_block_size: usize, pub dspark_noise_token_id: u32, pub dspark_target_layer_ids: Vec<usize>, pub dspark_markov_rank: usize, pub hybrid_override_pattern: String, pub mamba_num_heads: usize, pub mamba_head_dim: usize, pub ssm_state_size: usize, pub n_groups: usize, pub expand: usize, pub n_routed_experts: usize, pub norm_eps: f64, pub conv_kernel: usize, pub moe_shared_expert_intermediate_size: usize, pub routed_scaling_factor: f64, pub linear_gate_lower_bound: f32, pub swiglu_limit: f32, pub mlp_only_layers: Vec<usize>, pub moe_latent_size: usize, pub moe_intermediate_sizes: Vec<usize>, pub num_experts_per_toks: Vec<usize>, pub kv_lora_rank: usize, pub kv_layer_dims: Vec<(usize, usize)>, pub q_lora_rank: usize, pub qk_nope_head_dim: usize, pub qk_rope_head_dim: usize, pub v_head_dim: usize, pub ngram_vocab_size_ratio: usize, pub emb_neighbor_num: usize, pub emb_split_num: usize, pub ngram_vocab_size_base: usize, pub ngram_split_parts: usize, pub ple_layer_ids: Vec<usize>, pub ple_conv_kernel_size: usize, pub o_lora_rank: usize, pub o_groups: usize, pub yarn_mscale: f32, pub yarn_mscale_all_dim: f32, pub hc_mult: usize, pub hc_sinkhorn_iters: usize, pub hc_eps: f32, pub hc_lowrank: usize, pub final_norm_identity: bool, pub compress_ratios: Vec<usize>, pub index_n_heads: usize, pub index_head_dim: usize, pub index_topk: usize, pub index_compress_ratio: usize, pub index_kpool: usize, pub index_kpool_always_select_tail: bool, pub num_hash_layers: usize, pub yarn_factor: f32, pub yarn_beta_slow: f32, pub yarn_beta_fast: f32, pub yarn_original_max_position_embeddings: usize, pub yarn_attention_factor: f32, pub llama_4_scaling_beta: f32, pub llama_4_scaling_original_max_position_embeddings: usize, pub vision: Option<VisionConfig>, pub quantization_config: Option<QuantizationConfig>, pub attn_gated: bool, pub gdn_norm_sigmoid: bool, pub nested_config: bool, pub mrope_section: [usize; 3], pub mrope_interleaved: bool, pub weight_prefix: String, pub profile: bool, pub ep_rank: usize, pub ep_world_size: usize, pub tp_rank: usize, pub tp_world_size: usize, pub serve_max_seq_len: usize, pub fp8_kv_calibration_tokens: usize, pub fp8_kv_headroom: f32, pub final_logit_softcapping: f32, pub embed_scale: f32, pub scoring_func: String, pub use_routing_bias: bool, pub qk_norm_type: String, pub num_mtp_modules: usize, pub mtp_transformer_layers: usize, pub rotary_dim: usize, pub dflash_capture_layers: Vec<usize>, pub dflash_gamma: Option<usize>, pub adapter_max_rank: usize,
}
Expand description

Model configuration parsed from HuggingFace config.json.

Single source of truth for model dimensions. All kernel launch parameters and buffer sizes derive from this struct.

Fields§

§hidden_size: usize§num_hidden_layers: usize§intermediate_size: usize§vocab_size: usize§num_attention_heads: usize§num_attention_heads_per_layer: Vec<usize>

Per-layer Q-head counts for heterogeneous attention models. Empty means every layer uses num_attention_heads.

§num_key_value_heads: usize

GQA: number of K/V heads (≤ num_attention_heads). MQA when 1.

§head_dim: usize§partial_rotary_factor: f64

Fraction of head_dim that gets RoPE-rotated. 1.0 = full RoPE, 0.5 = half-rotated (Phi-style). Default 1.0.

§linear_num_key_heads: usize§linear_key_head_dim: usize§linear_num_value_heads: usize§linear_value_head_dim: usize§linear_conv_kernel_dim: usize

1D causal-conv kernel size on the SSM input (typically 3 or 4).

§num_experts: usize§zero_expert_num: usize

LongCat-Flash zero-computation “identity” experts: the router scores num_experts + zero_expert_num logits, and a token routed to an expert id >= num_experts receives the INPUT itself scaled by the routing weight instead of an expert FFN. 0 = no zero-experts.

§num_experts_per_tok: usize

Top-K experts activated per token (the “A” in 35B-A3B = 3B active params).

§moe_intermediate_size: usize§shared_expert_intermediate_size: usize§norm_topk_prob: bool

Renormalize routing probabilities so the K active experts sum to 1 after top-K selection. Qwen3.5+ sets true; older Qwen2 MoE variants set false.

§decoder_sparse_step: usize

MoE block stride: layer i uses MoE iff i % decoder_sparse_step == 0. 1 = every layer is MoE. Mistral / DeepSeek-style stagger uses 2.

§layer_types: Vec<LayerType>

Per-layer kind (FullAttention | LinearAttention | …) parsed from HF config. When empty, falls back to full_attention_interval.

§mtp_layer_types: Vec<LayerType>

Per-layer kind for the extra layers that sit past num_hidden_layers: multi-token-prediction / NextN blocks. Empty for models that have none.

Kept separate from layer_types on purpose. GLM-5.3-Flash’s layer 45 is a real decoder layer with its own attention block, but num_hidden_layers is 45 and config.layer_types has 45 entries covering 0..=44 — so layer 45 has no honest slot there. Appending it would make every length check and every “iterate the text stack” loop silently include a speculative-decoding layer. Look it up through ModelConfig::layer_type_at, which routes indices past the text stack here.

§full_attention_interval: usize

Stride for full-attention layers in hybrid models when layer_types is empty: every Nth layer is FullAttention, the rest LinearAttention. 1 = every layer is full attention.

§sliding_window: u32

Gemma-4 hybrid-attention sliding window size (0 = full attention). Sliding layers only attend to the last sliding_window KV positions; full layers (every 6th in Gemma-4) ignore this (effectively 0). Parsed from HF config.json sliding_window field. Uses nullable_u32 because Nemotron-H (and some other models) set it to null in JSON.

§max_position_embeddings: usize§rope_theta: f64§rms_norm_eps: f64§bos_token_id: u32

BOS token ID (null → 0 for models without explicit BOS).

§glm5next_router_mode: Glm5NextRouterMode

Which dtype ladder GLM-5.3’s MoE router runs in. See Glm5NextRouterMode — this is a semantic switch, and HfFp32 is the production default.

§eos_token_id: u32

The PRIMARY stop token. See ModelConfig::eos_ids for the complete set — a config may declare several, and this holds only the first.

§eos_token_ids: Vec<u32>

The COMPLETE stop-token set. HF configs are allowed to declare eos_token_id as an array, and several real checkpoints do — GLM-5.3-Flash declares three: 154820 <|endoftext|>, 154827 <|user|>, 154829 <|observation|>. eos_token_id above holds only the PRIMARY one (element 0), which is what every scalar consumer and every chat template wants; collapsing to it and discarding the rest is what made an agent model unable to stop on its own turn terminators.

Populated by parse_config for every model family from the raw JSON, scalar or array. Empty means “not populated” (a hand-built ModelConfig), NOT “no stop tokens” — read it through ModelConfig::eos_ids, never directly.

§tie_word_embeddings: bool§lm_head_bf16_override: Option<bool>

CLI override (--lm-head-dtype) for LM-head quantization, set at serve time (not from config.json). Some(true) = force BF16 lm_head; Some(false) = force the model’s quantized lm_head; None = use the model-config-driven default. Consumed by skip_lm_head_quantization(). Replaces the ATLAS_LMHEAD_BF16 env var.

§lm_head_fp8: bool

When skip_lm_head_quantization() == false, quantize the LM head to FP8 (E4M3, per-row scales, decoded via w8a16_gemv) instead of NVFP4. Set by --lm-head-dtype fp8. Additive: leaves the NVFP4/BF16 paths byte-identical when false.

§model_type: String§mtp_num_hidden_layers: usize§dspark_block_size: usize

Number of query positions generated by one semi-autoregressive draft pass. Zero means the checkpoint does not declare checkpoint-native DSpark.

§dspark_noise_token_id: u32

Token used to initialize the non-anchor positions in a DSpark block.

§dspark_target_layer_ids: Vec<usize>

Target layers whose hidden states are concatenated for the DSpark input.

§dspark_markov_rank: usize

Width of the low-rank Markov token transition head.

§hybrid_override_pattern: String§mamba_num_heads: usize§mamba_head_dim: usize§ssm_state_size: usize§n_groups: usize§expand: usize§n_routed_experts: usize

Nemotron-H uses n_routed_experts (mapped to num_experts in parse_config).

§norm_eps: f64

Nemotron-H uses norm_eps (mapped to rms_norm_eps in parse_config).

§conv_kernel: usize

Nemotron-H conv kernel size (mapped to linear_conv_kernel_dim in parse_config).

§moe_shared_expert_intermediate_size: usize

Nemotron-H shared expert intermediate (mapped to shared_expert_intermediate_size).

§routed_scaling_factor: f64

Nemotron-H routed scaling factor for expert outputs.

§linear_gate_lower_bound: f32

KDA forget-gate lower bound (linear_attn_config.gate_lower_bound). GLM-5.3 declares -5.0; it bounds the log-decay kda_gate produces, so a defaulted 0.0 would clamp the decay to a completely different range. Read by the glm5_next parser, never guessed.

§swiglu_limit: f32

SwiGLU clamp bound (swiglu_limit). 0.0 = the model does not clamp.

🔴 GLM-5.3-Flash declares swiglu_limit = 10.0, and the clamp is asymmetric: gate is upper-bounded only, up is bounded both ways. Read, never defaulted for a model that declares it — a missing clamp is invisible on well-scaled activations and silently wrong on the tails (see kernels/gb10/common/glm5next_ffn.cu).

§mlp_only_layers: Vec<usize>

Decoder-layer indices that use a dense MLP instead of routed experts.

§moe_latent_size: usize

LatentMoE: latent projection dimension for routed experts (Super 120B). When present, routed experts operate in latent space [moe_latent_size] instead of full [hidden_size]. Absent for Nano 30B.

§moe_intermediate_sizes: Vec<usize>

Per-layer MoE intermediate sizes (Nemotron-H Puzzle heterogeneous channel pruning). Length == num_hidden_layers; 0 for non-MoE layers. Empty = fall back to scalar moe_intermediate_size for every MoE layer.

§num_experts_per_toks: Vec<usize>

Per-layer top-K expert counts (Puzzle). Same layout as moe_intermediate_sizes. Empty = use scalar num_experts_per_tok.

§kv_lora_rank: usize

KV latent dimension for compressed cache. 0 = standard attention (no MLA).

§kv_layer_dims: Vec<(usize, usize)>

Per-layer KV cache dimensions (num_kv_heads, head_dim). Populated by loaders for heterogeneous-attention models (e.g. Gemma-4 with sliding and full attention having different head counts and dims). Empty for homogeneous models.

§q_lora_rank: usize

Query latent dimension for low-rank Q projection. 0 = standard Q.

§qk_nope_head_dim: usize

Non-rotary portion of Q/K per head (NoPE component).

§qk_rope_head_dim: usize

Rotary portion of Q/K per head (RoPE component).

§v_head_dim: usize

Value dimension per head (may differ from head_dim in MLA).

§ngram_vocab_size_ratio: usize

N-gram table size multiplier: each table has ~ratio*vocab_size rows (LongCat-Lite: 78 → ~10.2M rows/table). 0 = no n-gram embeddings.

§emb_neighbor_num: usize

Largest n-gram size N (LongCat-Lite: 4 → bigram/trigram/4-gram).

§emb_split_num: usize

Independent hash splits K per n-gram size (LongCat-Lite: 4).

§ngram_vocab_size_base: usize

Rows per n-gram HEAD, absolute (ngram_vocab_size_base).

The Qwen4-Exp form of the same idea LongCat expresses as a ratio: LongCat says “ratio x vocab_size rows per table”, Qwen says “20,000,000 rows per head” outright. Mutually exclusive with ngram_vocab_size_ratio — whichever the checkpoint declares wins, and the authoritative per-head sizes/offsets ship as I64 tensors (ngram_heads_vocab_sizes / ngram_heads_offsets) which the loader reads rather than re-deriving. 0 = not a base-form checkpoint.

§ngram_split_parts: usize

Physical shard count of the n-gram table (split_ngram_parts).

PURELY a file-layout fact, NOT an architectural one: Qwen4-Exp stores one logical [sum(head_vocabs), ngram_dim] table as 128 equal shard_N.weight tensors. The head ranges are independent of the shard boundaries and a head can straddle several shards, so the row cache must address the logical table and translate. 0 = unsharded.

§ple_layer_ids: Vec<usize>

Decoder layers that carry a PLE (per-layer-embedding) n-gram injection (ple_layer_ids). Qwen4-Exp injects at ONE layer, not at the token embedding the way LongCat does — which is why this is a layer list and not a flag. Empty = no PLE.

§ple_conv_kernel_size: usize

Depthwise conv width inside the PLE block (ple_conv_kernel_size). 0 = no conv.

§o_lora_rank: usize

Output projection latent dimension for low-rank O projection. DeepSeek-V4 uses o_lora_rank to compress the output projection. 0 = standard O (no low-rank compression).

§o_groups: usize

Number of block-diagonal groups for the grouped O projection (wo_a). DeepSeek-V4-Flash splits the n_heads*head_dim attention output into o_groups independent groups, each projected to o_lora_rank before the follow-up wo_b mixes the o_groups*o_lora_rank vector back to hidden_size. 0 = ungrouped (dense O).

§yarn_mscale: f32

YaRN attention-temperature mscale (rope_scaling.mscale). HF default is 1.0 when absent. DeepSeek folds _mscale into the rope cos/sin.

§yarn_mscale_all_dim: f32

YaRN attention-temperature mscale_all_dim (rope_scaling.mscale_all_dim). HF default is 0.0 when absent. Used in the _mscale ratio that scales the rope cos/sin (and, when non-zero, the softmax scale).

§hc_mult: usize

Number of hyper-connection residual streams per block (hc_mult). 0 = disabled (every model except DeepSeek-V4). DeepSeek-V4 uses 4.

§hc_sinkhorn_iters: usize

Number of Sinkhorn normalization iterations for the HC mixing matrix (hc_sinkhorn_iters). DeepSeek-V4 default is 20.

§hc_eps: f32

Numerical-stability epsilon for HC sigmoid/softmax/Sinkhorn (hc_eps). DeepSeek-V4 default is 1e-6.

§hc_lowrank: usize

Rank of the hyper-connection input mixer (hc_lowrank).

Qwen4-Exp mixes the hc_mult residual streams through a LOW-RANK pair — input_mix_weight_down [r, hc_mult*hidden] then input_mix_weight_up [hc_mult*hidden, r] — where DeepSeek-V4 uses a Sinkhorn-normalized square matrix. The two share hc_mult and the stream-major layout but NOT the mixing math, so a non-zero value here selects the low-rank variant. 0 = DeepSeek-V4’s Sinkhorn form.

§final_norm_identity: bool

The checkpoint carries NO final normalization before lm_head: the real one is applied inside the hyper-connection mixer while the residual streams collapse. Applying the engine’s ones-placeholder RMS anyway still DIVIDES the hidden by its per-token RMS, which flattens the logits by a per-token factor (measured 1.16-1.63x vs the reference forward on qwen4_exp) – an uninvited temperature multiplier that argmax survives but sampling does not. When set, the final-norm step becomes an identity copy.

§compress_ratios: Vec<usize>

Per-layer compression ratios for hybrid attention (CSA/HCA). 0 = full attention, >0 = compressed attention with that ratio. Length equals num_hidden_layers. Empty = all layers full attention.

§index_n_heads: usize

Number of semantic-indexer heads used by DeepSeek-V4 CSA layers.

§index_head_dim: usize

Per-head dimension of the DeepSeek-V4 semantic indexer.

§index_topk: usize

Maximum compressed-history rows selected per query by the semantic indexer.

§index_compress_ratio: usize

Indexer compression ratio, recorded WITHOUT populating compress_ratios.

Qwen3.8-Flash-Next’s QSA indexer is inert below its budget — selection is topk(min(budget/ratio, complete_blocks)), so at seq_len <= index_topk every block is chosen and dense attention is exact. Keeping compress_ratios empty stops DeepSeek-V4’s compressor being dispatched in its place; keeping the ratio here lets a loader refuse above the budget instead of silently attending densely. 0 = no indexer.

§index_kpool: usize

GLM-5.3 DSA: tokens per k-pool (index_kpool). The pool budget is index_topk / index_kpool, so this is not cosmetic — it sets how many candidates the top-k actually ranks. 0 = model has no k-pooling.

§index_kpool_always_select_tail: bool

GLM-5.3 DSA: always append the trailing partial pool’s tokens to the selection, widening the emitted index row by index_kpool - 1.

§num_hash_layers: usize

Number of hash-based attention layers (DeepSeek-V4 HCA). 0 = none.

§yarn_factor: f32

YaRN scaling factor (yarn.factor). 0.0 = YaRN disabled, use plain RoPE.

§yarn_beta_slow: f32

YaRN low-rotation cutoff (yarn.alpha in Mistral params, beta_slow in HF transformers terminology).

§yarn_beta_fast: f32

YaRN high-rotation cutoff (yarn.beta in Mistral params, beta_fast in HF transformers terminology).

§yarn_original_max_position_embeddings: usize

YaRN original context length used for the correction range (yarn.original_max_position_embeddings).

§yarn_attention_factor: f32

Multiplier applied to both YaRN cosine and sine values. 1.0 means no attention-temperature scaling.

§llama_4_scaling_beta: f32

llama_4_scaling Q temperature beta (llama_4_scaling.beta). Q is multiplied by 1 + beta * log(1 + floor(pos / original_max_pos)) after RoPE. 0.0 = disabled. Mistral Small 4 uses 0.1.

§llama_4_scaling_original_max_position_embeddings: usize

llama_4_scaling original context length for the Q temperature scale.

§vision: Option<VisionConfig>

Vision encoder configuration parsed from vision_config in config.json. None for text-only models.

§quantization_config: Option<QuantizationConfig>

Advertised quantization format + algorithm + per-module ignore list. Populated from config.json::quantization_config or a sibling hf_quant_config.json at parse_config time. None for un-quantized BF16/FP16 checkpoints. Consumed by the QuantFormat dispatcher (crates/spark-model/src/quant_format/) to pick the correct on-disk loader without guessing from tensor names.

§attn_gated: bool

Whether Q projection includes an output gate (Q+Gate interleaved, 2x q_dim). False for Qwen3-VL, Nemotron-H, Mistral (ungated Q).

§gdn_norm_sigmoid: bool

The GDN gated-norm’s gate activation is SIGMOID rather than SiLU.

The reference constructs its RMSNormGated with activation = output_gate_type or hidden_act, so on a checkpoint with output_gate_type: "sigmoid" (Qwen3.8-Flash-Next) BOTH the attention output gate and the GDN norm gate are sigmoid. Every other Qwen-family GDN model gates with SiLU. Found by the qwen4_exp phase-E bisect: recurrence proven correct, norm stage off at cos 0.81, and sigmoid closed it to 0.0.

§nested_config: bool

Whether config.json wraps the LLM config in a nested field (e.g., text_config). Determines weight prefix auto-detection behavior.

§mrope_section: [usize; 3]

MRoPE (multi-modal rotary position embedding) section sizes in [T, H, W] order. [0, 0, 0] = scalar RoPE (default for Qwen3.5 and earlier). Qwen3.6 uses [11, 11, 10]. Summed × 2 == rotary_dim.

§mrope_interleaved: bool

MRoPE channel layout: true = round-robin [T H W T H W …] (Qwen3.6), false = contiguous [T…T | H…H | W…W] (Qwen3-VL non-interleaved). Ignored when mrope_section == [0, 0, 0].

§weight_prefix: String§profile: bool

--profile: skip CUDA graphs, sync and time each layer.

Carried here rather than through ATLAS_PROFILE, which serve.rs used to set_var at runtime under a // SAFETY: called before any threads are spawned comment that was already false — the tokio pool, the startup blocking thread, the signal listener, the TUI thread and the OOM watchdog all exist by then, and a concurrent getenv during setenv is UB. A field on the config the model already receives has none of that hazard.

§ep_rank: usize§ep_world_size: usize§tp_rank: usize

TP rank within the TP sub-communicator. 0 if tp_world_size==1.

§tp_world_size: usize

Number of TP ranks. 1 = no TP. Composes with EP statically: attention/MLP weights are TP-sharded; MoE expert weights are EP-sharded.

§serve_max_seq_len: usize

The serve’s --max-seq-len. 0 when nobody set it (a unit test, an offline tool), which every reader must treat as “unknown” and fall back from — never as zero context. Distinct from max_position_embeddings, which is the checkpoint’s claim (1,048,576 on GLM-5.3) rather than what this process reserved memory for.

§fp8_kv_calibration_tokens: usize

Number of warmup tokens for online FP8 KV scale calibration. 0 = disabled (use static scales from checkpoint or uncalibrated 1.0).

§fp8_kv_headroom: f32

Headroom multiplier on the first-observe absmax when freezing the online FP8 KV scale (--fp8-kv-headroom, default 2.0). The first observe sees only the first prefill chunk, so the frozen scale covers headroom× its observed max — later tokens that grow don’t clip, at <1 bit of precision.

§final_logit_softcapping: f32

Final logit softcapping: logits = cap * tanh(logits / cap). 0.0 = disabled (default for all models except Gemma-4 which uses 30.0).

§embed_scale: f32

Embedding scale factor: embeddings *= scale after lookup. 0.0 = disabled (default). Gemma models use sqrt(hidden_size).

§scoring_func: String

MoE routing activation. “” = default softmax. “sigmoid” = DeepSeek-V3 / MiniMax-M2 style: raw gate logits pass through sigmoid to produce per-expert scores in (0,1), independent (not normalized across experts). Top-k selection may use a bias term (see moe_routing_bias).

§use_routing_bias: bool

If true, a per-expert e_score_correction_bias tensor is added to routing scores for top-k selection only (not dispatch weighting). This is the DeepSeek-V3 loss-free balancing trick. The bias tensor itself lives in the checkpoint (typically one [num_experts] vector per MoE layer).

§qk_norm_type: String

QK normalization granularity. “” = none (Qwen3-Next default). “per_layer” = each attention layer has its own learned q_layernorm / k_layernorm weight of shape [head_dim], applied after Q/K projection and before RoPE (MiniMax M2).

§num_mtp_modules: usize

Number of sequential MTP draft modules. 0 = no MTP. 1 = existing Atlas MTP path (Qwen3.5). 3 = MiniMax M2 (each module is a single transformer layer that predicts one future token).

§mtp_transformer_layers: usize

Transformer layers per MTP module. 1 for MiniMax M2 (3 modules × 1 layer = 3 future-token predictors).

§rotary_dim: usize

Explicit rotary dimension from config (bypasses partial_rotary_factor computation). MiniMax M2 ships rotary_dim: 64 while head_dim=128, so the rotary factor is 0.5 — we honor the explicit int value when present for byte-exact rope dim.

§dflash_capture_layers: Vec<usize>

Target-model layer indices to capture intermediate hidden states from for DFlash speculative decoding. Sourced from the drafter’s dflash_config.target_layer_ids (e.g., [1, 10, 19, 28, 37] for Qwen3.6-35B-A3B-DFlash). Empty when DFlash is disabled — its presence gates TransformerModel::dflash_hidden_save allocation and the per-layer capture hooks. Order matters: shallow-to-deep concatenation is what the drafter’s fc projection expects.

§dflash_gamma: Option<usize>

Resolved DFlash drafter γ (block size), set by the factory alongside dflash_capture_layers. Sizes the SSM verify intermediate pools at the ACTUAL K = γ+1 instead of the legacy 17-wide ceiling — at γ=8, C=8 that ceiling alone cost ~12 GB of pool (2026-08-19 256K/C8 boot ledger). None = DFlash inactive (or unknown → 17-wide fallback).

§adapter_max_rank: usize

LoRA adapter rank ceiling (--max-lora-rank). 0 = LoRA disabled. Set programmatically before model build (never parsed from the HF config.json); the only consumer is BufferSizes, which sizes the adapter delta scratch from it. adapter_* naming avoids the MLA *lora_rank collision (config.rs:182-207).

Implementations§

Source§

impl ModelConfig

Source§

impl ModelConfig

Source

pub fn eos_ids(&self) -> Vec<u32>

Every configured stop-token id, primary first.

Falls back to vec![eos_token_id] when eos_token_ids was never populated, so a hand-built ModelConfig and a scalar-EOS checkpoint both behave exactly as before.

Source

pub fn is_eos(&self, id: u32) -> bool

Does this token id terminate generation?

Source

pub fn gqa_ratio(&self) -> usize

GQA ratio: number of Q heads per KV head.

Source

pub fn layer_type_at(&self, layer_idx: usize) -> Option<LayerType>

Layer type for a given layer index. Falls back to full_attention_interval if layer_types is empty. Layer kind for ANY index in the checkpoint, including layers past the text stack.

layer_type covers the text stack only. Indices >= num_hidden_layers are MTP/NextN layers and resolve through mtp_layer_types; that is what lets GLM-5.3’s layer 45 be represented as the sparse-attention block it actually is, rather than being appended to the text stack and silently swept into every text-layer loop.

Source

pub fn sparse_attention_layers(&self) -> Vec<usize>

Layers (text stack only) whose mixer is deepseek_sparse_attention.

Source

pub fn has_sparse_attention(&self) -> bool

True when any layer — text stack or MTP — needs the sparse-attention indexer. Scheduling and cache sizing both key off this, so it must not be answered from layer_types alone.

Source

pub fn layer_type(&self, layer_idx: usize) -> LayerType

Source

pub fn num_attention_layers(&self) -> usize

Number of attention (KV-cache-consuming) layers: full, sliding, and sparse. All three write to the paged KV cache — only which keys they read differs (all / a window / a runtime-selected top-k) — so every consumer sized from this count — KV pool num_layers, attn_layer_dtypes, loader layer_kv_dtypes indexing — must see them all. Step 3.7 is the only model emitting SlidingAttention layer types (12 full + 33 sliding); counting full-only there undersized the dtype vec and panicked the loader at layer 13.

🪤 The same omission recurred for SparseAttention: GLM-5.3-Flash is 34 linear_attention + 11 deepseek_sparse_attention, so a full/sliding filter returned 0 and the KV pool came out zero-sized (“KV cache block size is zero”, measured 2026-08-28). Delegating to LayerType::is_attention is what keeps this honest: the predicate lives next to the enum, so a new variant is answered in one place.

Source

pub fn num_ssm_layers(&self) -> usize

Number of SSM (linear attention) layers.

Source

pub fn has_recurrent_state(&self) -> bool

Whether this model carries recurrent (SSM / linear-attention) state — the honest capability signal for the SSM snapshot tiers. Derived from Self::num_ssm_layers so the config-level predicate and the runtime pool predicate (ssm_pool.num_ssm_layers > 0) agree by construction (SSOT). A pure-attention model (dense or MoE) returns false: requesting an SSM tier for it must fail fast, never silently no-op.

Source

pub fn has_experts(&self) -> bool

Whether this model has MoE routed experts — the capability signal for the expert-streaming tier. Keyed on config, never on observed expert tensors (EP ranks legitimately own zero local expert tensors).

Source

pub fn rotary_dim(&self) -> usize

Rotary embedding dimension.

Priority:

  1. Explicit rotary_dim field (MiniMax M2 — integer in config.json).
  2. partial_rotary_factor * head_dim (Qwen3/Gemma-4 convention — float).
Source

pub fn ssm_qkvz_size(&self) -> usize

SSM projection output size: Q + K + V + Z concatenated.

Source

pub fn ssm_qkv_size(&self) -> usize

SSM QKV projection output size (without Z): Q + K + V.

Source

pub fn ssm_z_size(&self) -> usize

SSM Z gate projection output size.

Source

pub fn ssm_ba_size(&self) -> usize

SSM beta+alpha projection output size.

Source

pub fn local_expert_range(&self) -> (usize, usize)

Range of expert indices local to this EP rank. Returns (start, end) where start is inclusive and end is exclusive.

Source

pub fn is_local_expert(&self, expert_id: usize) -> bool

Whether the given expert ID is local to this EP rank.

Source

pub fn tp_shard_range(&self, total: usize) -> (usize, usize)

Range [start, end) of a total-sized dimension owned by this TP rank. total must be divisible by tp_world_size. Returns (0, total) when TP is disabled.

Source

pub fn tp_shard_dim(&self, total: usize) -> usize

Per-rank shard size for a total-sized dimension under TP.

Source

pub fn layer_prefix(&self, layer_idx: usize) -> String

Weight key prefix for layer-level weights. Returns "model.layers" for flat models (qwen3_next), or "model.language_model.layers" for conditional generation models (qwen3_5_moe).

Source

pub fn capabilities(&self) -> ModelCapabilities

Derive model-agnostic capabilities from this config.

Source

pub fn is_qwen35(&self) -> bool

Factory use only. Prefer config.attn_gated or config.capabilities().

Source

pub fn is_qwen35_dense(&self) -> bool

Factory use only.

Source

pub fn is_qwen3_vl(&self) -> bool

Factory use only.

Recognises the upstream qwen3_vl_moe model_type (Qwen3-VL MoE) and Qwen3.5-VL — which ships with model_type = "qwen3_5" plus architectures = ["Qwen3_5ForConditionalGeneration"] and a populated vision_config block. The vision_config presence is the durable signal: the trunk model_type stays qwen3_5 whether the checkpoint is text-only or VL, but VL ships an extra vision encoder which the parser exposes as config.vision.

Source

pub fn skip_lm_head_quantization(&self) -> bool

Whether to skip NVFP4 quantization of the LM head. MLA models (kv_lora_rank > 0) lose logit precision under NVFP4. Gemma-4 dense (31B): the LM head ties to BF16 embed_tokens whose rows have heavy outliers (final_norm.weight max=510, several embedding rows in similar range). The runtime BF16→NVFP4 path uses a single per-tensor absmax for scale2, which forces a coarse scale that loses ~7 bits in normal-magnitude rows. For a 262 144-row vocab matrix that compounds into the 0.14-margin argmax flip on creative prompts (verified 2026-05-01 via FP32 lm_head bisection: NVFP4 output had top1= a 21.85 vs FP32 BF16 view top1= a 21.85 — quantization noise was visible in the SAME logit channel that flipped the tiebreak). Skipping the runtime quantization keeps the LM head as plain BF16 dense; the FP32 lm_head path (gated by ATLAS_GEMMA4_FP32_LMHEAD=1) can then act on full-precision weights without the NVFP4 floor.

Source

pub fn mamba2_d_inner(&self) -> usize

Mamba-2 d_inner = mamba_num_heads * mamba_head_dim.

Source

pub fn mamba2_d_xbc(&self) -> usize

Mamba-2 d_xBC = d_inner + 2 * n_groups * ssm_state_size. This is the dimension that goes through conv1d (x + B + C concatenated).

Source

pub fn mamba2_in_proj_size(&self) -> usize

Mamba-2 in_proj output size = z + xBC + dt.

Source

pub fn ssm_h_state_bytes(&self) -> usize

Per-layer SSM hidden state size in bytes (FP32). Dispatches on SSM architecture: Mamba-2 vs GDN, using config fields.

Source

pub fn ssm_conv_state_bytes(&self) -> usize

Per-layer SSM conv state size in bytes (FP32).

Source

pub fn ssm_state_norm_dims(&self) -> (usize, usize, usize)

SSM state normalization dimensions: (num_heads, k_dim, v_dim). Used by the state normalization kernel to prevent drift.

Source

pub fn moe_input_size(&self) -> usize

MoE expert input dimension: latent size if LatentMoE, else hidden_size.

Source

pub fn moe_intermediate_size_for(&self, layer: usize) -> usize

Routed expert intermediate size for layer i.

Puzzle checkpoints prune channels non-uniformly across MoE layers; look up moe_intermediate_sizes[i] when populated, else the scalar.

Source

pub fn num_experts_per_tok_for(&self, layer: usize) -> usize

Top-K experts per token for layer i (Puzzle per-block schedule).

Source

pub fn max_moe_intermediate_size(&self) -> usize

Max routed intermediate across all layers (buffer / scratch sizing).

Source

pub fn num_moe_layers(&self) -> usize

Number of MoE-only layers (Nemotron-H).

Source

pub fn kv_only_prefix_cache_is_safe(&self) -> bool

Whether the radix prefix cache captures every state needed to resume this model exactly. Preflight SSOT for build_prefix_cache.

Source

pub fn kv_only_swap_out_is_safe(&self) -> bool

Whether a sequence may be swapped out to the --swap-space-gb pool and restored from it. Preflight SSOT for resolve_swap_space_gb.

save_sequence_state_dispatch writes KV blocks plus the SsmLayerState of each LayerType::LinearAttention layer, and nothing else; the swap-out then calls free_sequence, which hands every remaining per-layer state to #821’s release_state. A model that is not KV-complete therefore resumes with a freshly ZEROED pool behind a KV image that assumes a populated one — a silently wrong answer, not a crash. Distinct from the prefix-cache predicate because they are distinct guarantees; they happen to have the same answer today.

Trait Implementations§

Source§

impl Clone for ModelConfig

Source§

fn clone(&self) -> ModelConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ModelConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for ModelConfig

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,