atlas_kernels/lib.rs
1// SPDX-License-Identifier: AGPL-3.0-only
2
3#![deny(warnings)]
4#![deny(clippy::all)]
5
6//! Atlas CUDA kernel PTX modules.
7//!
8//! Single source of truth for embedded PTX. The `spark-runtime`
9//! (pure Rust engine) and benchmarks consume these.
10//!
11//! PTX modules are grouped by [`KernelTarget`] — each `(H, M_q)`
12//! tuple maps to a distinct set of hyperoptimized kernels.
13//!
14//! Constants, `ptx_modules()`, and `all_ptx_sets()` are auto-generated
15//! by `build.rs` from the `kernels/{hw}/{model}/{quant}/` directories.
16//! When `ATLAS_TARGET_MODEL=*` or `ATLAS_TARGET_QUANT=*`, multiple
17//! targets are compiled and available at runtime.
18
19use atlas_core::target::KernelTarget;
20
21pub mod resolve;
22pub use resolve::{ResolveCandidate, TargetResolveError, ptx_for_config, ptx_for_exact_target};
23
24// Build-time/run-time shared `[behavior]` defaults — also `include!`d by
25// `build_parse_behavior.rs` so the build script's parse defaults cannot
26// drift from `ModelBehavior::default()` (the #328 failure mode).
27mod behavior_defaults;
28pub use behavior_defaults::{
29 DEFAULT_EFFORT_CAPPED_AT_CEILING, DEFAULT_MAX_INTER_TOOL_PROSE, DEFAULT_MAX_THINKING_BUDGET,
30};
31
32// Auto-generated: per-target PTX constants, ptx_modules() function,
33// and all_ptx_sets() for multi-target builds.
34// NOTE: cargo does NOT track this build-script-generated include! as a
35// recompile trigger, so when build.rs regenerates target_ptx.rs (e.g. the
36// module set changes) this lib can keep a STALE embedded set. Any edit to
37// this file (or `cargo clean -p atlas-kernels`) forces a fresh recompile
38// against the current OUT_DIR/target_ptx.rs.
39include!(concat!(env!("OUT_DIR"), "/target_ptx.rs"));
40
41/// Content fingerprint of the generated kernel set, emitted by `build.rs` as a
42/// `rustc-env`. Referencing it here makes cargo recompile this crate whenever
43/// the kernel set changes — closing the `include!`-not-tracked staleness hole
44/// that silently embedded a stale module list (the 98-vs-99 regression).
45pub const KERNEL_SET_HASH: &str = env!("ATLAS_KERNEL_SET_HASH");
46
47/// What each kernel target in THIS binary was compiled from, as JSON.
48///
49/// A map of `"hardware/model/quant"` to `{hash, arch, compiler, flags}`, baked
50/// by `build.rs` at the moment the kernels were compiled. The benchmark gate
51/// copies it into a record so the record attests to the binary's sources rather
52/// than to whatever the working tree held when the record was written — a
53/// commit sha does not describe a stale `target/`, a dirty tree, or an image
54/// carried between boxes.
55///
56/// `{}` when the build compiled nothing (`ATLAS_SKIP_BUILD=1`) or could not
57/// identify its compiler. That is not an error: an empty attestation excuses no
58/// future diff, so such a binary's records behave exactly as they did before
59/// attestations existed.
60///
61/// `option_env!` rather than `env!` so the crate still builds against an
62/// `OUT_DIR` produced by an older build script.
63pub const TARGET_CLOSURES: &str = match option_env!("ATLAS_TARGET_CLOSURES") {
64 Some(json) => json,
65 None => "{}",
66};
67
68// ═══════════════════════════════════════════════════════════════════
69// Target-aware PTX grouping
70// ═══════════════════════════════════════════════════════════════════
71
72/// Per-category sampling defaults from MODEL.toml.
73#[derive(Debug, Clone, Copy)]
74pub struct SamplingCategory {
75 pub temperature: f32,
76 pub top_p: f32,
77 pub top_k: u32,
78 pub presence_penalty: f32,
79 pub frequency_penalty: f32,
80 /// Multiplicative penalty on already-seen tokens (1.0 = disabled).
81 /// Populated from MODEL.toml `[sampling.*].repetition_penalty` via build.rs.
82 pub repetition_penalty: f32,
83 /// DRY (Don't-Repeat-Yourself) sampler parameters. Penalises tokens
84 /// that extend repeated n-grams past `dry_allowed_length` with an
85 /// exponential `dry_multiplier * dry_base^(match_len - allowed)` —
86 /// the targeted fix for phrase-level attractors (e.g. the
87 /// ```` ```bash cd … cargo test ``` ```` fence-narration loop
88 /// observed in Qwen3.5-35B-A3B-FP8 opencode sessions at turn ≥ 8).
89 ///
90 /// `presence_penalty` on its own is a FLAT per-unique-token hit
91 /// (does not scale with repetition count), so it can't break a
92 /// phrase attractor where individual tokens already paid their
93 /// penalty once. DRY scales with the repeat-length and is the
94 /// published remedy (oobabooga/text-generation-webui#5677, used in
95 /// llama.cpp / Aphrodite / TabbyAPI).
96 ///
97 /// `dry_multiplier = 0.0` disables DRY for this category (default
98 /// for every preset unless MODEL.toml sets it explicitly).
99 pub dry_multiplier: f32,
100 pub dry_base: f32,
101 pub dry_allowed_length: u32,
102 /// LZ penalty (arXiv:2504.20131). Per-extension n-gram penalty
103 /// over a 256-token rolling window. Frequency-weighted and length-
104 /// scaled, so it correctly distinguishes "phrase loop" from
105 /// "legitimate vocabulary reuse" without the flat-per-token
106 /// `presence_penalty` regression. 0.0 = disabled. SGLang reference
107 /// strength = 0.2 (lossless on AIME/GPQA).
108 pub lz_penalty: f32,
109 /// Model-declared min-p, or `None` when MODEL.toml is silent.
110 ///
111 /// `Option`, not `f32`, because absence and `0.0` mean opposite things
112 /// here. The server ships `--default-min-p 0.08` and every request that
113 /// does not name min_p takes it, so a model whose card specifies
114 /// `min_p = 0` had no way to say so: `[behavior].min_p_floor` only ever
115 /// RAISES min_p (`min_p.max(floor)`), and the preset did not carry the
116 /// value at all. A plain `f32` defaulting to 0.0 would silently strip the
117 /// 0.08 floor from every model that has a `[sampling.*]` table, which is
118 /// the opposite regression.
119 ///
120 /// `Some(x)` outranks the CLI default and is outranked by
121 /// `generation_config.json` — the same precedence temperature/top_k/top_p
122 /// already follow. `None` preserves the CLI-owned behaviour exactly.
123 pub min_p: Option<f32>,
124 /// Model-declared top-n-sigma, or `None` when MODEL.toml is silent.
125 ///
126 /// Same absence-vs-zero problem as `min_p`: the server ships
127 /// `--default-top-n-sigma 1.0`, so a model whose card asks for NO sigma
128 /// filter had no way to say so. `Some(0.0)` disables it; `None` leaves the
129 /// CLI default owning the field.
130 pub top_n_sigma: Option<f32>,
131}
132
133/// Model-specific sampling presets loaded from MODEL.toml `[sampling.*]`.
134#[derive(Debug, Clone, Copy)]
135pub struct SamplingPresets {
136 pub thinking_text: SamplingCategory,
137 pub thinking_coding: SamplingCategory,
138 pub non_thinking: SamplingCategory,
139 /// Tool-calling preset: model-recommended sampling for agentic tasks.
140 /// Qwen3.5 recommends temperature=0.6 (NOT greedy) to avoid repetition loops.
141 pub tools: SamplingCategory,
142}
143
144impl Default for SamplingPresets {
145 fn default() -> Self {
146 let default_cat = SamplingCategory {
147 temperature: 0.7,
148 top_p: 0.95,
149 top_k: 20,
150 presence_penalty: 0.0,
151 frequency_penalty: 0.0,
152 repetition_penalty: 1.0,
153 // DRY defaults = disabled (multiplier 0.0). Per-MODEL.toml
154 // tools presets opt in when the model needs it.
155 dry_multiplier: 0.0,
156 dry_base: 1.75,
157 dry_allowed_length: 2,
158 lz_penalty: 0.0,
159 min_p: None,
160 top_n_sigma: None,
161 };
162 let tools_cat = SamplingCategory {
163 temperature: 0.6,
164 top_p: 0.95,
165 top_k: 20,
166 presence_penalty: 0.0,
167 frequency_penalty: 0.0,
168 repetition_penalty: 1.0,
169 dry_multiplier: 0.0,
170 dry_base: 1.75,
171 dry_allowed_length: 2,
172 lz_penalty: 0.0,
173 min_p: None,
174 top_n_sigma: None,
175 };
176 Self {
177 thinking_text: default_cat,
178 thinking_coding: default_cat,
179 non_thinking: default_cat,
180 tools: tools_cat,
181 }
182 }
183}
184
185/// Model-specific behavior flags from MODEL.toml `[behavior]`.
186#[derive(Debug, Clone)]
187pub struct ModelBehavior {
188 /// Allow thinking when tools are active. Default: true.
189 pub thinking_in_tools: bool,
190 /// Maximum thinking budget (tokens). Default:
191 /// [`DEFAULT_MAX_THINKING_BUDGET`].
192 pub max_thinking_budget: u32,
193 /// Clamp qualitative `reasoning_effort` levels at the model's effective
194 /// ceiling (high/xhigh resolve to `max_thinking_budget` instead of
195 /// 2x/4x it). Default [`DEFAULT_EFFORT_CAPPED_AT_CEILING`] = `false`
196 /// (historical ladder shape). See `behavior_defaults.rs` for when a
197 /// model should set `true` (measured budget non-monotonicity).
198 pub effort_capped_at_ceiling: bool,
199 /// Default thinking state for this model when the client request does not
200 /// specify a reasoning_effort / thinking parameter. Typical values:
201 /// - thinking-first models (Mistral Small 4, Qwen3.5, …): `true`
202 /// - instruct-only models with no `<think>` tokens: `false`
203 ///
204 /// Overridden per-request by `reasoning_effort`, and globally by the
205 /// `--disable-thinking` CLI flag.
206 pub thinking_default: bool,
207 /// Default FP8 KV calibration tokens (0 = disabled).
208 pub fp8_kv_calibration_tokens: usize,
209 /// Default KV cache dtype from MODEL.toml (e.g., "bf16", "fp8").
210 /// When non-empty, overrides the CLI default for models that need
211 /// higher precision. User can still override with explicit --kv-cache-dtype.
212 pub default_kv_dtype: &'static str,
213 /// Default num_drafts for speculative decoding (0 = use CLI default).
214 /// K = num_drafts + 1 (num_drafts=1 → K=2 verifies 2 tokens per step).
215 /// Optimal K varies per model; benchmarks sometimes show K=2 beats K=3.
216 /// User override with --num-drafts still wins.
217 pub default_num_drafts: u32,
218 /// Skip the `<tool_call>\n` steering prefix in the chat template's
219 /// generation prompt. Some Nemotron variants (Super 120B) weren't
220 /// trained on qwen3_coder XML and emit a `<tool_call>` token loop
221 /// when the prefix forces them into that structure. Default: false
222 /// (keep the existing Nemotron-Nano-correct behavior).
223 pub disable_tool_steering: bool,
224 /// Do not append Atlas's derived `<environment>working_directory` block to
225 /// a client system prompt. Native agent clients may already provide the
226 /// cwd; duplicating it can become a tool-selection attractor.
227 pub disable_cwd_hint_injection: bool,
228 /// Use the selected MODEL.toml sampling category for default temperature,
229 /// top-k, and top-p instead of generation_config.json. Explicit request
230 /// values still take precedence.
231 pub use_sampling_presets_for_core: bool,
232 /// Per-model tool-call parser override. Empty string = use the
233 /// `tool_defaults.toml` mapping for this `model_type`. Set in MODEL.toml
234 /// `[behavior].tool_call_parser` when one variant of a model_type needs
235 /// a different parser than its siblings (e.g. Nemotron-Super-120B uses
236 /// `bare_json` while Nemotron-Nano-30B stays on `qwen3_coder`).
237 pub tool_call_parser: &'static str,
238 /// Enable the content-loop watchdog (period-N token-repetition detector
239 /// at `decode_logits_step.rs:230`). Default: `false` — most models
240 /// terminate cleanly via EOS / `max_tokens` without it. Models with a
241 /// known prose-attractor failure mode (Qwen3.5-35B-A3B's "Running:```bash
242 /// cmd```Executing:" loop, observed during agentic Claude Code sessions)
243 /// should set this `true` in MODEL.toml `[behavior]`.
244 ///
245 /// The watchdog has false-positives on legitimate structured output
246 /// (chess board JS init `{color:BLACK,type:'P'},` × 8, HTML tables,
247 /// JSON arrays of similar objects, multiplication tables). Enable only
248 /// when the model has been observed to need it.
249 pub enable_loop_watchdog: bool,
250 /// See build_parse.rs: gate for the THINKING-phase loop watchdog.
251 pub enable_think_loop_watchdog: bool,
252 /// See build_parse_behavior.rs: honor a mid-`<think>` EOS by implicitly
253 /// closing the block. Defaults FALSE (pre-p350 behaviour).
254 pub honor_eos_inside_thinking: bool,
255 /// A4 floor: suppress `</think>` until this many think tokens
256 /// (16 = historical constant; 0 disables — card-native brief thinking).
257 pub min_reasoning_floor_tokens: u32,
258 /// Cap the thinking budget at 90% of the request's `max_tokens` (true), or
259 /// let `max_thinking_budget` be the sole cap (false = vLLM single-budget:
260 /// reasoning may use the full generation budget). See thinking.rs::resolve.
261 pub cap_thinking_at_max_tokens: bool,
262 /// Server-side min-p FLOOR (0.0 = disabled). Applied as `min_p.max(floor)`
263 /// AFTER request/preset resolution, so it binds even when a client sends
264 /// `min_p = 0` (or omits it on a server without `--default-min-p`). On
265 /// drift-prone quantized models (FP8 / NVFP4 lm-head) an unfloored tail
266 /// lets the degenerate low-probability tail be sampled into repetition
267 /// loops + argmax-flip garbling on long generation — the Claude-Code
268 /// failure mode. MEASURED 2026-06-07 (nvfp4-head@64k): 0.05 turned 4 loop-
269 /// watchdog fires → 0. Set in MODEL.toml `[behavior]`.
270 pub min_p_floor: f32,
271 /// Server-side temperature CEILING (0.0 = disabled). `temperature.min(max)`
272 /// AFTER resolution — defense-in-depth net against a client sending a high
273 /// temperature; min_p_floor is the dominant lever. Set in MODEL.toml.
274 pub temperature_max: f32,
275 /// Thinking-loop watchdog: substring-occurrence count that trips a
276 /// forced `</think>`. Default 3 (historical `THINK_LOOP_MIN_REPEATS`).
277 pub think_loop_min_repeats: u32,
278 /// Thinking-loop watchdog: trailing-token scan window. Default 160.
279 pub think_loop_scan_window: u32,
280 /// F2 confidence-run early-stop enabled. Default `true`. Set false
281 /// for models whose deterministic code drafting trips the heuristic.
282 pub confidence_early_stop: bool,
283 /// F2 confidence run length before arming forced `</think>`.
284 /// Default 30.
285 pub confidence_run_length: u32,
286 /// Fuzzy-repetition detector Hamming tolerance divisor: a
287 /// `pattern_len`-token window tolerates `pattern_len / div`
288 /// mismatches. Default 12 (~8%).
289 pub fuzzy_repeat_tolerance_div: u32,
290 /// Cap on free-text tokens between successive `<tool_call>` opens in
291 /// `tool_choice=auto`. Default [`DEFAULT_MAX_INTER_TOOL_PROSE`]
292 /// (see `behavior_defaults.rs` for the tuning history — #328).
293 pub max_inter_tool_prose: u32,
294 /// Unconditional per-generation cap on post-`</think>` content tokens
295 /// for tool-active requests (grammar attached). Bounds a runaway where
296 /// a grammar-legal-but-never-closing tool value burns to `max_tokens`
297 /// (the dominant opencode `webserver_ok` 360s-timeout cause). Default
298 /// 100_000 — effectively unbounded, the historical no-op — so a model
299 /// that sets nothing is byte-identical to before. Set a small value
300 /// (e.g. 1536) per-model to backstop the runaway. Never caps plain
301 /// chat: the runtime gate also requires `grammar_state.is_some()`.
302 pub max_post_think_content_tokens: u32,
303 /// TSCG (Tool-Schema Compilation) enabled — compile tool JSON
304 /// schemas to compact function signatures before prompting.
305 /// Default `false`; the TAS operator is tokenizer-specific so
306 /// enable + verify per model. arXiv:2605.04107.
307 pub tscg: bool,
308 /// Disable XGrammar tool-call constrained decoding for this model.
309 /// Default `false`. Escape hatch for the "structure snowballing"
310 /// alignment tax (arXiv:2604.06066) — a few models tool-call more
311 /// reliably unconstrained. When `true`, tool calls are parsed but
312 /// not grammar-enforced.
313 pub disable_tool_grammar: bool,
314 /// Phase-C: when a decode-time watchdog (content-loop, fuzzy-repeat,
315 /// inter-tool prose) detects degeneration, roll the sequence back to
316 /// the last well-formed boundary and let generation re-steer, instead
317 /// of hard-stopping the response. Default `true` (recovers responses,
318 /// especially mid-tool-call — arXiv:2603.27905 ATLAS-RTC). Set `false`
319 /// to keep the legacy hard-stop behavior. Capped at
320 /// [`crate::ROLLBACK_RESTEER_CAP`] rollbacks per sequence, after which
321 /// the hard-stop fires regardless.
322 pub rollback_resteer: bool,
323 /// Phase-C ROM (arXiv:2603.22016) scaffold. Path to a trained
324 /// repetition-onset detection head artifact. Empty string = no ROM
325 /// head; the F2 confidence heuristic stays as the fallback. A trained
326 /// artifact can be dropped in later via MODEL.toml
327 /// `[behavior].rom_head` without further code changes — the runtime
328 /// loads it through the `RomHead` trait seam. The detector
329 /// itself is intentionally NOT implemented (no per-model trained head
330 /// is available); only the optional hook is wired.
331 pub rom_head: &'static str,
332 /// Tier 5c (2026-05-26): one-shot tool-call re-roll on hard
333 /// validation failure. When `true`, `validate_tool_calls` errors on
334 /// the chat path fire a single retry inference with the same
335 /// grammar spec + a correction nudge appended to the prompt. If the
336 /// retry produces valid tool calls, they replace the failed call
337 /// before the response leaves the server. Default `true` — the
338 /// blocking-path canonical-probe trace shows a write-→bash recovery
339 /// path that's strictly better than the previous "`[atlas]` Tool call
340 /// rejected" content fallback. Set `false` per-model when a
341 /// specific model is known to ALWAYS get tool args right on the
342 /// first attempt (extra inference round-trip cost is wasted there).
343 pub tool_retry: bool,
344 /// Jinja `preserve_thinking` chat-template flag (Qwen3.6+ dense family):
345 /// keep historical `<think>` blocks in re-rendered assistant turns
346 /// instead of stripping them before the last user query.
347 ///
348 /// Tri-state on purpose (SSOT): `None` = do NOT inject the variable —
349 /// the model's own template default applies (Qwen3.6 strips unless
350 /// `preserve_thinking` is true; Qwen3.8 KEEPS unless it is explicitly
351 /// false). `Some(_)` pins the value for this target, changing
352 /// multi-turn prompt bytes and therefore prefix-cache hit rate.
353 /// Per-request `chat_template_kwargs.preserve_thinking` still wins.
354 pub preserve_thinking: Option<bool>,
355}
356
357/// Phase-C: maximum number of watchdog-triggered rollbacks a single
358/// sequence may perform before the watchdog reverts to a hard stop.
359/// Bounds the worst case where re-steering re-enters the same attractor
360/// — without this a degenerate sequence could rollback indefinitely.
361pub const ROLLBACK_RESTEER_CAP: u32 = 2;
362
363/// Phase-C: number of boundary SSM-state snapshots retained per sequence in
364/// the decode-rollback ring (hybrid GDN/Mamba models). DECOUPLED from
365/// [`ROLLBACK_RESTEER_CAP`]: the cap bounds how many times we re-steer, but
366/// the ring must retain enough *boundary* snapshots that a clean PRE-loop
367/// boundary survives long enough to roll back to. Sizing it at the old
368/// `CAP + 1 = 3` meant a loop spanning ≥3 sentence/newline boundaries evicted
369/// the clean boundary before the fuzzy detector (3 repeats) fired, forcing a
370/// `NoSsmSnapshot` decline → hard-stop (observed: Claude-Code @ nvfp4-head,
371/// 2026-06-07). 8 covers the 3-repeat detector with margin at modest cost
372/// (8 × max_batch × per-layer GDN state, allocated once). Pure-attention
373/// models ignore this (their ring is 0; they roll back to any boundary).
374pub const DECODE_ROLLBACK_RING_SLOTS: usize = 8;
375
376/// Domain salt folded into every decode cold-tier key so a decode-ring blob can
377/// never collide with a Marconi prefix-hash key on a shared store/peer.
378pub const DECODE_DOMAIN: u64 = 0xD3C0_DE12_A5B6_C7D8;
379
380impl Default for ModelBehavior {
381 fn default() -> Self {
382 Self {
383 thinking_in_tools: true,
384 max_thinking_budget: DEFAULT_MAX_THINKING_BUDGET,
385 effort_capped_at_ceiling: DEFAULT_EFFORT_CAPPED_AT_CEILING,
386 thinking_default: false,
387 fp8_kv_calibration_tokens: 0,
388 default_kv_dtype: "",
389 default_num_drafts: 0,
390 disable_tool_steering: false,
391 disable_cwd_hint_injection: false,
392 use_sampling_presets_for_core: false,
393 tool_call_parser: "",
394 enable_loop_watchdog: false,
395 enable_think_loop_watchdog: true,
396 honor_eos_inside_thinking: false,
397 min_reasoning_floor_tokens: 16,
398 cap_thinking_at_max_tokens: true,
399 min_p_floor: 0.0,
400 temperature_max: 0.0,
401 think_loop_min_repeats: 3,
402 think_loop_scan_window: 160,
403 confidence_early_stop: true,
404 confidence_run_length: 30,
405 fuzzy_repeat_tolerance_div: 12,
406 max_inter_tool_prose: DEFAULT_MAX_INTER_TOOL_PROSE,
407 max_post_think_content_tokens: 100_000,
408 tscg: false,
409 disable_tool_grammar: false,
410 rollback_resteer: true,
411 rom_head: "",
412 tool_retry: true,
413 preserve_thinking: None,
414 }
415 }
416}
417
418/// Declares which `(model_type, hidden_size)` pairs a kernel target supports.
419/// Parsed from `[[model_types]]` in MODEL.toml at build time.
420pub struct ModelTypeMatch {
421 pub model_type: &'static str,
422 /// `None` = wildcard (matches any hidden_size not caught by a more specific entry).
423 pub hidden_size: Option<usize>,
424}
425
426/// DFlash speculative-decoding pairing for a target model.
427/// Parsed from `[dflash]` in MODEL.toml at build time. `None` when the
428/// model has no DFlash drafter associated.
429#[derive(Debug, Clone)]
430pub struct DflashConfig {
431 /// HuggingFace id (or local path) of the drafter checkpoint.
432 pub draft_model: &'static str,
433 /// Block size γ (parallel draft tokens per step). Defaults to 16.
434 pub gamma: usize,
435 /// Drafter sliding-window size in tokens. 0 = full attention.
436 pub window_size: usize,
437 /// Token id used to fill the γ "to-be-predicted" positions during
438 /// drafter forward. From the drafter's `dflash_config.mask_token_id`.
439 pub mask_token_id: u32,
440 /// Target-side layer indices to capture intermediate hidden states from
441 /// (shallow-to-deep). The drafter's `fc` projection consumes the stack
442 /// of these hiddens. From the drafter's `dflash_config.target_layer_ids`.
443 pub target_layer_ids: &'static [usize],
444}
445
446/// Kernel modules hyperoptimized for a specific (H, M_q) target.
447///
448/// Each blob is the compiled kernel for one module, emitted uniformly as
449/// `&'static [u8]` by build.rs (`include_bytes!`). NVIDIA PTX is ASCII
450/// text but valid as bytes; SCALE/AMD and Metal produce binary objects.
451/// The runtime registry sniffs text-vs-binary per blob at load time.
452pub struct TargetPtxSet {
453 pub target: KernelTarget,
454 pub modules: Vec<(&'static str, &'static [u8])>,
455 pub sampling: SamplingPresets,
456 pub behavior: ModelBehavior,
457 pub model_type_matches: Vec<ModelTypeMatch>,
458 /// `[model] match_names` needles from MODEL.toml — case-insensitive
459 /// substrings of the checkpoint reference (HF id / `--model-name` /
460 /// resolved model dir) that identify checkpoints THIS target serves.
461 /// Consulted only to break a tie when several targets declare the same
462 /// `(model_type, hidden_size)` (e.g. qwen3.6-27b vs qwen3.8-27b, whose
463 /// configs are bit-identical); see [`resolve::resolve_target`]. Empty
464 /// for targets that never collide — `build.rs` panics if a colliding
465 /// target omits them.
466 pub match_names: &'static [&'static str],
467 /// DFlash drafter pairing for this model. `None` when the MODEL.toml has
468 /// no `[dflash]` section. Consumed by spark-server when `--dflash` is
469 /// set without an explicit `--draft-model` flag.
470 pub dflash: Option<DflashConfig>,
471 /// `(module, kernel)` pairs this model's kernel files DROPPED by shadowing
472 /// their `common/` namesakes — the kernel exists in `common/` but this
473 /// model's fork of the file does not define it, so it is not compiled here.
474 ///
475 /// Shadowing is whole-file, so a fork that predates a kernel added to
476 /// `common/` silently loses it: `try_kernel` returns handle 0 and whatever
477 /// depends on it fails CLOSED. The startup audit joins this against the
478 /// kernels the model actually looked up, which separates the two classes of
479 /// missing kernel — dropped-by-fork (a build defect) from
480 /// never-built-for-this-architecture (expected, e.g. MLA on a Qwen model).
481 pub shadowed_dropped: &'static [(&'static str, &'static str)],
482 /// `(module, kernel)` lookups this model's dispatch may issue and fail to
483 /// resolve WITHOUT that being an error, declared in the model's MODEL.toml
484 /// `[expected_absent]` with a mandatory stated reason per entry.
485 ///
486 /// The boot audit (`kernel_audit::classify_failures`) fails CLOSED on every
487 /// unresolved lookup that is not in this list, so the list is the entire
488 /// difference between "this model is known to run this way" and "nobody has
489 /// looked". It is TRANSITIONAL: the right fix for a lookup that can never
490 /// resolve is to gate it on config so it is never issued (see
491 /// `qwen3_attention::init_arch_gates`), which removes it from here.
492 pub expected_absent: &'static [(&'static str, &'static str)],
493}
494
495mod query;
496pub use query::{available_targets, ptx_for_model};
497
498#[cfg(test)]
499#[path = "lib_tests.rs"]
500mod tests;