spark_model/layers/ops/model_levers.rs
1// SPDX-License-Identifier: AGPL-3.0-only
2
3//! Model-side kernel-path levers, resolved once and then carried.
4//!
5//! # ★ THE ENVIRONMENT IS READ EXACTLY ONCE PER PROCESS. KEEP IT THAT WAY.
6//!
7//! Every `ATLAS_*` variable below is a process constant: nothing mutates the
8//! environment after start (the runtime `set_var` that once could was
9//! deliberately removed — see `main_modules/serve_load.rs` and `config.rs`).
10//! So resolving them more than once is pure waste, and on a hot path it is
11//! worse than waste:
12//!
13//! * `std::env::var` allocates a `String` per read, and
14//! * it takes the PROCESS-WIDE environment lock, so concurrent readers
15//! SERIALISE against each other.
16//!
17//! MEASURED on GB10: one resolve of the ~30 variables here costs 0.57 us
18//! single-threaded but **4.00 us at 8 threads and 5.76 us at 16** — the cost
19//! grows with concurrency, which makes it invisible to any single-stream
20//! benchmark. `from_env()` was called **32,513 times in one
21//! `concurrency-sweep`** (48 layers x ~680 prefills) while its own doc claimed
22//! it was "called once, when the model is built".
23//!
24//! **The rule for this module and anything like it:** read the environment in
25//! ONE place, at ONE time, and pass the resolved value down. Use
26//! [`ModelLevers::get`] for the process-wide copy; take `levers` from the
27//! `ForwardContext` or the model when you already have one. If you find
28//! yourself calling anything named `*_from_env`, `resolve_*` or `*_env()`
29//! inside a function that runs per token, per layer, per forward pass or per
30//! request, that is the bug this note exists to prevent.
31//!
32//! The second of the two lever categories on [`crate::layer::ForwardContext`]:
33//!
34//! * [`super::GemmDispatch`] — which GEMM implementation each projection takes.
35//! * [`ModelLevers`] — everything else the model's kernel paths branch on:
36//! the SSM/GDN recurrence variant, FFN routing, MoE quantization, LoRA
37//! application mode, diagnostics.
38//!
39//! Both were `OnceLock<bool>` statics reading `ATLAS_*` at first touch. Two
40//! problems with that, and only the first is about hot-swap:
41//!
42//! 1. A static outlives the model whose flags it encodes. Load a second model
43//! whose recipe sets different levers and the process keeps taking the
44//! previous model's branches — silently, because a cached `bool` cannot
45//! report that it is stale.
46//! 2. It hides the dependency. A function that reads the environment through a
47//! static declares nothing in its signature, cannot be exercised with a
48//! different configuration without mutating the process, and gives the
49//! compiler nothing to check.
50//!
51//! Carrying it fixes both, and a site that forgets the field fails to build.
52
53/// Kernel-path levers for one loaded model.
54///
55/// Plain `Copy` data resolved from the environment at model construction. Group
56/// membership follows the subsystem the lever steers, so a reader can see at a
57/// glance which part of the forward pass a flag reaches.
58// `Eq` is deliberately absent since `draft_conf_tau` joined: it is an f32
59// threshold. Comparing two resolutions is a test-only need and `PartialEq`
60// covers it.
61#[derive(Clone, Copy, Debug, PartialEq, Default)]
62pub struct ModelLevers {
63 // ── SSM / GDN recurrence ──
64 /// Keep GDN recurrent state in registers across the prefill chunk loop.
65 /// Default ON (the fold that shipped in PR #369, −7.25 % wall); the env var
66 /// is an opt-OUT, which is why the field is stored positively and the
67 /// resolution inverts it.
68 pub gdn_regresident: bool,
69 /// Batched FLA path for multi-sequence GDN decode.
70 pub gdn_batched_fla: bool,
71 /// WY17 GDN recurrence variant. Ships ON; `ATLAS_GDN_WY17=0` opts out.
72 pub gdn_wy17: bool,
73 /// WY-N GDN recurrence variant. Ships ON; `ATLAS_GDN_WYN=0` opts out.
74 pub gdn_wyn: bool,
75
76 // ── FFN / MoE ──
77 /// Lossless single-warp decode GEMV (`w4a16_gemv_sw`, `w4a16_gemv_dual_sw`).
78 /// Ships ON; `ATLAS_NO_GEMV_SW=1` restores the 64-thread kernels.
79 pub gemv_sw: bool,
80 /// Route decode FFN through the tile GEMM rather than the scalar GEMV.
81 pub decode_ffn_via_gemm: bool,
82 /// Small-M FFN GEMM tile shape. Ships ON; `ATLAS_FFN_SMALLM=0` opts out.
83 pub ffn_small_m: bool,
84 /// FP4 holo layout for the MoE down projection.
85 pub holo_moe_down_fp4: bool,
86 /// FP4 holo layout for the MoE gate/up projections.
87 pub holo_moe_gateup_fp4: bool,
88 /// Collect per-layer MoE expert-union statistics. Diagnostic.
89 pub moe_union_stats: bool,
90 /// `ATLAS_FP32_ROUTING=1` — emit the MoE-input norm in FP32 so the gate
91 /// GEMM routes at full precision, removing the bf16-store rounding that
92 /// flips experts on gfx1151. Read once per LAYER per DECODE TOKEN from
93 /// six call sites via `MoeFfnLayer::fp32_routing_active`, which also
94 /// checks four weight/kernel preconditions — the lever is only the last
95 /// term of that conjunction, which is why it lives here and the
96 /// preconditions stay on the layer.
97 pub fp32_routing: bool,
98 /// `ATLAS_FP32_GATE=1` — the batched-gate sibling of
99 /// [`Self::fp32_routing`].
100 pub fp32_gate: bool,
101 /// `ATLAS_FRANKENSTEIN_DECODE_VIA_PREFILL=1` — route the five DFlash
102 /// capture layers' decode through the PREFILL MoE kernel, on the
103 /// hypothesis that the decode MoE kernel is the dominant cause of low
104 /// drafter acceptance. ~250 us per capture layer, so ~1.25 ms/token
105 /// against a ~58 ms/token decode. Diagnostic; non-capture layers are
106 /// untouched.
107 pub frankenstein_decode_via_prefill: bool,
108 /// `ATLAS_K2_DIAG=1` — K=2 routed-decode diagnostics.
109 pub k2_diag: bool,
110
111 // ── Dense FFN: which GEMM each prefill/decode arm takes ──
112 //
113 // These twelve were read with `std::env::var_os` from inside
114 // `DenseFfnLayer::forward` and `forward_prefill_inner`, i.e. once per
115 // LAYER per decode token and once per layer per prefill chunk — and one
116 // of them from inside a per-GEMM macro, so three times per layer. No
117 // allocation (that is `var_os`'s advantage over `var`) but the same
118 // process-wide environment lock, which serialises concurrent decode
119 // threads. The load-time readers in `finalize_q4k_load` and
120 // `finalize_nvfp4_mmq_load` are deliberately left where they are: they
121 // run once per weight, at load.
122 /// Split SiLU+down on the decode path: `silu_mul` into `gate_out`, then a
123 /// separate `w4a16_decode_gemv` for down. Ships ON;
124 /// `ATLAS_NO_DECODE_SPLIT_SILU` (presence) restores the fused kernel.
125 /// A LoRA adapter pins this path on regardless — the fused alternative
126 /// never materialises `silu(gate)*up`, which the down delta must
127 /// contract over — so the call site is `levers.decode_split_silu ||
128 /// self.lora.is_some()`.
129 pub decode_split_silu: bool,
130 /// `ATLAS_BF16_TC_PREFILL` (presence) — BF16 tensor-core prefill GEMM.
131 /// Read here only; the usable gate is derived at the call site AFTER
132 /// v1/v2 selection, from the handle actually launched. Gating on v1's
133 /// handle while dispatching v2 admitted launches of a kernel the target
134 /// may not carry.
135 pub bf16_tc_prefill: bool,
136 /// `ATLAS_FP8_M64_PREFILL` (presence) — m16n8k32 e4m3 M64 prefill GEMM,
137 /// ~1.47x vs v2 BF16. Lossy (cosine 0.9997), so opt-in only.
138 pub fp8_m64_prefill: bool,
139 /// `ATLAS_INT8_PREFILL` (presence) — requant→`int8_gemm_faith2` prefill
140 /// (cosine 0.999978 vs the host full-precision dequant GEMM).
141 pub int8_prefill: bool,
142 /// `ATLAS_INT8_FAITH5` (presence) — int32 per-sub-block accumulation,
143 /// which breaks the MMA→scale dependency chain. Same kernel signature
144 /// and launch geometry as faith2, so it is a handle swap.
145 pub int8_faith5: bool,
146 /// Vendored llama NVFP4 W4A4 MMQ for the gate/up prefill GEMMs
147 /// (~80 TFLOP/s vs t_m128's ~51). Ships ON;
148 /// `ATLAS_NO_FFN_NVFP4_MMQ` (presence) is the kill switch.
149 pub ffn_nvfp4_mmq: bool,
150 /// The same MMQ arm for the down projection — t_m128 runs the narrow-N
151 /// down at only ~34 TFLOP/s. Ships ON; `ATLAS_NO_FFN_NVFP4_MMQ_DOWN`
152 /// (presence) is the kill switch. Separate from
153 /// [`Self::ffn_nvfp4_mmq`] because down is the heavy-tailed projection
154 /// (W4A4 cosine 0.9961) and gets its own gate.
155 pub ffn_nvfp4_mmq_down: bool,
156 /// `ATLAS_FFN_MMQ` (presence) — Q4_K MMQ prefill arm.
157 pub ffn_mmq: bool,
158 /// `ATLAS_FFN_MMQ_DOWN_Q4K` (presence) — keep the down projection ON
159 /// Q4_K instead of the near-lossless faith2 NVFP4 hybrid.
160 ///
161 /// Stores the POSITIVE of a variable whose call site reads the negative
162 /// (`!levers.ffn_mmq_down_q4k`), the same shape as
163 /// [`Self::moe_legacy_pertoken_decode`]. down = SiLU(gate)*up is
164 /// heavy-tailed and Q4_K superblock scaling clips it — BFCL `multiple`
165 /// −4.0%, which is why llama promotes only down→Q6_K.
166 pub ffn_mmq_down_q4k: bool,
167 /// `ATLAS_FP4_PREFILL` (presence) — native W4A4 FP4 tensor cores
168 /// (sm_121a), NVFP4 weights used directly with no requant. Lossy
169 /// (cos ~0.99 vs fp32).
170 pub fp4_prefill: bool,
171 /// The v2 BF16 t_m128 prefill kernel — faster and bit-identical to v1.
172 /// Ships ON; `ATLAS_DISABLE_PREFILL_V2` (presence) forces v1 so the two
173 /// can be compared for TTFT in one binary.
174 pub prefill_v2: bool,
175
176 // ── MoE routed prefill ──
177 //
178 // Read once per LAYER per prefill chunk from `forward_prefill_routed`,
179 // and the CUTLASS gate is asked TWICE per call through a free function.
180 /// `ATLAS_HOLO_MOE_GROUPED_CUTLASS=1` — single-launch CUTLASS grouped
181 /// NVFP4 gate_up. Off by default; unset falls back to the hand-rolled
182 /// fused FP4/FP8 grouped kernels.
183 pub moe_grouped_cutlass: bool,
184 /// `ATLAS_HOLO_MOE_GROUPED_DOWN=1` — take the down projection through
185 /// the same CUTLASS grouped path. Requires
186 /// [`Self::moe_grouped_cutlass`]; a separate gate because down consumes
187 /// the already-expert-contiguous post-SiLU output and needs no gather.
188 pub moe_grouped_down: bool,
189 /// `ATLAS_MOE_PREFILL_EXACT_TILES=1|0` overrides the tile bound;
190 /// `None` (unset) defers to the checkpoint — the win was measured on
191 /// NVFP4, so the default is scoped to where it was measured.
192 ///
193 /// Tri-state on purpose. Measured: exact_tiles ON gave p90 +4.9% against
194 /// a +5.0% limit (0.1% from failing the gate) and OFF gave p90 −5.0%,
195 /// while the median barely moved either way (+0.1% vs −0.9%). Only the
196 /// tail shows it, so both directions must stay reachable. Graph capture
197 /// forces it off regardless — the bound is read back from device memory.
198 pub moe_prefill_exact_tiles: Option<bool>,
199 /// `ATLAS_MOE_PREFILL_MAX_LOAD_FACTOR=<n>` — cap the per-expert tile
200 /// bound at n times the average when exact tiles are off. `None` (unset
201 /// or `0`) means the worst case.
202 pub moe_prefill_max_load_factor: Option<usize>,
203 /// `ATLAS_MOE_PREFILL_ZERO=1` — memset the grouped scratch before
204 /// dispatch. Implied by EP (`ctx.comm.is_some()`). In non-EP the sort
205 /// produces a dense permutation over exactly the rows the grouped
206 /// kernels write, so skipping the clear removes ~138 MB/layer on Holo.
207 pub moe_prefill_zero: bool,
208 /// `ATLAS_MOE_PREFILL_FP8_DOWN=1` — FP8 grouped GEMM for the routed
209 /// down projection.
210 pub moe_prefill_fp8_down: bool,
211
212 // ── Nemotron prefill (Mamba2 SSM + MoE) ──
213 //
214 // Nine reads across three functions, each once per LAYER per PREFILL
215 // CHUNK. All are PRESENCE-gated — the sites spelled them
216 // `std::env::var(..).is_err()` / `.is_ok()`, so `=0` neither arms an
217 // opt-in nor re-enables an opt-out. Resolution uses `var_os`, which
218 // differs from `var` only for a non-UTF-8 value: `var` reports that as
219 // absent, `var_os` as present. The difference lands on the safe side of
220 // every one of these.
221 /// W4A4 native-FP4 SSM projections at N >= 512. Ships ON;
222 /// `ATLAS_NO_SSM_W4A4` (presence) is the kill switch.
223 pub ssm_w4a4: bool,
224 /// The chunked SSD scan. Ships ON; `ATLAS_NO_SSD` (presence) falls back
225 /// to the sequential scan. Gated additionally on `ssd_scan_fits`, since
226 /// Nano-30B's state_size=128 overflows the shared-memory budget that
227 /// Puzzle-75B's 96 fits — which is why that never surfaced until it did.
228 pub ssd: bool,
229 /// The persistent SSM prefill kernel, which keeps H in shared memory and
230 /// is only reachable when SSD is unavailable. Ships ON;
231 /// `ATLAS_NO_SSM_PERSISTENT` (presence) disables, for a same-binary A/B
232 /// against the sequential scan.
233 pub ssm_persistent: bool,
234 /// Zero the grouped-MoE intermediate arena buffers before dispatch.
235 /// Ships ON; `ATLAS_MOE_NO_ZERO_INTERMEDIATES` (presence) skips.
236 ///
237 /// Defence in depth: these buffers are reused across requests and nothing
238 /// else clears them, so a row a future change fails to write would leak
239 /// the PREVIOUS request's activations rather than merely being wrong.
240 /// Asked twice in one call before this — once for up, once for down.
241 pub moe_zero_intermediates: bool,
242 /// `ATLAS_MOE_MAX_M_TILES_ESTIMATE` (presence) — restore the old
243 /// average-based tile bound. A/B only; the comment at the site says it
244 /// is NOT safe to serve on, because the estimate can under-bound the
245 /// worst case of one expert taking every routed token.
246 pub moe_max_m_tiles_estimate: bool,
247 /// `ATLAS_MOE_W4A4` (presence) — W4A4 grouped up-projection at N >= 512.
248 pub moe_w4a4: bool,
249 /// W4A4 for the shared-expert UP projection at N >= 512. Ships ON;
250 /// `ATLAS_NO_SHARED_W4A4` (presence) is the kill switch.
251 pub shared_w4a4: bool,
252 /// `ATLAS_SHARED_W4A4_DOWN` (presence) — the DOWN half of the same, and
253 /// a SEPARATE opt-in: down is the heavy-tailed projection, so it does not
254 /// inherit [`Self::shared_w4a4`].
255 pub shared_w4a4_down: bool,
256
257 // ── Attention ──
258 /// Contiguous-attention path for the DFlash head.
259 pub dflash_contig_attn: bool,
260
261 // ── LoRA ──
262 /// Apply LoRA eagerly at load instead of at each forward.
263 pub lora_eager: bool,
264 /// Allow hot rotation of LoRA adapters.
265 pub lora_rotate: bool,
266
267 // ── Diagnostics ──
268 /// K=4 chain-widening diagnostics.
269 pub k4_diag: bool,
270 /// Per-layer hidden-state norm dumps on the Gemma-4 decode path. Heavy —
271 /// one device-to-host copy per layer.
272 pub gemma4_diag: bool,
273 /// `ATLAS_DFLASH_DEBUG_DUMP_FULL=1` — the model-side half of the DFlash
274 /// full dump: emit the whole token sequence ONCE so a Python reference
275 /// can run the same tokens through HF transformers.
276 ///
277 /// ★ The SAME variable that [`crate::layers::dflash_head::levers::DFlashLevers::debug_dump_full`]
278 /// carries. Two structs, one flag — deliberately, because the two halves
279 /// of the dump are armed together by design and the head is not reachable
280 /// from `TransformerModel` (`proposer` is a `dyn DraftProposer`, so there
281 /// is nothing to read the head's levers through without a downcast).
282 /// `the_two_halves_of_the_dflash_dump_agree` pins that the two
283 /// resolutions cannot drift, which is what makes the duplication safe —
284 /// an unchecked second spelling of one lever is how
285 /// `ATLAS_DSPARK_ANCHOR_BIAS` came to have two implementations.
286 pub dflash_debug_dump_full: bool,
287 /// `ATLAS_MTP_DEBUG_NORMS=1` — per-stage norm dumps inside the MTP
288 /// drafter's `forward_one`, which asked for it FOUR times per drafted
289 /// token, each read only to decide whether to do nothing.
290 pub mtp_debug_norms: bool,
291 /// `ATLAS_MTP_DRAFT_CONF=<t>` — confidence floor for submitting drafts
292 /// to verification, clamped to `[0.0, 0.99]`. `0.0` (unset) disables.
293 ///
294 /// When the drafter's chain confidence (the min top-1 softmax prob
295 /// across one propose's drafts) is below this, the drafts are discarded
296 /// and the next step decodes serially, skipping a verify that would most
297 /// likely reject. Economics at K=1 on the 35B MoE: verify ~35 ms for
298 /// 1+accepted tokens against decode+propose ~21 ms for 1, so a draft is
299 /// only worth verifying at p(accept) >~ 0.66. STAGED OFF pending its
300 /// measured A/B.
301 ///
302 /// Three of its four readers asked per propose whether the feature was
303 /// on, i.e. paid the environment lock to learn it was off. The fourth,
304 /// `MtpHead::last_confidence`, is reached only when it is already ON, so
305 /// it keeps its own read and its own contract — see the note there.
306 pub draft_conf_tau: f32,
307 /// `ATLAS_SSM_SAVE_DUMP` (presence) — the CBD scratch/SSM-state
308 /// fingerprint probe. Asked THREE times per decode step by the decode
309 /// path alone, each read only to decide whether to do nothing.
310 pub ssm_save_dump: bool,
311
312 // ── Batched decode dispatch ──
313 //
314 // Five reads in `decode_batch_dispatch` / `decode_batch_compute_main`,
315 // once per BATCHED DECODE STEP. Note the spellings differ between
316 // neighbouring lines of the same function — two are truthy
317 // (`"1"` or `"true"`, case-SENSITIVE) and three are strict `"1"` — and
318 // each field keeps the one its site had.
319 /// `ATLAS_MLA_PERSEQ_FALLBACK=1|true` — route MLA batches through the
320 /// per-sequence path instead of the batched one.
321 pub mla_perseq_fallback: bool,
322 /// `ATLAS_HC_PERSEQ_DECODE=1` — per-sequence hyper-connection decode.
323 /// ORed with `qsa_active`, and the routing decision is resolved ABOVE
324 /// the EP branch on purpose: it used to sit below, so under EP a
325 /// QSA-active batch returned before reaching the gate, landed on the
326 /// batched multi-seq path, and died on its guard.
327 pub hc_perseq_decode: bool,
328 /// `ATLAS_DECODE_BATCH_LOG=1` — log the batch's slot/position vectors
329 /// each step.
330 pub decode_batch_log: bool,
331 /// `ATLAS_MS_PROFILE=1` — per-phase multi-seq profiling, which forces
332 /// eager execution so the per-phase syncs are legal under capture.
333 ///
334 /// NOT [`Self::ssm_ms_profile`], which is `ATLAS_SSM_MS_PROFILE`. Two
335 /// different variables one underscore apart, both live.
336 pub ms_profile: bool,
337 /// `ATLAS_CONC_HSD=1|true` — per-sequence hidden-state dump, to localize
338 /// where `pos >= 1` diverges from `pos 0` in concurrent batched decode.
339 pub conc_hsd: bool,
340
341 // ── Decode graph capture ──
342 /// `ATLAS_EP_GRAPHS=1|true` — allow CUDA-graph capture under expert
343 /// parallelism. The EP all-reduce queues ncclSend/Recv plus a local add
344 /// on the capture stream and NCCL >= 2.9 supports capture, so this MAY
345 /// capture cleanly; env-gated so a deploy can revert instantly if
346 /// capture crashes or replay hangs.
347 pub ep_graphs: bool,
348 /// `ATLAS_GDN_DECODE_GRAPH=1|true` — capture the whole single-token GDN
349 /// HeadParallel TP decode forward (~130 kernels plus the per-layer TP
350 /// all-reduces) into one replayable graph. Default OFF.
351 pub gdn_decode_graph: bool,
352
353 // ── Attention (cont.) ──
354 /// BF16 tensor-core attention projections: dequant FP4 to BF16 and use a
355 /// BF16 MMA instead of the default path, which crushes activations to FP8
356 /// E4M3. Removes the FP8 prefill perturbation on those projections.
357 pub bf16_tc_proj: bool,
358 /// The checkpoint's attention weights are ALREADY Hadamard-rotated at load
359 /// (`TQ_PLUS_WEIGHT_ROTATION`), so the runtime must not rotate again.
360 ///
361 /// A property of the loaded checkpoint, and the SSOT for it. It previously
362 /// had FIVE implementations of the same `=1`-or-`true` test — four raw
363 /// `std::env::var` calls on attention paths (one per attention layer per
364 /// DECODE TOKEN in `decode/attention_forward.rs`, one per layer per
365 /// batched decode step in `multi_seq/attn.rs`, two per layer per prefill
366 /// chunk) plus a fifth in the weight loader, whose `#[allow(dead_code)]`
367 /// was stale — `attention_arms.rs` calls it. Reading this per token cost
368 /// an allocation and the process-wide environment lock on the hottest path
369 /// in the model, and five copies of one predicate is how a flag ends up
370 /// decoded two different ways in one binary.
371 pub weight_pre_rotated: bool,
372
373 // ── SSM / GDN decode ──
374 // These five ran on the batched-decode path — per SSM layer per decode
375 // step, ~6-7M environment reads per sweep on a 36-SSM-layer hybrid, the
376 // largest raw count in this crate. Three are diagnostics that are off in
377 // every shipped configuration, and were paying a `String` allocation and
378 // the process-wide environment lock to say so on every layer of every
379 // token. Their neighbour `ssm_tc_proj_min_n()` in the same file was
380 // already `OnceLock`'d with the note "Read ONCE — this site runs under
381 // graph capture", so these were an inconsistency, not a design.
382 /// Per-step multi-sequence SSM profiling dump.
383 pub ssm_ms_profile: bool,
384 /// Finer per-sub-step SSM profiling inside the batched recurrence.
385 pub ssm_detail_profile: bool,
386 /// Ships ON: use the batch-4 GEMV tier for the SSM projections when the
387 /// kernel is resolved and n <= 16. `ATLAS_SSM_GEMV_BATCH4=0` opts out.
388 pub ssm_gemv_batch4: bool,
389 /// Fuse the GDN conv with the F32 norm when the head geometry allows.
390 pub gdn_fused_conv: bool,
391 /// Take the pre-token-major MoE decode kernel. The field stores the
392 /// POSITIVE of the variable's name, so the call site reads
393 /// `!levers.moe_legacy_pertoken_decode` for the default token-major path —
394 /// the inversion lives here, once, rather than at the branch.
395 pub moe_legacy_pertoken_decode: bool,
396 /// Configured max decode batch (`--max-batch-size`), the reference count
397 /// the split-K attention split count is pinned to. Not from the
398 /// environment: `TransformerModel::new` writes it from the serve arg.
399 ///
400 /// It pins DETERMINISM — the online-softmax split-merge is
401 /// non-associative, so a sequence decoded alone must see the same
402 /// reduction tree as one co-batched with fifteen others. Held in a
403 /// `OnceLock` it was also idempotent, so a second model with a different
404 /// max batch would silently keep the first model's split count.
405 pub max_decode_seqs: u32,
406 /// `ATLAS_MTP_SHADOW_TOPK=k` (0 = off, clamped to 8): the drafter D2Hs
407 /// its logits and logs the top-k candidates. Observational only.
408 pub shadow_topk: usize,
409 /// `ATLAS_KV_POISON=1` — fill a fresh KV block with NaN instead of zero,
410 /// the discriminator for the "unwritten fresh tail block read"
411 /// hypothesis. A diagnostic that changes what the kernels READ, so it
412 /// must not leak across a swap.
413 pub kv_poison: bool,
414 /// MTP drafter context policy (`ATLAS_NO_DRAFTER_CONTEXT` /
415 /// `ATLAS_DRAFTER_PREFILL_ONLY`), resolved and logged once per model.
416 /// The two halves are coupled — prefill without carry is a measured
417 /// −927 ms/turn loss — so they travel as one value.
418 pub drafter: crate::model::drafter_context::DrafterContext,
419}
420
421/// How the levers above are READ. Private: the environment is touched in
422/// exactly one place, and `ModelLevers::get` is the only way out of it.
423#[path = "model_levers_resolve.rs"]
424mod resolve;
425
426#[cfg(test)]
427#[path = "model_levers_tests.rs"]
428mod tests;
429
430/// ★ Where the environment may be read at all. Kept next to the levers it
431/// exists to protect, not inside `tests` — it guards other modules too.
432#[cfg(test)]
433#[path = "hot_path_env_guards.rs"]
434mod hot_path_env_guards;