1use crate::{
7 is_sha256_digest, parse_bool_env_value, parse_usize_env_value, AttentionExecutionPolicy,
8 CompiledNativeOperatorIdentity, ExecutionResourceAuthority, RuntimeConfigEffect,
9 RuntimeConfigEntry, RuntimeConfigSnapshot, RuntimeConfigSource,
10 CUDA_NATIVE_ADAPTIVE_V1_MAX_SEQUENCE_TOKENS,
11};
12use serde::{Deserialize, Serialize};
13use std::collections::BTreeMap;
14use thiserror::Error;
15
16pub const M3_QWEN3_30B_A3B_INT4_PRESET: &str = "m3_qwen3_30b_a3b_int4";
17pub const QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET: &str =
18 "qwen25_72b_gptq_int4_2x4090_layer_split";
19const DEFAULT_KV_BLOCK_SIZE_TOKENS: usize = 16;
20const DEFAULT_KV_BLOCKS: usize = 2048;
21const GIB: u64 = 1024 * 1024 * 1024;
22const FA2_NATIVE_MANIFEST_KEY: &str = "FERRUM_FA2_NATIVE_MANIFEST";
23const FA2_NATIVE_ARTIFACT_KEY: &str = "FERRUM_FA2_NATIVE_ARTIFACT";
24const FA2_NATIVE_SOURCE_SHA256_KEY: &str = "FERRUM_FA2_NATIVE_SOURCE_SHA256";
25const FA2_NATIVE_INPUTS_SHA256_KEY: &str = "FERRUM_FA2_NATIVE_INPUTS_SHA256";
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct ModelCapabilities {
29 pub architecture: String,
30 pub quantization: Option<String>,
31 pub moe: Option<MoeCapabilities>,
32 pub max_context_len: Option<usize>,
33 pub num_hidden_layers: Option<usize>,
34 pub head_dim: Option<usize>,
35 pub kv_heads: Option<usize>,
36 pub estimated_weight_bytes: Option<u64>,
37 pub recurrent_state_bytes_per_sequence: Option<u64>,
38 pub supported_dtypes: Vec<String>,
39 pub graph_safe_moe: bool,
40}
41
42impl ModelCapabilities {
43 pub fn unknown() -> Self {
44 Self {
45 architecture: "unknown".to_string(),
46 quantization: None,
47 moe: None,
48 max_context_len: None,
49 num_hidden_layers: None,
50 head_dim: None,
51 kv_heads: None,
52 estimated_weight_bytes: None,
53 recurrent_state_bytes_per_sequence: None,
54 supported_dtypes: vec!["fp16".to_string(), "fp32".to_string()],
55 graph_safe_moe: false,
56 }
57 }
58
59 pub fn qwen3_30b_a3b_gptq_int4() -> Self {
60 Self {
61 architecture: "qwen3_moe".to_string(),
62 quantization: Some("gptq_int4".to_string()),
63 moe: Some(MoeCapabilities {
64 num_experts: 128,
65 experts_per_token: 8,
66 moe_intermediate_size: Some(768),
67 }),
68 max_context_len: Some(40960),
69 num_hidden_layers: Some(48),
70 head_dim: Some(128),
71 kv_heads: Some(4),
72 estimated_weight_bytes: Some(18 * GIB),
77 recurrent_state_bytes_per_sequence: None,
78 supported_dtypes: vec!["fp16".to_string()],
79 graph_safe_moe: false,
80 }
81 }
82
83 pub fn qwen25_72b_gptq_int4() -> Self {
84 Self {
85 architecture: "qwen2".to_string(),
86 quantization: Some("gptq_int4".to_string()),
87 moe: None,
88 max_context_len: Some(32_768),
89 num_hidden_layers: Some(80),
90 head_dim: Some(128),
91 kv_heads: Some(8),
92 estimated_weight_bytes: Some(39 * GIB),
93 recurrent_state_bytes_per_sequence: None,
94 supported_dtypes: vec!["fp16".to_string()],
95 graph_safe_moe: false,
96 }
97 }
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
101pub struct MoeCapabilities {
102 pub num_experts: usize,
103 pub experts_per_token: usize,
104 pub moe_intermediate_size: Option<usize>,
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub struct HardwareCapabilities {
109 pub backend: String,
110 pub cuda_runtime: Option<String>,
111 pub compute_capability: Option<String>,
112 pub vram_bytes: Option<u64>,
113 pub sm_count: Option<u32>,
114 pub supported_dtypes: Vec<String>,
115 pub supported_kv_dtypes: Vec<String>,
116 pub graph_support: bool,
117 pub compiled_features: CompiledKernelFeatures,
118}
119
120impl HardwareCapabilities {
121 pub fn unknown() -> Self {
122 Self {
123 backend: "unknown".to_string(),
124 cuda_runtime: None,
125 compute_capability: None,
126 vram_bytes: None,
127 sm_count: None,
128 supported_dtypes: vec!["fp16".to_string(), "fp32".to_string()],
129 supported_kv_dtypes: vec!["fp16".to_string()],
130 graph_support: false,
131 compiled_features: CompiledKernelFeatures::default(),
132 }
133 }
134
135 pub fn rtx4090_cuda(features: CompiledKernelFeatures) -> Self {
136 Self {
137 backend: "cuda".to_string(),
138 cuda_runtime: None,
139 compute_capability: Some("8.9".to_string()),
140 vram_bytes: Some(24 * 1024 * 1024 * 1024),
141 sm_count: Some(128),
142 supported_dtypes: vec!["fp16".to_string(), "fp32".to_string()],
143 supported_kv_dtypes: vec!["fp16".to_string(), "int8".to_string()],
144 graph_support: true,
145 compiled_features: features,
146 }
147 }
148}
149
150#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
151pub struct CompiledNativeOperatorArtifact {
152 pub manifest_path: String,
153 pub artifact_path: String,
154 pub source_package_sha256: String,
155 pub inputs_sha256: String,
156 pub binary_sha256: String,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
160pub struct CompiledKernelFeatures {
161 pub cuda: bool,
162 pub vllm_paged_attn: bool,
163 pub vllm_moe_marlin: bool,
164 pub cuda_graph: bool,
165 pub greedy_argmax: bool,
166 pub fa2_source: bool,
167 pub fa2_direct_ffi: bool,
168 pub fa2_native_operator_artifact: bool,
169 #[serde(default, skip_serializing_if = "Option::is_none")]
170 pub fa2_native_operator_artifact_metadata: Option<CompiledNativeOperatorArtifact>,
171 #[serde(default, skip_serializing_if = "Vec::is_empty")]
172 pub native_operator_artifacts: Vec<CompiledNativeOperatorIdentity>,
173}
174
175impl Default for CompiledKernelFeatures {
176 fn default() -> Self {
177 Self {
178 cuda: false,
179 vllm_paged_attn: false,
180 vllm_moe_marlin: false,
181 cuda_graph: false,
182 greedy_argmax: false,
183 fa2_source: false,
184 fa2_direct_ffi: false,
185 fa2_native_operator_artifact: false,
186 fa2_native_operator_artifact_metadata: None,
187 native_operator_artifacts: Vec::new(),
188 }
189 }
190}
191
192impl CompiledKernelFeatures {
193 pub fn m3_fast_path_without_fa2() -> Self {
194 Self {
195 cuda: true,
196 vllm_paged_attn: true,
197 vllm_moe_marlin: true,
198 cuda_graph: true,
199 greedy_argmax: true,
200 fa2_source: false,
201 fa2_direct_ffi: false,
202 fa2_native_operator_artifact: false,
203 fa2_native_operator_artifact_metadata: None,
204 native_operator_artifacts: Vec::new(),
205 }
206 }
207
208 pub fn m3_fast_path_with_source_fa2() -> Self {
209 Self {
210 fa2_source: true,
211 ..Self::m3_fast_path_without_fa2()
212 }
213 }
214
215 pub fn m3_fast_path_with_native_fa2_artifact() -> Self {
216 Self {
217 fa2_native_operator_artifact: true,
218 fa2_native_operator_artifact_metadata: Some(CompiledNativeOperatorArtifact {
219 manifest_path: "/tmp/native/fa2/native_operator_manifest.json".to_string(),
220 artifact_path: "/tmp/native/fa2/libferrum_native_fa2.a".to_string(),
221 source_package_sha256:
222 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_string(),
223 inputs_sha256: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
224 .to_string(),
225 binary_sha256: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
226 .to_string(),
227 }),
228 ..Self::m3_fast_path_without_fa2()
229 }
230 }
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
234pub struct WorkloadProfile {
235 pub preset: Option<String>,
236 pub serving_mode: String,
237 pub target_concurrency: usize,
238 pub prompt_length_class: String,
239 pub output_length_class: String,
240 pub priority: WorkloadPriority,
241}
242
243impl WorkloadProfile {
244 pub fn serving_default() -> Self {
245 Self {
246 preset: None,
247 serving_mode: "openai_chat".to_string(),
248 target_concurrency: 1,
249 prompt_length_class: "unknown".to_string(),
250 output_length_class: "unknown".to_string(),
251 priority: WorkloadPriority::Balanced,
252 }
253 }
254
255 pub fn serving_default_for_hardware(hardware: &HardwareCapabilities) -> Self {
256 let mut profile = Self::serving_default();
257 if hardware.backend.eq_ignore_ascii_case("cuda")
258 || hardware.backend.eq_ignore_ascii_case("metal")
259 {
260 profile.target_concurrency = hardware
261 .vram_bytes
262 .map(vram_default_max_sequences)
263 .unwrap_or(4)
264 .max(1);
265 }
266 profile
267 }
268
269 pub fn m3_qwen3_30b_a3b_int4() -> Self {
270 Self {
271 preset: Some(M3_QWEN3_30B_A3B_INT4_PRESET.to_string()),
272 serving_mode: "bench_serve".to_string(),
273 target_concurrency: 32,
274 prompt_length_class: "random_256".to_string(),
275 output_length_class: "random_128".to_string(),
276 priority: WorkloadPriority::Throughput,
277 }
278 }
279
280 pub fn qwen25_72b_gptq_int4_2x4090_layer_split() -> Self {
281 Self {
282 preset: Some(QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET.to_string()),
283 serving_mode: "bench_serve".to_string(),
284 target_concurrency: 16,
285 prompt_length_class: "random_256".to_string(),
286 output_length_class: "random_128".to_string(),
287 priority: WorkloadPriority::Throughput,
288 }
289 }
290
291 fn is_m3_preset(&self) -> bool {
292 self.is_preset(M3_QWEN3_30B_A3B_INT4_PRESET)
293 }
294
295 fn is_preset(&self, preset: &str) -> bool {
296 self.preset.as_deref() == Some(preset)
297 }
298}
299
300impl Default for WorkloadProfile {
301 fn default() -> Self {
302 Self::serving_default()
303 }
304}
305
306#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
307#[serde(rename_all = "snake_case")]
308pub enum WorkloadPriority {
309 Latency,
310 Throughput,
311 Balanced,
312}
313
314#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
315pub struct ResolvedFerrumConfig {
316 pub schema_version: u32,
317 pub preset: Option<String>,
318 #[serde(default)]
319 pub execution_resource_authority: ExecutionResourceAuthority,
320 #[serde(default)]
321 pub requested_attention_policy: Option<AttentionExecutionPolicy>,
322 #[serde(default)]
323 pub compiled_attention_policy: Option<AttentionExecutionPolicy>,
324 pub runtime_config: RuntimeConfigSnapshot,
325 pub model_capabilities: ModelCapabilities,
326 pub hardware_capabilities: HardwareCapabilities,
327 pub workload_profile: WorkloadProfile,
328 pub decisions: Vec<AutoConfigDecision>,
329}
330
331impl ResolvedFerrumConfig {
332 pub fn effective_config_document(&self) -> serde_json::Value {
333 let backend = self.hardware_capabilities.backend.clone();
334 let requested_gpu_devices = self
335 .runtime_csv_usize("FERRUM_REQUESTED_GPU_DEVICES")
336 .or_else(|| default_gpu_devices_for_backend(&backend));
337 let selected_gpu_devices = self
338 .runtime_csv_usize("FERRUM_SELECTED_GPU_DEVICES")
339 .or_else(|| requested_gpu_devices.clone())
340 .or_else(|| default_gpu_devices_for_backend(&backend));
341 let cuda_device_count = self
342 .runtime_usize("FERRUM_CUDA_DEVICE_COUNT")
343 .or_else(|| {
344 backend.eq_ignore_ascii_case("cuda").then(|| {
345 selected_gpu_devices
346 .as_ref()
347 .map(|devices| devices.len())
348 .unwrap_or(1)
349 })
350 })
351 .unwrap_or(0);
352 let selected_distributed_strategy = self
353 .runtime_entry_value("FERRUM_SELECTED_DISTRIBUTED_STRATEGY")
354 .unwrap_or_else(|| {
355 if selected_gpu_devices
356 .as_ref()
357 .map(|devices| devices.len() > 1)
358 .unwrap_or(false)
359 {
360 "layer_split".to_string()
361 } else if backend.eq_ignore_ascii_case("cuda") {
362 "single_gpu".to_string()
363 } else {
364 "none".to_string()
365 }
366 });
367 let selected_layer_split_plan =
368 self.runtime_entry_value("FERRUM_SELECTED_LAYER_SPLIT_PLAN");
369 let selected_layer_split_stages =
370 self.runtime_json_value("FERRUM_SELECTED_LAYER_SPLIT_STAGES");
371 let selected_layer_split_stage_count = selected_layer_split_stages
372 .as_ref()
373 .and_then(|value| value.as_array().map(|stages| stages.len()))
374 .or_else(|| {
375 selected_layer_split_plan
376 .as_ref()
377 .and_then(|_| selected_gpu_devices.as_ref().map(Vec::len))
378 });
379 let requested_pipeline_mode = self.runtime_entry_value("FERRUM_LAYER_SPLIT_PIPELINE_MODE");
380 let selected_pipeline_mode = if selected_layer_split_plan.is_some() {
381 requested_pipeline_mode.unwrap_or_else(|| {
382 if selected_layer_split_stage_count == Some(2) {
383 "overlapped".to_string()
384 } else {
385 "batch".to_string()
386 }
387 })
388 } else {
389 "sequential".to_string()
390 };
391 let selected_max_sequences = self.selected_usize("max_sequences");
392 let selected_microbatch_size = if selected_layer_split_plan.is_some() {
393 selected_max_sequences.map(|max_sequences| {
394 if selected_pipeline_mode == "overlapped" {
395 max_sequences.div_ceil(2).max(1)
396 } else {
397 max_sequences
398 }
399 })
400 } else {
401 Some(1)
402 };
403 let selected_stage_bridge = selected_layer_split_plan.as_ref().map(|_| "host");
404 let selected_max_model_len = self.selected_usize("max_model_len");
405 let selected_kv_capacity = self.runtime_usize("FERRUM_KV_CAPACITY");
406 let selected_max_batched_tokens = self.selected_usize("max_batched_tokens");
407 let selected_recurrent_state_max_slots = self.selected_recurrent_state_max_slots();
408 let selected_admission_limit = effective_admission_limit(
409 self.execution_resource_authority,
410 selected_max_sequences,
411 selected_recurrent_state_max_slots,
412 );
413 let selected_attention_policy = self.selected_string("attention_execution_policy");
414 let selected_attention_impl = self.selected_string("attention_decode_backend");
415 serde_json::json!({
416 "schema_version": 1,
417 "preset": self.preset,
418 "env_hash": self.runtime_env_hash(),
419 "backend": backend.clone(),
420 "execution_resource_authority": self.execution_resource_authority,
421 "requested_gpu_devices": requested_gpu_devices.clone(),
422 "selected_gpu_devices": selected_gpu_devices.clone(),
423 "cuda_device_count": cuda_device_count,
424 "selected_distributed_strategy": selected_distributed_strategy.clone(),
425 "selected_layer_split_plan": selected_layer_split_plan.clone(),
426 "selected_layer_split_stages": selected_layer_split_stages,
427 "selected_pipeline_mode": selected_pipeline_mode,
428 "selected_microbatch_size": selected_microbatch_size,
429 "selected_stage_bridge": selected_stage_bridge,
430 "selected_weight_placement": if selected_layer_split_plan.is_some() { "layer_split" } else { "single_device" },
431 "selected_kv_layout": if backend.eq_ignore_ascii_case("cpu") { "contiguous" } else { "paged" },
432 "selected_attention_impl": selected_attention_impl,
433 "selected_attention_policy": selected_attention_policy,
434 "attention_execution": self.attention_execution_document(),
435 "selected_graph_mode": self.selected_graph_mode(),
436 "selected_max_sequences": selected_max_sequences,
437 "selected_max_model_len": selected_max_model_len,
438 "selected_kv_capacity": selected_kv_capacity,
439 "selected_max_batched_tokens": selected_max_batched_tokens,
440 "selected_recurrent_state_max_slots": selected_recurrent_state_max_slots,
441 "selected_admission_limit": selected_admission_limit,
442 "entries": self.runtime_config.entries,
443 "model_capabilities": self.model_capabilities,
444 "hardware_capabilities": self.hardware_capabilities,
445 "workload_profile": self.workload_profile,
446 "admission": self.admission_summary_document(),
447 "decisions": self.decisions,
448 })
449 }
450
451 pub fn admission_summary_document(&self) -> serde_json::Value {
452 let max_sequences = self.selected_usize("max_sequences");
453 let recurrent_state_max_slots = self.selected_recurrent_state_max_slots();
454 let effective_max_concurrent = effective_admission_limit(
455 self.execution_resource_authority,
456 max_sequences,
457 recurrent_state_max_slots,
458 );
459 let kv_blocks = self.selected_usize("kv_block_count");
460 let max_batched_tokens = self.selected_usize("max_batched_tokens");
461 let max_model_len = self.selected_usize("max_model_len");
462 let kv_capacity_tokens =
463 kv_blocks.map(|blocks| blocks.saturating_mul(DEFAULT_KV_BLOCK_SIZE_TOKENS));
464 let kv_bytes_per_token = kv_cache_bytes_per_token_for_model(&self.model_capabilities);
465 let recurrent_budget =
466 recurrent_state_budget_for(&self.model_capabilities, &self.hardware_capabilities);
467 let scheduler_policy = self
468 .selected_string("scheduler_admission_policy")
469 .unwrap_or_else(|| "unknown".to_string());
470 serde_json::json!({
471 "schema_version": 1,
472 "backend": self.hardware_capabilities.backend,
473 "model_architecture": self.model_capabilities.architecture,
474 "resource_authority": self.execution_resource_authority,
475 "source": "startup_preflight",
476 "scheduler_policy": scheduler_policy,
477 "effective_max_concurrent": effective_max_concurrent,
478 "maximum_active_sequences": max_sequences,
479 "maximum_scheduled_tokens": max_batched_tokens,
480 "queue_depth": 0u64,
481 "active_prefill": 0u64,
482 "active_decode": 0u64,
483 "current_batch_size": 0u64,
484 "rejected_requests_total": 0u64,
485 "failed_requests_total": 0u64,
486 "completed_requests_total": 0u64,
487 "max_sequences": max_sequences,
488 "recurrent_state_max_slots": if self.execution_resource_authority == ExecutionResourceAuthority::LegacyEngine {
489 recurrent_state_max_slots
490 } else {
491 None
492 },
493 "legacy_recurrent_state_max_slots_estimate": recurrent_state_max_slots,
494 "legacy_recurrent_state_limit_applies": self.execution_resource_authority == ExecutionResourceAuthority::LegacyEngine,
495 "kv_block_count": kv_blocks,
496 "kv_block_size_tokens": DEFAULT_KV_BLOCK_SIZE_TOKENS,
497 "kv_capacity_tokens": kv_capacity_tokens,
498 "max_model_length": max_model_len,
499 "max_batched_tokens": max_batched_tokens,
500 "memory_estimate": {
501 "vram_bytes": self.hardware_capabilities.vram_bytes,
502 "estimated_weight_bytes": self.model_capabilities.estimated_weight_bytes,
503 "kv_bytes_per_token": kv_bytes_per_token,
504 "recurrent_state_bytes_per_sequence": self.model_capabilities.recurrent_state_bytes_per_sequence,
505 "recurrent_state_budget_bytes": recurrent_budget.map(|budget| budget.remaining_bytes),
506 "recurrent_state_budget_raw_slots": recurrent_budget.map(|budget| budget.raw_slots),
507 "recurrent_state_budget_max_slots": recurrent_budget.map(|budget| budget.floored_slots),
508 "recurrent_state_capacity_bytes": match (recurrent_state_max_slots, self.model_capabilities.recurrent_state_bytes_per_sequence) {
509 (Some(slots), Some(bytes_per_sequence)) => {
510 (slots as u64).checked_mul(bytes_per_sequence)
511 }
512 _ => None,
513 },
514 "kv_capacity_bytes": match (kv_capacity_tokens, kv_bytes_per_token) {
515 (Some(tokens), Some(bytes_per_token)) => {
516 (tokens as u64).checked_mul(bytes_per_token)
517 }
518 _ => None,
519 },
520 },
521 })
522 }
523
524 pub fn decision_trace_jsonl(&self) -> Result<String, serde_json::Error> {
525 let mut out = String::new();
526 for decision in &self.decisions {
527 out.push_str(&serde_json::to_string(decision)?);
528 out.push('\n');
529 }
530 Ok(out)
531 }
532
533 pub fn runtime_env_hash(&self) -> String {
534 use sha2::{Digest, Sha256};
535
536 let bytes = serde_json::to_vec(&self.runtime_config.entries).unwrap_or_default();
537 let digest = Sha256::digest(bytes);
538 format!("sha256:{digest:x}")
539 }
540
541 fn selected_usize(&self, selection: &str) -> Option<usize> {
542 self.selected_string(selection)?.parse().ok()
543 }
544
545 fn selected_string(&self, selection: &str) -> Option<String> {
546 self.decisions
547 .iter()
548 .find(|decision| decision.selection == selection)
549 .map(|decision| decision.selected.clone())
550 }
551
552 fn selected_recurrent_state_max_slots(&self) -> Option<usize> {
553 self.selected_usize("recurrent_state_max_slots")
554 .or_else(|| self.runtime_usize("FERRUM_RECURRENT_STATE_MAX_SLOTS"))
555 }
556
557 fn selected_graph_mode(&self) -> Option<String> {
558 let decode_graph = self.selected_string("decode_graph_policy");
559 if decode_graph
560 .as_deref()
561 .is_some_and(|mode| mode != "graph_disabled")
562 {
563 return decode_graph;
564 }
565 self.selected_string("moe_graph_policy")
566 }
567
568 fn attention_execution_document(&self) -> serde_json::Value {
569 let compiled_policy = self
570 .compiled_attention_policy
571 .map(AttentionExecutionPolicy::as_runtime_value);
572 let requested_policy = self
573 .requested_attention_policy
574 .map(AttentionExecutionPolicy::as_runtime_value);
575 if self.execution_resource_authority != ExecutionResourceAuthority::PlanRuntime {
576 return serde_json::json!({
577 "schema_version": 1,
578 "resource_authority": self.execution_resource_authority,
579 "requested_policy": null,
580 "compiled_policy": null,
581 "selection_scope": "legacy_runtime",
582 "observed_variant_source": "legacy_backend_profile",
583 "legacy_attention_keys_apply": true,
584 });
585 }
586 let native_adaptive =
587 compiled_policy == Some(AttentionExecutionPolicy::NativeAdaptive.as_runtime_value());
588 serde_json::json!({
589 "schema_version": 1,
590 "resource_authority": self.execution_resource_authority,
591 "requested_policy": requested_policy,
592 "compiled_policy": compiled_policy,
593 "selection_scope": "per_invocation",
594 "observed_variant_source": "vnext.device_native_work",
595 "legacy_attention_keys_apply": false,
596 "decode": if native_adaptive && self.hardware_capabilities.backend.eq_ignore_ascii_case("cuda") {
597 serde_json::json!({
598 "selector": "sequence_frontier",
599 "short_variant": "vllm_paged_attention_v1_addressed",
600 "long_variant": "vllm_paged_attention_v2_addressed",
601 "short_max_sequence_tokens": CUDA_NATIVE_ADAPTIVE_V1_MAX_SEQUENCE_TOKENS,
602 })
603 } else {
604 serde_json::json!({
605 "selector": "compiled_provider",
606 })
607 },
608 "prefill": {
609 "selector": "admitted_shape",
610 "variants_are_observed_at_runtime": true,
611 },
612 })
613 }
614
615 fn runtime_entry_value(&self, key: &str) -> Option<String> {
616 self.runtime_config
617 .entries
618 .iter()
619 .find(|entry| entry.key == key)
620 .map(|entry| entry.effective_value.clone())
621 }
622
623 fn runtime_usize(&self, key: &str) -> Option<usize> {
624 self.runtime_entry_value(key)?.parse().ok()
625 }
626
627 fn runtime_csv_usize(&self, key: &str) -> Option<Vec<usize>> {
628 let raw = self.runtime_entry_value(key)?;
629 let mut out = Vec::new();
630 for part in raw.split(',') {
631 let value = part.trim();
632 if value.is_empty() {
633 return None;
634 }
635 out.push(value.parse().ok()?);
636 }
637 Some(out)
638 }
639
640 fn runtime_json_value(&self, key: &str) -> Option<serde_json::Value> {
641 serde_json::from_str(&self.runtime_entry_value(key)?).ok()
642 }
643}
644
645#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
646pub struct AutoConfigDecision {
647 pub schema_version: u32,
648 pub selection: String,
649 pub selected: String,
650 pub source: AutoConfigSource,
651 pub source_key: Option<String>,
652 pub candidates: Vec<String>,
653 pub rejected: Vec<RejectedCandidate>,
654 pub affects: Vec<RuntimeConfigEffect>,
655}
656
657#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
658pub struct RejectedCandidate {
659 pub value: String,
660 pub reason: String,
661}
662
663#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
664#[serde(rename_all = "snake_case")]
665pub enum AutoConfigSource {
666 Default,
667 Cli,
668 ConfigFile,
669 Env,
670 ScriptCase,
671 ModelMetadata,
672 HardwareCapability,
673 MemoryProfile,
674 WorkloadPreset,
675 CompiledFeature,
676}
677
678#[derive(Debug, Clone, PartialEq, Eq, Error)]
679pub enum AutoConfigError {
680 #[error("{key}: invalid override: {reason}")]
681 InvalidOverride { key: String, reason: String },
682 #[error("{selection}: unsupported combination: {reason}")]
683 UnsupportedCombination { selection: String, reason: String },
684}
685
686pub struct FerrumConfigBuilder {
687 runtime_config: RuntimeConfigSnapshot,
688 model: ModelCapabilities,
689 hardware: HardwareCapabilities,
690 workload: WorkloadProfile,
691 execution_resource_authority: ExecutionResourceAuthority,
692}
693
694impl FerrumConfigBuilder {
695 pub fn new(runtime_config: RuntimeConfigSnapshot) -> Self {
696 Self {
697 runtime_config,
698 model: ModelCapabilities::unknown(),
699 hardware: HardwareCapabilities::unknown(),
700 workload: WorkloadProfile::default(),
701 execution_resource_authority: ExecutionResourceAuthority::LegacyEngine,
702 }
703 }
704
705 pub fn m3_qwen3_30b_a3b_int4(runtime_config: RuntimeConfigSnapshot) -> Self {
706 Self::new(runtime_config)
707 .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
708 .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
709 CompiledKernelFeatures::m3_fast_path_without_fa2(),
710 ))
711 .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
712 }
713
714 pub fn with_model_capabilities(mut self, model: ModelCapabilities) -> Self {
715 self.model = model;
716 self
717 }
718
719 pub fn with_hardware_capabilities(mut self, hardware: HardwareCapabilities) -> Self {
720 self.hardware = hardware;
721 self
722 }
723
724 pub fn with_workload_profile(mut self, workload: WorkloadProfile) -> Self {
725 self.workload = workload;
726 self
727 }
728
729 pub fn with_execution_resource_authority(
730 mut self,
731 authority: ExecutionResourceAuthority,
732 ) -> Self {
733 self.execution_resource_authority = authority;
734 self
735 }
736
737 pub fn resolve(self) -> Result<ResolvedFerrumConfig, AutoConfigError> {
738 let mut decisions = Vec::new();
739 let cuda_backend = self.is_cuda_backend();
740 let plan_runtime =
741 self.execution_resource_authority == ExecutionResourceAuthority::PlanRuntime;
742 if plan_runtime {
743 for key in [
744 "FERRUM_USE_VLLM_PAGED_ATTN",
745 "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
746 ] {
747 if self.entry(key).is_some() {
748 return Err(AutoConfigError::InvalidOverride {
749 key: key.to_owned(),
750 reason: format!(
751 "legacy attention control does not apply to PlanRuntime; use FERRUM_ATTENTION_POLICY=auto|portable|native-adaptive"
752 ),
753 });
754 }
755 }
756 }
757 let plan_attention = plan_runtime
758 .then(|| self.resolve_plan_attention_policy())
759 .transpose()?;
760 let cuda_gptq_moe = cuda_backend
767 && self.model.moe.is_some()
768 && self.model.quantization.as_deref().is_some_and(|q| {
769 let q = q.to_ascii_lowercase();
770 q.contains("gptq") || q.contains("int4")
771 });
772 let cuda_qwen_moe = cuda_backend
773 && self.model.moe.is_some()
774 && qwen_moe_architecture_uses_vllm_paged_attn(&self.model.architecture);
775 let use_vllm_paged_attn = self.bool_value(
776 "FERRUM_USE_VLLM_PAGED_ATTN",
777 (self.workload.is_m3_preset() || cuda_qwen_moe)
778 && cuda_backend
779 && self.hardware.compiled_features.vllm_paged_attn,
780 AutoConfigSource::WorkloadPreset,
781 )?;
782 let fa_layout =
783 self.bool_value("FERRUM_FA_LAYOUT_VARLEN", false, AutoConfigSource::Default)?;
784 let fa2_source = self.bool_value("FERRUM_FA2_SOURCE", false, AutoConfigSource::Default)?;
785 let shim_present = self.raw("FERRUM_FA2_DIRECT_FFI_SHIM").is_some();
786 let fa2_direct_ffi = self.bool_value(
787 "FERRUM_FA2_DIRECT_FFI",
788 shim_present,
789 if shim_present {
790 AutoConfigSource::Env
791 } else {
792 AutoConfigSource::Default
793 },
794 )?;
795 let fa2_native_manifest = self.optional_string_value(FA2_NATIVE_MANIFEST_KEY)?;
796 let fa2_native_artifact = self.optional_string_value(FA2_NATIVE_ARTIFACT_KEY)?;
797 let fa2_native_source_sha256 = self.optional_string_value(FA2_NATIVE_SOURCE_SHA256_KEY)?;
798 let fa2_native_inputs_sha256 = self.optional_string_value(FA2_NATIVE_INPUTS_SHA256_KEY)?;
799 let vllm_v1_short = self.bool_value(
800 "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
801 use_vllm_paged_attn.value && self.model.head_dim.unwrap_or(128) <= 128,
802 AutoConfigSource::Default,
803 )?;
804 let vllm_moe = self.bool_value(
805 "FERRUM_VLLM_MOE",
806 (cuda_gptq_moe || (self.workload.is_m3_preset() && cuda_backend))
807 && self.hardware.compiled_features.vllm_moe_marlin,
808 AutoConfigSource::WorkloadPreset,
809 )?;
810 let device_route = self.bool_value(
811 "FERRUM_MOE_DEVICE_ROUTE",
812 vllm_moe.value,
813 AutoConfigSource::WorkloadPreset,
814 )?;
815 let pair_ids = self.bool_value(
816 "FERRUM_VLLM_MOE_PAIR_IDS",
817 vllm_moe.value,
818 AutoConfigSource::WorkloadPreset,
819 )?;
820 let graph = self.bool_value("FERRUM_MOE_GRAPH", false, AutoConfigSource::WorkloadPreset)?;
821 let batched_graph =
822 self.bool_value("FERRUM_BATCHED_GRAPH", false, AutoConfigSource::Default)?;
823 let reusable_execution = self.bool_value(
824 "FERRUM_REUSABLE_EXECUTION",
825 cuda_backend
826 && self.hardware.graph_support
827 && self.hardware.compiled_features.cuda_graph,
828 AutoConfigSource::HardwareCapability,
829 )?;
830 let unified_graph =
831 self.bool_value("FERRUM_UNIFIED_GRAPH", false, AutoConfigSource::Default)?;
832 let unified_graph_layers_only = self.bool_value(
833 "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY",
834 false,
835 AutoConfigSource::Default,
836 )?;
837 let unified_graph_lm_head_eager = self.bool_value(
838 "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
839 false,
840 AutoConfigSource::Default,
841 )?;
842 let greedy = self.bool_value(
843 "FERRUM_GREEDY_ARGMAX",
844 (cuda_backend || self.hardware.backend.eq_ignore_ascii_case("metal"))
845 && self.hardware.compiled_features.greedy_argmax,
846 AutoConfigSource::HardwareCapability,
847 )?;
848 let prefix_cache = self.bool_value(
849 "FERRUM_PREFIX_CACHE",
850 false,
851 if self.workload.is_m3_preset() {
852 AutoConfigSource::WorkloadPreset
853 } else {
854 AutoConfigSource::Default
855 },
856 )?;
857 let default_max_sequences = self.default_max_sequences();
858 let max_sequences = self.usize_value(
859 "FERRUM_PAGED_MAX_SEQS",
860 default_max_sequences.value,
861 default_max_sequences.source,
862 )?;
863 if plan_runtime && self.entry("FERRUM_RECURRENT_STATE_MAX_SLOTS").is_some() {
864 return self.invalid(
865 "FERRUM_RECURRENT_STATE_MAX_SLOTS",
866 "legacy engine recurrent-state slots do not control the plan runtime; use max sequences as the protocol ceiling and let dynamic resource admission defer on live capacity",
867 );
868 }
869 let default_recurrent_state_max_slots = (!plan_runtime)
870 .then(|| self.default_recurrent_state_max_slots(&max_sequences))
871 .flatten();
872 let recurrent_state_max_slots = if default_recurrent_state_max_slots.is_some()
873 || self.entry("FERRUM_RECURRENT_STATE_MAX_SLOTS").is_some()
874 {
875 let default = default_recurrent_state_max_slots
876 .as_ref()
877 .unwrap_or(&max_sequences);
878 Some(self.usize_value(
879 "FERRUM_RECURRENT_STATE_MAX_SLOTS",
880 default.value,
881 default.source,
882 )?)
883 } else {
884 None
885 };
886 let default_kv_blocks = self.default_kv_blocks(&max_sequences);
887 let kv_blocks = self.usize_value(
888 "FERRUM_KV_MAX_BLOCKS",
889 default_kv_blocks.value,
890 default_kv_blocks.source,
891 )?;
892 let default_max_batched_tokens =
893 self.default_max_batched_tokens(&max_sequences, &kv_blocks);
894 let max_batched_tokens = self.usize_value(
895 "FERRUM_MAX_BATCHED_TOKENS",
896 default_max_batched_tokens.value,
897 default_max_batched_tokens.source,
898 )?;
899 let max_model_len = self.optional_usize_value("FERRUM_MAX_MODEL_LEN")?;
900 let default_prefill_first_until_active =
901 self.default_prefill_first_until_active(&max_sequences);
902 if !plan_runtime {
903 self.validate_attention(
904 use_vllm_paged_attn.value,
905 fa_layout.value,
906 fa2_source.value,
907 fa2_direct_ffi.value,
908 shim_present,
909 vllm_v1_short.value,
910 )?;
911 self.validate_fa2_native_artifact(
912 fa2_native_manifest.as_ref(),
913 fa2_native_artifact.as_ref(),
914 fa2_native_source_sha256.as_ref(),
915 fa2_native_inputs_sha256.as_ref(),
916 fa2_source.value,
917 fa2_direct_ffi.value,
918 )?;
919 }
920 self.validate_moe(
921 vllm_moe.value,
922 device_route.value,
923 pair_ids.value,
924 graph.value,
925 )?;
926 self.validate_batched_graph(batched_graph.value)?;
927 self.validate_unified_graph(
928 unified_graph.value,
929 unified_graph_layers_only.value,
930 unified_graph_lm_head_eager.value,
931 )?;
932 self.validate_memory(
933 kv_blocks.value,
934 max_sequences.value,
935 recurrent_state_max_slots.as_ref().map(|slots| slots.value),
936 max_batched_tokens.value,
937 max_model_len.as_ref().map(|value| value.value),
938 )?;
939 self.validate_dtypes()?;
940 self.validate_layer_split_pipeline_mode()?;
941 self.validate_sampling(greedy.value)?;
942
943 if let Some((_, compiled_attention)) = plan_attention.as_ref() {
944 decisions.push(self.plan_attention_execution_decision(compiled_attention));
945 decisions.push(self.plan_attention_phase_decision(
946 "attention_prefill_mixed_backend",
947 compiled_attention,
948 ));
949 decisions.push(
950 self.plan_attention_phase_decision("attention_decode_backend", compiled_attention),
951 );
952 } else {
953 decisions.push(self.attention_prefill_decision(
954 use_vllm_paged_attn.clone(),
955 fa_layout,
956 fa2_source,
957 fa2_direct_ffi,
958 ));
959 decisions.push(self.fa2_native_artifact_decision(
960 fa2_native_manifest.as_ref(),
961 fa2_native_artifact.as_ref(),
962 ));
963 decisions.push(self.fa2_native_runtime_selection_decision(
964 fa2_native_manifest.as_ref(),
965 fa2_native_artifact.as_ref(),
966 ));
967 decisions.push(
968 self.attention_decode_decision(use_vllm_paged_attn.clone(), vllm_v1_short.clone()),
969 );
970 }
971 let mut runtime_config = self.runtime_config.clone();
978 let legacy_attention_values = [
979 ("FERRUM_USE_VLLM_PAGED_ATTN", &use_vllm_paged_attn),
980 ("FERRUM_VLLM_PAGED_ATTN_V1_SHORT", &vllm_v1_short),
981 ];
982 for (key, resolved) in legacy_attention_values
983 .into_iter()
984 .filter(|_| !plan_runtime)
985 .chain([
986 ("FERRUM_VLLM_MOE", &vllm_moe),
987 ("FERRUM_MOE_DEVICE_ROUTE", &device_route),
988 ("FERRUM_VLLM_MOE_PAIR_IDS", &pair_ids),
989 ("FERRUM_BATCHED_GRAPH", &batched_graph),
990 ("FERRUM_REUSABLE_EXECUTION", &reusable_execution),
991 ("FERRUM_UNIFIED_GRAPH", &unified_graph),
992 (
993 "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY",
994 &unified_graph_layers_only,
995 ),
996 (
997 "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
998 &unified_graph_lm_head_eager,
999 ),
1000 ("FERRUM_GREEDY_ARGMAX", &greedy),
1001 ])
1002 {
1003 if self.entry(key).is_none() {
1004 runtime_config.upsert(
1005 key,
1006 if resolved.value { "1" } else { "0" },
1007 RuntimeConfigSource::MemoryProfile,
1008 );
1009 }
1010 }
1011 if self.entry("FERRUM_ATTENTION_POLICY").is_none() {
1012 if let Some((_, compiled_attention)) = plan_attention.as_ref() {
1013 runtime_config.upsert(
1014 "FERRUM_ATTENTION_POLICY",
1015 compiled_attention.value.as_runtime_value(),
1016 RuntimeConfigSource::Default,
1017 );
1018 }
1019 }
1020 if let Some(until) = default_prefill_first_until_active.as_ref() {
1021 if self
1022 .entry("FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
1023 .is_none()
1024 && self.entry("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE").is_none()
1025 {
1026 runtime_config.upsert(
1027 "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE",
1028 until.value.to_string(),
1029 RuntimeConfigSource::Default,
1030 );
1031 }
1032 }
1033 if let Some(slots) = recurrent_state_max_slots.as_ref() {
1034 if self.entry("FERRUM_RECURRENT_STATE_MAX_SLOTS").is_none() {
1035 runtime_config.upsert(
1036 "FERRUM_RECURRENT_STATE_MAX_SLOTS",
1037 slots.value.to_string(),
1038 RuntimeConfigSource::MemoryProfile,
1039 );
1040 }
1041 }
1042 decisions.push(self.moe_decision(vllm_moe, device_route, pair_ids));
1043 decisions.push(self.graph_decision(graph));
1044 decisions.push(self.reusable_execution_decision(reusable_execution));
1045 decisions.push(self.decode_graph_decision(
1046 batched_graph,
1047 unified_graph,
1048 unified_graph_layers_only,
1049 unified_graph_lm_head_eager,
1050 ));
1051 decisions.push(self.scalar_decision(
1052 "kv_block_count",
1053 kv_blocks,
1054 RuntimeConfigEffect::Memory,
1055 ));
1056 decisions.push(self.scalar_decision(
1057 "max_sequences",
1058 max_sequences,
1059 RuntimeConfigEffect::Memory,
1060 ));
1061 if let Some(slots) = recurrent_state_max_slots {
1062 decisions.push(self.scalar_decision(
1063 "recurrent_state_max_slots",
1064 slots,
1065 RuntimeConfigEffect::Memory,
1066 ));
1067 }
1068 decisions.push(self.scalar_decision(
1069 "max_batched_tokens",
1070 max_batched_tokens,
1071 RuntimeConfigEffect::Performance,
1072 ));
1073 if let Some(max_model_len) = max_model_len {
1074 decisions.push(self.scalar_decision(
1075 "max_model_len",
1076 max_model_len,
1077 RuntimeConfigEffect::Memory,
1078 ));
1079 }
1080 decisions.push(self.prefix_cache_decision(prefix_cache));
1081 decisions.push(self.scheduler_decision(default_prefill_first_until_active)?);
1082 decisions.push(self.sampling_decision(greedy));
1083
1084 Ok(ResolvedFerrumConfig {
1085 schema_version: 1,
1086 preset: self.workload.preset.clone(),
1087 execution_resource_authority: self.execution_resource_authority,
1088 requested_attention_policy: plan_attention
1089 .as_ref()
1090 .map(|(requested, _)| requested.value),
1091 compiled_attention_policy: plan_attention.as_ref().map(|(_, compiled)| compiled.value),
1092 runtime_config,
1093 model_capabilities: self.model.clone(),
1094 hardware_capabilities: self.hardware.clone(),
1095 workload_profile: self.workload.clone(),
1096 decisions,
1097 })
1098 }
1099
1100 fn entries(&self) -> BTreeMap<&str, &str> {
1101 self.runtime_config
1102 .entries
1103 .iter()
1104 .map(|entry| (entry.key.as_str(), entry.effective_value.as_str()))
1105 .collect()
1106 }
1107
1108 fn raw(&self, key: &str) -> Option<&str> {
1109 self.entry(key).map(|entry| entry.effective_value.as_str())
1110 }
1111
1112 fn entry(&self, key: &str) -> Option<&RuntimeConfigEntry> {
1113 self.runtime_config
1114 .entries
1115 .iter()
1116 .find(|entry| entry.key == key)
1117 }
1118
1119 fn source_for_key(&self, key: &str, default_source: AutoConfigSource) -> AutoConfigSource {
1120 self.entry(key)
1121 .map(|entry| auto_config_source_from_runtime(entry.source))
1122 .unwrap_or(default_source)
1123 }
1124
1125 fn is_cuda_backend(&self) -> bool {
1126 self.hardware.backend.eq_ignore_ascii_case("cuda")
1127 }
1128
1129 fn is_accelerator_backend(&self) -> bool {
1130 self.is_cuda_backend() || self.hardware.backend.eq_ignore_ascii_case("metal")
1131 }
1132
1133 fn cuda_compute_capability_at_least(&self, major: u32, minor: u32) -> Option<bool> {
1134 let (actual_major, actual_minor) =
1135 parse_compute_capability(self.hardware.compute_capability.as_deref()?)?;
1136 Some((actual_major, actual_minor) >= (major, minor))
1137 }
1138
1139 fn default_max_sequences(&self) -> ResolvedValue<usize> {
1140 let target = self.workload.target_concurrency.max(1);
1141 let mut selected = target;
1142 if self.workload.is_m3_preset() {
1143 if let Some(sm_count) = self.hardware.sm_count {
1144 selected = selected.min((sm_count as usize / 4).max(1));
1148 }
1149 if let Some(vram_bytes) = self.hardware.vram_bytes {
1150 selected = selected.min(vram_default_max_sequences(vram_bytes));
1151 }
1152 }
1153 ResolvedValue {
1154 value: selected.max(1),
1155 source: if selected < target {
1156 AutoConfigSource::HardwareCapability
1157 } else {
1158 AutoConfigSource::WorkloadPreset
1159 },
1160 source_key: None,
1161 }
1162 }
1163
1164 fn default_max_batched_tokens(
1165 &self,
1166 max_sequences: &ResolvedValue<usize>,
1167 kv_blocks: &ResolvedValue<usize>,
1168 ) -> ResolvedValue<usize> {
1169 let kv_token_capacity = kv_blocks
1170 .value
1171 .saturating_mul(DEFAULT_KV_BLOCK_SIZE_TOKENS)
1172 .max(max_sequences.value.max(1));
1173 let target = if self
1174 .workload
1175 .is_preset(QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET)
1176 {
1177 1536
1178 } else {
1179 max_sequences.value.max(1).saturating_mul(64)
1180 };
1181 let value = target
1182 .min(kv_token_capacity)
1183 .max(max_sequences.value.max(1));
1184 ResolvedValue {
1185 value,
1186 source: if max_sequences.source == AutoConfigSource::HardwareCapability
1187 || kv_blocks.source == AutoConfigSource::HardwareCapability
1188 {
1189 AutoConfigSource::HardwareCapability
1190 } else {
1191 AutoConfigSource::WorkloadPreset
1192 },
1193 source_key: None,
1194 }
1195 }
1196
1197 fn default_prefill_first_until_active(
1198 &self,
1199 max_sequences: &ResolvedValue<usize>,
1200 ) -> Option<ResolvedValue<usize>> {
1201 if max_sequences.value <= 1 || !self.is_accelerator_backend() {
1202 return None;
1203 }
1204 Some(ResolvedValue {
1205 value: max_sequences.value,
1206 source: AutoConfigSource::Default,
1207 source_key: None,
1208 })
1209 }
1210
1211 fn default_kv_blocks(&self, max_sequences: &ResolvedValue<usize>) -> ResolvedValue<usize> {
1212 let min_blocks = ceil_div(max_sequences.value.max(1), DEFAULT_KV_BLOCK_SIZE_TOKENS);
1213 if self
1214 .workload
1215 .is_preset(QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET)
1216 {
1217 return ResolvedValue {
1218 value: 1024.max(min_blocks),
1219 source: AutoConfigSource::WorkloadPreset,
1220 source_key: None,
1221 };
1222 }
1223 let target = DEFAULT_KV_BLOCKS.max(min_blocks);
1224 let selected = match (
1225 self.hardware.vram_bytes,
1226 self.model.estimated_weight_bytes,
1227 self.kv_cache_bytes_per_token(),
1228 ) {
1229 (Some(vram_bytes), Some(weight_bytes), Some(kv_bytes_per_token))
1230 if kv_bytes_per_token > 0 =>
1231 {
1232 let headroom = (vram_bytes / 10).max(2 * GIB);
1233 let available = vram_bytes.saturating_sub(weight_bytes.saturating_add(headroom));
1234 let kv_token_budget = (available / kv_bytes_per_token) as usize;
1235 let block_budget = kv_token_budget / DEFAULT_KV_BLOCK_SIZE_TOKENS;
1236 target.min(block_budget.max(min_blocks))
1237 }
1238 _ => target,
1239 };
1240 ResolvedValue {
1241 value: selected.max(1),
1242 source: if selected < target {
1243 AutoConfigSource::HardwareCapability
1244 } else {
1245 AutoConfigSource::WorkloadPreset
1246 },
1247 source_key: None,
1248 }
1249 }
1250
1251 fn default_recurrent_state_max_slots(
1252 &self,
1253 max_sequences: &ResolvedValue<usize>,
1254 ) -> Option<ResolvedValue<usize>> {
1255 let limit = self.recurrent_state_budget_max_slots()?;
1256 let selected = max_sequences.value.min(limit.max(1));
1257 Some(ResolvedValue {
1258 value: selected.max(1),
1259 source: if selected < max_sequences.value {
1260 AutoConfigSource::MemoryProfile
1261 } else {
1262 max_sequences.source
1263 },
1264 source_key: None,
1265 })
1266 }
1267
1268 fn recurrent_state_budget_max_slots(&self) -> Option<usize> {
1269 self.recurrent_state_budget()
1270 .map(|budget| budget.floored_slots)
1271 }
1272
1273 fn recurrent_state_budget(&self) -> Option<RecurrentStateBudget> {
1274 recurrent_state_budget_for(&self.model, &self.hardware)
1275 }
1276
1277 fn kv_cache_bytes_per_token(&self) -> Option<u64> {
1278 kv_cache_bytes_per_token_for_model(&self.model)
1279 }
1280
1281 fn bool_value(
1282 &self,
1283 key: &str,
1284 default: bool,
1285 default_source: AutoConfigSource,
1286 ) -> Result<ResolvedValue<bool>, AutoConfigError> {
1287 match self.entry(key) {
1288 Some(entry) => Ok(ResolvedValue {
1289 value: parse_bool_env_value(&entry.effective_value).map_err(|reason| {
1290 AutoConfigError::InvalidOverride {
1291 key: key.to_string(),
1292 reason,
1293 }
1294 })?,
1295 source: auto_config_source_from_runtime(entry.source),
1296 source_key: Some(key.to_string()),
1297 }),
1298 None => Ok(ResolvedValue {
1299 value: default,
1300 source: default_source,
1301 source_key: None,
1302 }),
1303 }
1304 }
1305
1306 fn usize_value(
1307 &self,
1308 key: &str,
1309 default: usize,
1310 default_source: AutoConfigSource,
1311 ) -> Result<ResolvedValue<usize>, AutoConfigError> {
1312 match self.entry(key) {
1313 Some(entry) => Ok(ResolvedValue {
1314 value: parse_usize_env_value(&entry.effective_value).map_err(|reason| {
1315 AutoConfigError::InvalidOverride {
1316 key: key.to_string(),
1317 reason,
1318 }
1319 })?,
1320 source: auto_config_source_from_runtime(entry.source),
1321 source_key: Some(key.to_string()),
1322 }),
1323 None => Ok(ResolvedValue {
1324 value: default,
1325 source: default_source,
1326 source_key: None,
1327 }),
1328 }
1329 }
1330
1331 fn optional_usize_value(
1332 &self,
1333 key: &str,
1334 ) -> Result<Option<ResolvedValue<usize>>, AutoConfigError> {
1335 match self.entry(key) {
1336 Some(entry) => Ok(Some(ResolvedValue {
1337 value: parse_usize_env_value(&entry.effective_value).map_err(|reason| {
1338 AutoConfigError::InvalidOverride {
1339 key: key.to_string(),
1340 reason,
1341 }
1342 })?,
1343 source: auto_config_source_from_runtime(entry.source),
1344 source_key: Some(key.to_string()),
1345 })),
1346 None => Ok(None),
1347 }
1348 }
1349
1350 fn optional_string_value(
1351 &self,
1352 key: &str,
1353 ) -> Result<Option<ResolvedValue<String>>, AutoConfigError> {
1354 match self.entry(key) {
1355 Some(entry) if entry.effective_value.trim().is_empty() => {
1356 Err(AutoConfigError::InvalidOverride {
1357 key: key.to_string(),
1358 reason: "must be non-empty".to_string(),
1359 })
1360 }
1361 Some(entry) => Ok(Some(ResolvedValue {
1362 value: entry.effective_value.clone(),
1363 source: auto_config_source_from_runtime(entry.source),
1364 source_key: Some(key.to_string()),
1365 })),
1366 None => Ok(None),
1367 }
1368 }
1369
1370 fn validate_attention(
1371 &self,
1372 use_vllm_paged_attn: bool,
1373 fa_layout: bool,
1374 fa2_source: bool,
1375 fa2_direct_ffi: bool,
1376 shim_present: bool,
1377 vllm_v1_short: bool,
1378 ) -> Result<(), AutoConfigError> {
1379 if use_vllm_paged_attn && !self.hardware.compiled_features.vllm_paged_attn {
1380 return self.invalid(
1381 "FERRUM_USE_VLLM_PAGED_ATTN",
1382 "vLLM paged attention is not compiled",
1383 );
1384 }
1385 if use_vllm_paged_attn && !self.is_cuda_backend() {
1386 return self.invalid(
1387 "FERRUM_USE_VLLM_PAGED_ATTN",
1388 "vLLM paged attention requires CUDA backend",
1389 );
1390 }
1391 if fa_layout && !use_vllm_paged_attn {
1392 return self.invalid(
1393 "FERRUM_FA_LAYOUT_VARLEN",
1394 "FA layout requires vLLM paged attention layout",
1395 );
1396 }
1397 if fa2_source {
1398 return self.invalid(
1399 "FERRUM_FA2_SOURCE",
1400 "source-linked FA2 path has been removed; use a native operator artifact",
1401 );
1402 }
1403 if fa2_direct_ffi && !self.hardware.compiled_features.fa2_direct_ffi {
1404 return self.invalid(
1405 "FERRUM_FA2_DIRECT_FFI",
1406 "direct FA2 FFI shim support is not compiled",
1407 );
1408 }
1409 if fa2_direct_ffi && !self.is_cuda_backend() {
1410 return self.invalid(
1411 "FERRUM_FA2_DIRECT_FFI",
1412 "direct FA2 FFI shim requires CUDA backend",
1413 );
1414 }
1415 if fa2_direct_ffi && self.cuda_compute_capability_at_least(8, 0) == Some(false) {
1416 return self.invalid(
1417 "FERRUM_FA2_DIRECT_FFI",
1418 "direct FA2 FFI shim requires CUDA compute capability >= 8.0",
1419 );
1420 }
1421 if fa2_direct_ffi && !shim_present {
1422 return self.invalid(
1423 "FERRUM_FA2_DIRECT_FFI",
1424 "requires FERRUM_FA2_DIRECT_FFI_SHIM",
1425 );
1426 }
1427 if fa2_source && fa2_direct_ffi {
1428 return self.unsupported(
1429 "attention_prefill_mixed_backend",
1430 "FA2 source and direct FFI shim cannot both own the prefill path",
1431 );
1432 }
1433 if vllm_v1_short && !use_vllm_paged_attn {
1434 return self.invalid(
1435 "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
1436 "short-context v1 requires vLLM paged attention",
1437 );
1438 }
1439 Ok(())
1440 }
1441
1442 fn validate_fa2_native_artifact(
1443 &self,
1444 manifest: Option<&ResolvedValue<String>>,
1445 artifact: Option<&ResolvedValue<String>>,
1446 source_sha256: Option<&ResolvedValue<String>>,
1447 inputs_sha256: Option<&ResolvedValue<String>>,
1448 fa2_source: bool,
1449 fa2_direct_ffi: bool,
1450 ) -> Result<(), AutoConfigError> {
1451 let configured = manifest.is_some() || artifact.is_some();
1452 if manifest.is_some() != artifact.is_some() {
1453 let key = if manifest.is_none() {
1454 FA2_NATIVE_MANIFEST_KEY
1455 } else {
1456 FA2_NATIVE_ARTIFACT_KEY
1457 };
1458 return self.invalid(
1459 key,
1460 "FA2 native operator manifest and artifact must be configured together",
1461 );
1462 }
1463 for (key, value) in [
1464 (FA2_NATIVE_SOURCE_SHA256_KEY, source_sha256),
1465 (FA2_NATIVE_INPUTS_SHA256_KEY, inputs_sha256),
1466 ] {
1467 if let Some(value) = value {
1468 if !is_sha256_digest(&value.value) {
1469 return self.invalid(key, "must be a lowercase hex sha256 digest");
1470 }
1471 if !configured {
1472 return self.invalid(
1473 key,
1474 "sha256 pins require FA2 native operator manifest and artifact",
1475 );
1476 }
1477 }
1478 }
1479 if configured && !self.is_cuda_backend() {
1480 return self.invalid(
1481 FA2_NATIVE_MANIFEST_KEY,
1482 "FA2 native operator artifacts require CUDA backend",
1483 );
1484 }
1485 if configured && !self.hardware.compiled_features.fa2_native_operator_artifact {
1486 return self.invalid(
1487 FA2_NATIVE_MANIFEST_KEY,
1488 "FA2 native operator artifact config requires a binary built with a validated native operator artifact",
1489 );
1490 }
1491 if configured {
1492 let metadata = self
1493 .hardware
1494 .compiled_features
1495 .fa2_native_operator_artifact_metadata
1496 .as_ref()
1497 .ok_or_else(|| AutoConfigError::InvalidOverride {
1498 key: FA2_NATIVE_MANIFEST_KEY.to_string(),
1499 reason: "FA2 native operator artifact capability is missing build metadata"
1500 .to_string(),
1501 })?;
1502 let manifest = manifest.expect("configured manifest is present");
1503 let artifact = artifact.expect("configured artifact is present");
1504 if manifest.value != metadata.manifest_path {
1505 return self.invalid(
1506 FA2_NATIVE_MANIFEST_KEY,
1507 "FA2 native operator manifest does not match the artifact linked into this binary",
1508 );
1509 }
1510 if artifact.value != metadata.artifact_path {
1511 return self.invalid(
1512 FA2_NATIVE_ARTIFACT_KEY,
1513 "FA2 native operator artifact does not match the artifact linked into this binary",
1514 );
1515 }
1516 if let Some(source_sha256) = source_sha256 {
1517 if source_sha256.value != metadata.source_package_sha256 {
1518 return self.invalid(
1519 FA2_NATIVE_SOURCE_SHA256_KEY,
1520 "FA2 native operator source_package sha256 does not match the artifact linked into this binary",
1521 );
1522 }
1523 }
1524 if let Some(inputs_sha256) = inputs_sha256 {
1525 if inputs_sha256.value != metadata.inputs_sha256 {
1526 return self.invalid(
1527 FA2_NATIVE_INPUTS_SHA256_KEY,
1528 "FA2 native operator inputs sha256 does not match the artifact linked into this binary",
1529 );
1530 }
1531 }
1532 }
1533 if configured && fa2_source {
1534 return self.unsupported(
1535 "attention_prefill_mixed_backend",
1536 "FA2 native operator artifact cannot be combined with removed source-linked FA2",
1537 );
1538 }
1539 if configured && fa2_direct_ffi {
1540 return self.unsupported(
1541 "attention_prefill_mixed_backend",
1542 "FA2 native operator artifact and diagnostic direct FFI shim cannot both own the prefill path",
1543 );
1544 }
1545 Ok(())
1546 }
1547
1548 fn validate_moe(
1549 &self,
1550 vllm_moe: bool,
1551 device_route: bool,
1552 pair_ids: bool,
1553 graph: bool,
1554 ) -> Result<(), AutoConfigError> {
1555 if vllm_moe && !self.hardware.compiled_features.vllm_moe_marlin {
1556 return self.invalid("FERRUM_VLLM_MOE", "vLLM Marlin MoE is not compiled");
1557 }
1558 if vllm_moe && !self.is_cuda_backend() {
1559 return self.invalid("FERRUM_VLLM_MOE", "vLLM Marlin MoE requires CUDA backend");
1560 }
1561 if device_route && !vllm_moe {
1562 return self.invalid(
1563 "FERRUM_MOE_DEVICE_ROUTE",
1564 "device route currently requires vLLM MoE",
1565 );
1566 }
1567 if pair_ids && !vllm_moe {
1568 return self.invalid(
1569 "FERRUM_VLLM_MOE_PAIR_IDS",
1570 "pair-id routing requires vLLM MoE",
1571 );
1572 }
1573 let graph_relevant = self.model.moe.is_some() || self.workload.is_m3_preset();
1574 if graph && graph_relevant && !self.hardware.graph_support {
1575 return self.invalid(
1576 "FERRUM_MOE_GRAPH",
1577 "hardware/backend does not support CUDA graph replay",
1578 );
1579 }
1580 if graph && graph_relevant && !self.hardware.compiled_features.cuda_graph {
1581 return self.invalid("FERRUM_MOE_GRAPH", "CUDA graph support is not compiled");
1582 }
1583 if graph && graph_relevant && !vllm_moe {
1584 return self.invalid(
1585 "FERRUM_MOE_GRAPH",
1586 "graph decode requires the graph-clean vLLM MoE path",
1587 );
1588 }
1589 if graph && graph_relevant && self.model.moe.is_some() && !self.model.graph_safe_moe {
1590 return self.unsupported(
1591 "moe_graph_policy",
1592 "model MoE path is not marked graph-safe",
1593 );
1594 }
1595 Ok(())
1596 }
1597
1598 fn validate_batched_graph(&self, graph: bool) -> Result<(), AutoConfigError> {
1599 if !graph {
1600 return Ok(());
1601 }
1602 if self.model.moe.is_some() {
1603 return self.invalid(
1604 "FERRUM_BATCHED_GRAPH",
1605 "legacy batched decode graph does not apply to MoE models",
1606 );
1607 }
1608 if !self.is_cuda_backend() {
1609 return self.invalid(
1610 "FERRUM_BATCHED_GRAPH",
1611 "legacy batched decode graph requires CUDA backend",
1612 );
1613 }
1614 if !self.hardware.graph_support {
1615 return self.invalid(
1616 "FERRUM_BATCHED_GRAPH",
1617 "hardware/backend does not support CUDA graph replay",
1618 );
1619 }
1620 if !self.hardware.compiled_features.cuda_graph {
1621 return self.invalid("FERRUM_BATCHED_GRAPH", "CUDA graph support is not compiled");
1622 }
1623 Ok(())
1624 }
1625
1626 fn validate_unified_graph(
1627 &self,
1628 graph: bool,
1629 layers_only: bool,
1630 lm_head_eager: bool,
1631 ) -> Result<(), AutoConfigError> {
1632 if layers_only && !graph {
1633 return self.invalid(
1634 "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY",
1635 "layers-only unified graph capture requires FERRUM_UNIFIED_GRAPH=1",
1636 );
1637 }
1638 if lm_head_eager && !graph {
1639 return self.invalid(
1640 "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
1641 "lm-head-eager unified graph capture requires FERRUM_UNIFIED_GRAPH=1",
1642 );
1643 }
1644 if layers_only && lm_head_eager {
1645 return self.invalid(
1646 "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
1647 "lm-head-eager unified graph capture conflicts with layers-only capture",
1648 );
1649 }
1650 if !graph {
1651 return Ok(());
1652 }
1653 if self.model.moe.is_some() {
1654 return self.invalid(
1655 "FERRUM_UNIFIED_GRAPH",
1656 "unified decode graph does not apply to MoE models",
1657 );
1658 }
1659 if self.model.architecture.eq_ignore_ascii_case("gemma3") && !layers_only && !lm_head_eager
1660 {
1661 return self.invalid(
1662 "FERRUM_UNIFIED_GRAPH",
1663 "full unified decode graph is disabled for Gemma3 sandwich-norm models",
1664 );
1665 }
1666 if !self.is_cuda_backend() {
1667 return self.invalid(
1668 "FERRUM_UNIFIED_GRAPH",
1669 "unified decode graph requires CUDA backend",
1670 );
1671 }
1672 if !self.hardware.graph_support {
1673 return self.invalid(
1674 "FERRUM_UNIFIED_GRAPH",
1675 "hardware/backend does not support CUDA graph replay",
1676 );
1677 }
1678 if !self.hardware.compiled_features.cuda_graph {
1679 return self.invalid("FERRUM_UNIFIED_GRAPH", "CUDA graph support is not compiled");
1680 }
1681 Ok(())
1682 }
1683
1684 fn validate_sampling(&self, greedy: bool) -> Result<(), AutoConfigError> {
1685 if greedy && !self.hardware.compiled_features.greedy_argmax {
1686 return self.invalid("FERRUM_GREEDY_ARGMAX", "GPU argmax is not compiled");
1687 }
1688 if greedy
1689 && !(self.is_cuda_backend() || self.hardware.backend.eq_ignore_ascii_case("metal"))
1690 {
1691 return self.invalid(
1692 "FERRUM_GREEDY_ARGMAX",
1693 "greedy argmax requires CUDA or Metal backend",
1694 );
1695 }
1696 Ok(())
1697 }
1698
1699 fn validate_memory(
1700 &self,
1701 kv_blocks: usize,
1702 max_sequences: usize,
1703 recurrent_state_max_slots: Option<usize>,
1704 max_batched_tokens: usize,
1705 requested_max_model_len: Option<usize>,
1706 ) -> Result<(), AutoConfigError> {
1707 if kv_blocks == 0 {
1708 return self.invalid("FERRUM_KV_MAX_BLOCKS", "must be greater than zero");
1709 }
1710 if max_sequences == 0 {
1711 return self.invalid("FERRUM_PAGED_MAX_SEQS", "must be greater than zero");
1712 }
1713 if recurrent_state_max_slots == Some(0) {
1714 return self.invalid(
1715 "FERRUM_RECURRENT_STATE_MAX_SLOTS",
1716 "must be greater than zero",
1717 );
1718 }
1719 if self.execution_resource_authority == ExecutionResourceAuthority::LegacyEngine {
1720 if let Some(limit) = self.recurrent_state_budget_max_slots() {
1721 let recurrent_slots = recurrent_state_max_slots.unwrap_or(max_sequences);
1722 if recurrent_slots > limit {
1723 let key = if self.entry("FERRUM_RECURRENT_STATE_MAX_SLOTS").is_some() {
1724 "FERRUM_RECURRENT_STATE_MAX_SLOTS"
1725 } else {
1726 "FERRUM_PAGED_MAX_SEQS"
1727 };
1728 return Err(AutoConfigError::InvalidOverride {
1729 key: key.to_string(),
1730 reason: format!(
1731 "recurrent-state slot pool exceeds the model/hardware memory budget: slots={recurrent_slots}, budget={limit}; use FERRUM_RECURRENT_STATE_MAX_SLOTS={limit}, lower --max-num-seqs, or a larger-memory GPU."
1732 ),
1733 });
1734 }
1735 }
1736 }
1737 if max_batched_tokens < max_sequences {
1738 return self.invalid(
1739 "FERRUM_MAX_BATCHED_TOKENS",
1740 "must be at least FERRUM_PAGED_MAX_SEQS",
1741 );
1742 }
1743 let kv_token_capacity = kv_blocks.saturating_mul(DEFAULT_KV_BLOCK_SIZE_TOKENS);
1744 if max_batched_tokens > kv_token_capacity {
1745 return self.invalid(
1746 "FERRUM_MAX_BATCHED_TOKENS",
1747 "exceeds KV cache token capacity",
1748 );
1749 }
1750 if let Some(max_model_len) = requested_max_model_len {
1751 if max_model_len == 0 {
1752 return self.invalid("FERRUM_MAX_MODEL_LEN", "must be greater than zero");
1753 }
1754 if let Some(model_max) = self.model.max_context_len {
1755 if max_model_len > model_max {
1756 return self.invalid(
1757 "FERRUM_MAX_MODEL_LEN",
1758 "exceeds model metadata max context length",
1759 );
1760 }
1761 }
1762 if max_model_len > kv_token_capacity {
1763 return self.invalid(
1764 "FERRUM_KV_MAX_BLOCKS",
1765 "KV cache token capacity is smaller than FERRUM_MAX_MODEL_LEN",
1766 );
1767 }
1768 }
1769 Ok(())
1770 }
1771
1772 fn validate_dtypes(&self) -> Result<(), AutoConfigError> {
1773 if let Some(dtype) = self.raw("FERRUM_DTYPE") {
1774 let dtype = dtype.to_ascii_lowercase();
1775 if !self.hardware.supported_dtypes.iter().any(|d| d == &dtype) {
1776 return self.invalid("FERRUM_DTYPE", "dtype is not supported by hardware profile");
1777 }
1778 }
1779 if let Some(dtype) = self.raw("FERRUM_KV_DTYPE") {
1780 let dtype = dtype.to_ascii_lowercase();
1781 if !self
1782 .hardware
1783 .supported_kv_dtypes
1784 .iter()
1785 .any(|d| d == &dtype)
1786 {
1787 return self.invalid(
1788 "FERRUM_KV_DTYPE",
1789 "KV dtype is not supported by hardware profile",
1790 );
1791 }
1792 }
1793 Ok(())
1794 }
1795
1796 fn validate_layer_split_pipeline_mode(&self) -> Result<(), AutoConfigError> {
1797 let Some(mode) = self.raw("FERRUM_LAYER_SPLIT_PIPELINE_MODE") else {
1798 return Ok(());
1799 };
1800 match mode.trim().to_ascii_lowercase().as_str() {
1801 "batch" | "overlapped" => Ok(()),
1802 _ => self.invalid(
1803 "FERRUM_LAYER_SPLIT_PIPELINE_MODE",
1804 "must be batch or overlapped",
1805 ),
1806 }
1807 }
1808
1809 fn resolve_plan_attention_policy(
1810 &self,
1811 ) -> Result<
1812 (
1813 ResolvedValue<AttentionExecutionPolicy>,
1814 ResolvedValue<AttentionExecutionPolicy>,
1815 ),
1816 AutoConfigError,
1817 > {
1818 let requested = match self.entry("FERRUM_ATTENTION_POLICY") {
1819 Some(entry) => ResolvedValue {
1820 value: AttentionExecutionPolicy::parse_runtime_value(&entry.effective_value)
1821 .map_err(|reason| AutoConfigError::InvalidOverride {
1822 key: "FERRUM_ATTENTION_POLICY".to_owned(),
1823 reason,
1824 })?,
1825 source: auto_config_source_from_runtime(entry.source),
1826 source_key: Some("FERRUM_ATTENTION_POLICY".to_owned()),
1827 },
1828 None => ResolvedValue {
1829 value: AttentionExecutionPolicy::Auto,
1830 source: AutoConfigSource::Default,
1831 source_key: None,
1832 },
1833 };
1834 let native_adaptive_supported =
1838 self.is_cuda_backend() && self.hardware.compiled_features.vllm_paged_attn;
1839 let compiled = requested
1840 .value
1841 .resolve(native_adaptive_supported)
1842 .map_err(|reason| AutoConfigError::UnsupportedCombination {
1843 selection: "attention_execution_policy".to_owned(),
1844 reason,
1845 })?;
1846 Ok((
1847 requested.clone(),
1848 ResolvedValue {
1849 value: compiled,
1850 source: if requested.value == AttentionExecutionPolicy::Auto {
1851 AutoConfigSource::CompiledFeature
1852 } else {
1853 requested.source
1854 },
1855 source_key: requested.source_key.clone(),
1856 },
1857 ))
1858 }
1859
1860 fn plan_attention_execution_decision(
1861 &self,
1862 compiled: &ResolvedValue<AttentionExecutionPolicy>,
1863 ) -> AutoConfigDecision {
1864 let selected = compiled.value.as_runtime_value();
1865 self.decision(
1866 "attention_execution_policy",
1867 selected,
1868 compiled.source,
1869 compiled.source_key.clone(),
1870 ["native-adaptive", "portable"],
1871 self.rejected_except(
1872 selected,
1873 [
1874 (
1875 "native-adaptive",
1876 "native adaptive provider is not selected or compiled",
1877 ),
1878 ("portable", "portable provider is not selected"),
1879 ],
1880 ),
1881 vec![
1882 RuntimeConfigEffect::Correctness,
1883 RuntimeConfigEffect::Performance,
1884 ],
1885 )
1886 }
1887
1888 fn plan_attention_phase_decision(
1889 &self,
1890 selection: &str,
1891 compiled: &ResolvedValue<AttentionExecutionPolicy>,
1892 ) -> AutoConfigDecision {
1893 let selected = match (
1894 self.hardware.backend.to_ascii_lowercase().as_str(),
1895 compiled.value,
1896 ) {
1897 ("cuda", AttentionExecutionPolicy::NativeAdaptive) => "cuda_native_adaptive",
1898 ("metal", AttentionExecutionPolicy::NativeAdaptive) => "metal_native_adaptive",
1899 (_, AttentionExecutionPolicy::Portable) => "portable",
1900 (_, AttentionExecutionPolicy::Auto) => "unresolved",
1901 _ => "portable",
1902 };
1903 self.decision(
1904 selection,
1905 selected,
1906 compiled.source,
1907 compiled.source_key.clone(),
1908 ["cuda_native_adaptive", "metal_native_adaptive", "portable"],
1909 self.rejected_except(
1910 selected,
1911 [
1912 (
1913 "cuda_native_adaptive",
1914 "CUDA native adaptive provider is not selected",
1915 ),
1916 (
1917 "metal_native_adaptive",
1918 "Metal native adaptive provider is not selected",
1919 ),
1920 ("portable", "portable provider is not selected"),
1921 ],
1922 ),
1923 vec![
1924 RuntimeConfigEffect::Correctness,
1925 RuntimeConfigEffect::Performance,
1926 ],
1927 )
1928 }
1929
1930 fn attention_prefill_decision(
1931 &self,
1932 use_vllm_paged_attn: ResolvedValue<bool>,
1933 fa_layout: ResolvedValue<bool>,
1934 fa2_source: ResolvedValue<bool>,
1935 fa2_direct_ffi: ResolvedValue<bool>,
1936 ) -> AutoConfigDecision {
1937 let (selected, source, source_key) = if fa2_source.value {
1938 ("fa2_source", fa2_source.source, fa2_source.source_key)
1939 } else if fa2_direct_ffi.value {
1940 (
1941 "fa2_direct_ffi",
1942 fa2_direct_ffi.source,
1943 fa2_direct_ffi.source_key,
1944 )
1945 } else if fa_layout.value {
1946 ("fa_layout_varlen", fa_layout.source, fa_layout.source_key)
1947 } else if use_vllm_paged_attn.value {
1948 (
1949 "vllm_paged_varlen",
1950 use_vllm_paged_attn.source,
1951 use_vllm_paged_attn.source_key,
1952 )
1953 } else {
1954 ("legacy_paged_varlen", AutoConfigSource::Default, None)
1955 };
1956 self.decision(
1957 "attention_prefill_mixed_backend",
1958 selected,
1959 source,
1960 source_key,
1961 [
1962 "fa2_source",
1963 "fa2_direct_ffi",
1964 "fa_layout_varlen",
1965 "vllm_paged_varlen",
1966 "legacy_paged_varlen",
1967 ],
1968 self.rejected_except(
1969 selected,
1970 [
1971 ("fa2_source", "source-linked FA2 path not selected"),
1972 ("fa2_direct_ffi", "diagnostic direct FFI shim not selected"),
1973 ("fa_layout_varlen", "FA-compatible layout not selected"),
1974 ("vllm_paged_varlen", "vLLM paged varlen bridge not selected"),
1975 (
1976 "legacy_paged_varlen",
1977 "a higher-priority attention path was selected",
1978 ),
1979 ],
1980 ),
1981 vec![
1982 RuntimeConfigEffect::Performance,
1983 RuntimeConfigEffect::Memory,
1984 ],
1985 )
1986 }
1987
1988 fn fa2_native_artifact_decision(
1989 &self,
1990 manifest: Option<&ResolvedValue<String>>,
1991 artifact: Option<&ResolvedValue<String>>,
1992 ) -> AutoConfigDecision {
1993 let configured = manifest.is_some() && artifact.is_some();
1994 let selected = if configured {
1995 "configured"
1996 } else {
1997 "not_configured"
1998 };
1999 let (source, source_key) = manifest
2000 .map(|manifest| (manifest.source, manifest.source_key.clone()))
2001 .unwrap_or((AutoConfigSource::Default, None));
2002 self.decision(
2003 "fa2_native_operator_artifact",
2004 selected,
2005 source,
2006 source_key,
2007 ["configured", "not_configured"],
2008 self.rejected_except(
2009 selected,
2010 [
2011 (
2012 "configured",
2013 "no typed FA2 native operator manifest/artifact was configured",
2014 ),
2015 (
2016 "not_configured",
2017 "typed FA2 native operator manifest/artifact is present",
2018 ),
2019 ],
2020 ),
2021 vec![
2022 RuntimeConfigEffect::Correctness,
2023 RuntimeConfigEffect::Performance,
2024 ],
2025 )
2026 }
2027
2028 fn fa2_native_runtime_selection_decision(
2029 &self,
2030 manifest: Option<&ResolvedValue<String>>,
2031 artifact: Option<&ResolvedValue<String>>,
2032 ) -> AutoConfigDecision {
2033 let configured = manifest.is_some() && artifact.is_some();
2034 let selected = if configured {
2035 "not_selected"
2036 } else {
2037 "not_configured"
2038 };
2039 let (source, source_key) = manifest
2040 .map(|manifest| (manifest.source, manifest.source_key.clone()))
2041 .unwrap_or((AutoConfigSource::Default, None));
2042 self.decision(
2043 "fa2_native_operator_runtime_selection",
2044 selected,
2045 source,
2046 source_key,
2047 ["selected", "not_selected", "not_configured"],
2048 self.rejected_except(
2049 selected,
2050 [
2051 (
2052 "selected",
2053 "FA2 native runtime dispatch requires artifact-selected actual model smoke and retention evidence",
2054 ),
2055 (
2056 "not_selected",
2057 "typed FA2 native operator manifest/artifact is present",
2058 ),
2059 (
2060 "not_configured",
2061 "no typed FA2 native operator manifest/artifact was configured",
2062 ),
2063 ],
2064 ),
2065 vec![
2066 RuntimeConfigEffect::Correctness,
2067 RuntimeConfigEffect::Performance,
2068 ],
2069 )
2070 }
2071
2072 fn attention_decode_decision(
2073 &self,
2074 use_vllm_paged_attn: ResolvedValue<bool>,
2075 vllm_v1_short: ResolvedValue<bool>,
2076 ) -> AutoConfigDecision {
2077 let (selected, source, source_key) = if use_vllm_paged_attn.value {
2078 if vllm_v1_short.value {
2079 (
2080 "vllm_paged_attn_v1_short",
2081 vllm_v1_short.source,
2082 vllm_v1_short.source_key,
2083 )
2084 } else {
2085 (
2086 "vllm_paged_attn_v2",
2087 vllm_v1_short.source,
2088 vllm_v1_short.source_key,
2089 )
2090 }
2091 } else {
2092 ("legacy_paged_decode", use_vllm_paged_attn.source, None)
2093 };
2094 self.decision(
2095 "attention_decode_backend",
2096 selected,
2097 source,
2098 source_key,
2099 [
2100 "vllm_paged_attn_v1_short",
2101 "vllm_paged_attn_v2",
2102 "legacy_paged_decode",
2103 ],
2104 self.rejected_except(
2105 selected,
2106 [
2107 (
2108 "vllm_paged_attn_v1_short",
2109 "short-context v1 decode not selected",
2110 ),
2111 ("vllm_paged_attn_v2", "v2 decode not selected"),
2112 ("legacy_paged_decode", "legacy decode not selected"),
2113 ],
2114 ),
2115 vec![RuntimeConfigEffect::Performance],
2116 )
2117 }
2118
2119 fn moe_decision(
2120 &self,
2121 vllm_moe: ResolvedValue<bool>,
2122 device_route: ResolvedValue<bool>,
2123 pair_ids: ResolvedValue<bool>,
2124 ) -> AutoConfigDecision {
2125 let selected = if vllm_moe.value && device_route.value && pair_ids.value {
2126 "vllm_marlin_moe_device_route_pair_ids"
2127 } else if vllm_moe.value && device_route.value {
2128 "vllm_marlin_moe_device_route"
2129 } else if vllm_moe.value {
2130 "vllm_marlin_moe"
2131 } else {
2132 "legacy_moe"
2133 };
2134 self.decision(
2135 "moe_implementation",
2136 selected,
2137 vllm_moe.source,
2138 vllm_moe.source_key,
2139 [
2140 "vllm_marlin_moe_device_route_pair_ids",
2141 "vllm_marlin_moe_device_route",
2142 "vllm_marlin_moe",
2143 "legacy_moe",
2144 ],
2145 self.rejected_except(
2146 selected,
2147 [
2148 (
2149 "vllm_marlin_moe_device_route_pair_ids",
2150 "pair-id device route not selected",
2151 ),
2152 (
2153 "vllm_marlin_moe_device_route",
2154 "device-route MoE not selected",
2155 ),
2156 ("vllm_marlin_moe", "vLLM Marlin MoE not selected"),
2157 ("legacy_moe", "legacy MoE not selected"),
2158 ],
2159 ),
2160 vec![RuntimeConfigEffect::Performance],
2161 )
2162 }
2163
2164 fn graph_decision(&self, graph: ResolvedValue<bool>) -> AutoConfigDecision {
2165 let selected = if graph.value {
2166 "graph_clean_decode"
2167 } else {
2168 "graph_disabled"
2169 };
2170 self.decision(
2171 "moe_graph_policy",
2172 selected,
2173 graph.source,
2174 graph.source_key,
2175 ["graph_clean_decode", "graph_disabled"],
2176 self.rejected_except(
2177 selected,
2178 [
2179 ("graph_clean_decode", "graph decode not selected"),
2180 ("graph_disabled", "graph decode selected"),
2181 ],
2182 ),
2183 vec![
2184 RuntimeConfigEffect::Performance,
2185 RuntimeConfigEffect::Correctness,
2186 ],
2187 )
2188 }
2189
2190 fn decode_graph_decision(
2191 &self,
2192 batched_graph: ResolvedValue<bool>,
2193 unified_graph: ResolvedValue<bool>,
2194 unified_graph_layers_only: ResolvedValue<bool>,
2195 unified_graph_lm_head_eager: ResolvedValue<bool>,
2196 ) -> AutoConfigDecision {
2197 let selected = if unified_graph.value && unified_graph_layers_only.value {
2198 "unified_decode_graph_layers_only"
2199 } else if unified_graph.value && unified_graph_lm_head_eager.value {
2200 "unified_decode_graph_lm_head_eager"
2201 } else if unified_graph.value {
2202 "unified_decode_graph"
2203 } else if batched_graph.value {
2204 "legacy_batched_decode_graph"
2205 } else {
2206 "graph_disabled"
2207 };
2208 let source_value = if unified_graph_layers_only.value {
2209 unified_graph_layers_only
2210 } else if unified_graph_lm_head_eager.value {
2211 unified_graph_lm_head_eager
2212 } else if unified_graph.value
2213 || (!batched_graph.value && unified_graph.source != AutoConfigSource::Default)
2214 {
2215 unified_graph
2216 } else {
2217 batched_graph
2218 };
2219 self.decision(
2220 "decode_graph_policy",
2221 selected,
2222 source_value.source,
2223 source_value.source_key,
2224 [
2225 "unified_decode_graph_layers_only",
2226 "unified_decode_graph_lm_head_eager",
2227 "unified_decode_graph",
2228 "legacy_batched_decode_graph",
2229 "graph_disabled",
2230 ],
2231 self.rejected_except(
2232 selected,
2233 [
2234 (
2235 "unified_decode_graph_layers_only",
2236 "layers-only unified decode graph not selected",
2237 ),
2238 (
2239 "unified_decode_graph_lm_head_eager",
2240 "lm-head-eager unified decode graph not selected",
2241 ),
2242 ("unified_decode_graph", "unified decode graph not selected"),
2243 (
2244 "legacy_batched_decode_graph",
2245 "legacy batched decode graph not selected",
2246 ),
2247 ("graph_disabled", "decode graph selected"),
2248 ],
2249 ),
2250 vec![
2251 RuntimeConfigEffect::Performance,
2252 RuntimeConfigEffect::Correctness,
2253 ],
2254 )
2255 }
2256
2257 fn reusable_execution_decision(
2258 &self,
2259 reusable_execution: ResolvedValue<bool>,
2260 ) -> AutoConfigDecision {
2261 let selected = if reusable_execution.value {
2262 "enabled_when_runtime_capable"
2263 } else {
2264 "disabled"
2265 };
2266 self.decision(
2267 "reusable_execution_policy",
2268 selected,
2269 reusable_execution.source,
2270 reusable_execution.source_key,
2271 ["enabled_when_runtime_capable", "disabled"],
2272 self.rejected_except(
2273 selected,
2274 [
2275 (
2276 "enabled_when_runtime_capable",
2277 "reusable device programs are disabled",
2278 ),
2279 ("disabled", "reusable device programs are enabled"),
2280 ],
2281 ),
2282 vec![
2283 RuntimeConfigEffect::Performance,
2284 RuntimeConfigEffect::Correctness,
2285 ],
2286 )
2287 }
2288
2289 fn scalar_decision(
2290 &self,
2291 selection: &str,
2292 value: ResolvedValue<usize>,
2293 effect: RuntimeConfigEffect,
2294 ) -> AutoConfigDecision {
2295 self.decision(
2296 selection,
2297 &value.value.to_string(),
2298 value.source,
2299 value.source_key,
2300 [value.value.to_string()],
2301 Vec::new(),
2302 vec![effect],
2303 )
2304 }
2305
2306 fn scheduler_decision(
2307 &self,
2308 default_prefill_first_until_active: Option<ResolvedValue<usize>>,
2309 ) -> Result<AutoConfigDecision, AutoConfigError> {
2310 let entries = self.entries();
2311 let prompt_scheduler =
2312 || -> Result<(String, AutoConfigSource, Option<String>), AutoConfigError> {
2313 let prompt_token_estimate = self.bool_value(
2314 "FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE",
2315 true,
2316 AutoConfigSource::Default,
2317 )?;
2318 let selected = if prompt_token_estimate.value {
2319 "prompt_token_estimate"
2320 } else {
2321 "continuous_default"
2322 };
2323 Ok((
2324 selected.to_string(),
2325 prompt_token_estimate.source,
2326 prompt_token_estimate.source_key,
2327 ))
2328 };
2329 let explicit_prefill_first = entries.get("FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE");
2330 let explicit_prefill_first_present = explicit_prefill_first.is_some();
2331 let implicit_prefill_first = if explicit_prefill_first.is_none()
2332 && !entries.contains_key("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE")
2333 {
2334 default_prefill_first_until_active
2335 .as_ref()
2336 .map(|until| until.value.to_string())
2337 } else {
2338 None
2339 };
2340 let prefill_first = explicit_prefill_first
2341 .copied()
2342 .or(implicit_prefill_first.as_deref());
2343 let active_decode_chunk = entries.get("FERRUM_ACTIVE_DECODE_PREFILL_CHUNK");
2344 if let Some(chunk) = active_decode_chunk {
2345 let chunk_value = parse_usize_env_value(chunk).map_err(|reason| {
2346 AutoConfigError::InvalidOverride {
2347 key: "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK".to_string(),
2348 reason,
2349 }
2350 })?;
2351 self.unsupported_if(
2352 chunk_value == 0,
2353 "scheduler_admission_policy",
2354 "active decode prefill chunk must be greater than zero",
2355 )?;
2356 }
2357 let explicit_prefill_step_chunk = entries.get("FERRUM_SCHED_PREFILL_STEP_CHUNK");
2358 let explicit_prefill_step_chunk_present = explicit_prefill_step_chunk.is_some();
2359 let prefill_step_chunk = explicit_prefill_step_chunk.copied();
2360 if let Some(chunk) = prefill_step_chunk {
2361 let chunk_value = parse_usize_env_value(chunk).map_err(|reason| {
2362 AutoConfigError::InvalidOverride {
2363 key: "FERRUM_SCHED_PREFILL_STEP_CHUNK".to_string(),
2364 reason,
2365 }
2366 })?;
2367 self.unsupported_if(
2368 chunk_value == 0,
2369 "scheduler_admission_policy",
2370 "scheduler prefill step chunk must be greater than zero",
2371 )?;
2372 }
2373 let (mut selected, mut source, mut source_key) = if let (Some(until), Some(chunk)) =
2374 (prefill_first, active_decode_chunk)
2375 {
2376 parse_usize_env_value(until).map_err(|reason| AutoConfigError::InvalidOverride {
2377 key: "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE".to_string(),
2378 reason,
2379 })?;
2380 parse_usize_env_value(chunk).map_err(|reason| AutoConfigError::InvalidOverride {
2381 key: "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK".to_string(),
2382 reason,
2383 })?;
2384 let key = "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK";
2385 (
2386 format!("prefill_first_until_active:{until}+active_decode_prefill_chunk:{chunk}"),
2387 self.source_for_key(key, AutoConfigSource::Default),
2388 Some(key.to_string()),
2389 )
2390 } else if let Some(chunk) = active_decode_chunk {
2391 parse_usize_env_value(chunk).map_err(|reason| AutoConfigError::InvalidOverride {
2392 key: "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK".to_string(),
2393 reason,
2394 })?;
2395 let key = "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK";
2396 (
2397 format!("active_decode_prefill_chunk:{chunk}"),
2398 self.source_for_key(key, AutoConfigSource::Default),
2399 Some(key.to_string()),
2400 )
2401 } else if let Some(until) = prefill_first {
2402 parse_usize_env_value(until).map_err(|reason| AutoConfigError::InvalidOverride {
2403 key: "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE".to_string(),
2404 reason,
2405 })?;
2406 let key = "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE";
2407 let (source, source_key) = if explicit_prefill_first_present {
2408 (
2409 self.source_for_key(key, AutoConfigSource::Default),
2410 Some(key.to_string()),
2411 )
2412 } else if let Some(default) = default_prefill_first_until_active.as_ref() {
2413 (default.source, default.source_key.clone())
2414 } else {
2415 (AutoConfigSource::Default, None)
2416 };
2417 (
2418 format!("prefill_first_until_active:{until}"),
2419 source,
2420 source_key,
2421 )
2422 } else if !entries.contains_key("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE") {
2423 if let Some(until) = default_prefill_first_until_active.as_ref() {
2424 (
2425 format!("prefill_first_until_active:{}", until.value),
2426 until.source,
2427 until.source_key.clone(),
2428 )
2429 } else {
2430 prompt_scheduler()?
2431 }
2432 } else {
2433 prompt_scheduler()?
2434 };
2435 if let Some(chunk) = prefill_step_chunk {
2436 selected.push_str(&format!("+prefill_step_chunk:{chunk}"));
2437 if explicit_prefill_step_chunk_present && source_key.is_none() {
2438 let key = "FERRUM_SCHED_PREFILL_STEP_CHUNK";
2439 source = self.source_for_key(key, AutoConfigSource::Default);
2440 source_key = Some(key.to_string());
2441 }
2442 } else if default_prefill_first_until_active.is_some()
2443 && !entries.contains_key("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE")
2444 {
2445 selected.push_str("+prefill_token_budget:elastic");
2446 }
2447 Ok(self.decision(
2448 "scheduler_admission_policy",
2449 &selected,
2450 source,
2451 source_key,
2452 [
2453 "continuous_default",
2454 "prompt_token_estimate",
2455 "prefill_first_until_active",
2456 "prefill_first_until_active+active_decode_prefill_chunk",
2457 "active_decode_prefill_chunk",
2458 "prefill_step_chunk",
2459 "prefill_token_budget:elastic",
2460 ],
2461 Vec::new(),
2462 vec![RuntimeConfigEffect::Performance],
2463 ))
2464 }
2465
2466 fn prefix_cache_decision(&self, prefix_cache: ResolvedValue<bool>) -> AutoConfigDecision {
2467 let selected = if prefix_cache.value {
2468 "prefix_cache_enabled"
2469 } else {
2470 "prefix_cache_disabled"
2471 };
2472 self.decision(
2473 "prefix_cache_policy",
2474 selected,
2475 prefix_cache.source,
2476 prefix_cache.source_key,
2477 ["prefix_cache_enabled", "prefix_cache_disabled"],
2478 self.rejected_except(
2479 selected,
2480 [
2481 ("prefix_cache_enabled", "prefix cache not selected"),
2482 ("prefix_cache_disabled", "prefix cache enabled"),
2483 ],
2484 ),
2485 vec![
2486 RuntimeConfigEffect::Correctness,
2487 RuntimeConfigEffect::Performance,
2488 RuntimeConfigEffect::Memory,
2489 ],
2490 )
2491 }
2492
2493 fn sampling_decision(&self, greedy: ResolvedValue<bool>) -> AutoConfigDecision {
2494 let selected = if greedy.value {
2495 "gpu_greedy_argmax"
2496 } else {
2497 "logits_readback"
2498 };
2499 self.decision(
2500 "sampling_readback_path",
2501 selected,
2502 greedy.source,
2503 greedy.source_key,
2504 ["gpu_greedy_argmax", "logits_readback"],
2505 self.rejected_except(
2506 selected,
2507 [
2508 ("gpu_greedy_argmax", "GPU argmax not selected"),
2509 ("logits_readback", "logits readback not selected"),
2510 ],
2511 ),
2512 vec![
2513 RuntimeConfigEffect::Performance,
2514 RuntimeConfigEffect::Correctness,
2515 ],
2516 )
2517 }
2518
2519 fn decision<I, C>(
2520 &self,
2521 selection: &str,
2522 selected: &str,
2523 source: AutoConfigSource,
2524 source_key: Option<String>,
2525 candidates: I,
2526 rejected: Vec<RejectedCandidate>,
2527 affects: Vec<RuntimeConfigEffect>,
2528 ) -> AutoConfigDecision
2529 where
2530 I: IntoIterator<Item = C>,
2531 C: Into<String>,
2532 {
2533 AutoConfigDecision {
2534 schema_version: 1,
2535 selection: selection.to_string(),
2536 selected: selected.to_string(),
2537 source,
2538 source_key,
2539 candidates: candidates.into_iter().map(Into::into).collect(),
2540 rejected,
2541 affects,
2542 }
2543 }
2544
2545 fn rejected_except<I>(&self, selected: &str, candidates: I) -> Vec<RejectedCandidate>
2546 where
2547 I: IntoIterator<Item = (&'static str, &'static str)>,
2548 {
2549 candidates
2550 .into_iter()
2551 .filter(|(value, _)| *value != selected)
2552 .map(|(value, reason)| RejectedCandidate {
2553 value: value.to_string(),
2554 reason: reason.to_string(),
2555 })
2556 .collect()
2557 }
2558
2559 fn invalid<T>(&self, key: &str, reason: &str) -> Result<T, AutoConfigError> {
2560 Err(AutoConfigError::InvalidOverride {
2561 key: key.to_string(),
2562 reason: reason.to_string(),
2563 })
2564 }
2565
2566 fn unsupported<T>(&self, selection: &str, reason: &str) -> Result<T, AutoConfigError> {
2567 Err(AutoConfigError::UnsupportedCombination {
2568 selection: selection.to_string(),
2569 reason: reason.to_string(),
2570 })
2571 }
2572
2573 fn unsupported_if(
2574 &self,
2575 condition: bool,
2576 selection: &str,
2577 reason: &str,
2578 ) -> Result<(), AutoConfigError> {
2579 if condition {
2580 self.unsupported(selection, reason)
2581 } else {
2582 Ok(())
2583 }
2584 }
2585}
2586
2587fn kv_cache_bytes_per_token_for_model(model: &ModelCapabilities) -> Option<u64> {
2588 let layers = model.num_hidden_layers? as u64;
2589 let kv_heads = model.kv_heads? as u64;
2590 let head_dim = model.head_dim? as u64;
2591 layers
2592 .checked_mul(2)?
2593 .checked_mul(kv_heads)?
2594 .checked_mul(head_dim)?
2595 .checked_mul(2)
2596}
2597
2598fn qwen_moe_architecture_uses_vllm_paged_attn(architecture: &str) -> bool {
2599 architecture.eq_ignore_ascii_case("qwen3_moe")
2600 || architecture.eq_ignore_ascii_case("qwen3_5_moe")
2601}
2602
2603#[derive(Debug, Clone, PartialEq, Eq)]
2604struct ResolvedValue<T> {
2605 value: T,
2606 source: AutoConfigSource,
2607 source_key: Option<String>,
2608}
2609
2610#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2611struct RecurrentStateBudget {
2612 remaining_bytes: u64,
2613 bytes_per_sequence: u64,
2614 raw_slots: usize,
2615 floored_slots: usize,
2616}
2617
2618fn recurrent_state_budget_for(
2619 model: &ModelCapabilities,
2620 hardware: &HardwareCapabilities,
2621) -> Option<RecurrentStateBudget> {
2622 if !(hardware.backend.eq_ignore_ascii_case("cuda")
2623 || hardware.backend.eq_ignore_ascii_case("metal"))
2624 {
2625 return None;
2626 }
2627 let bytes_per_sequence = model.recurrent_state_bytes_per_sequence?.max(1);
2628 let vram_bytes = hardware.vram_bytes?;
2629 let weight_bytes = model.estimated_weight_bytes?;
2630 let remaining = vram_bytes.saturating_sub(weight_bytes);
2631 let raw_slots = (remaining / bytes_per_sequence) as usize;
2632 Some(RecurrentStateBudget {
2633 remaining_bytes: remaining,
2634 bytes_per_sequence,
2635 raw_slots,
2636 floored_slots: floor_power_of_two(raw_slots.max(1)),
2637 })
2638}
2639
2640fn parse_compute_capability(value: &str) -> Option<(u32, u32)> {
2641 let value = value.trim();
2642 if value.is_empty() {
2643 return None;
2644 }
2645 let (major, minor) = value.split_once('.').unwrap_or((value, "0"));
2646 Some((major.trim().parse().ok()?, minor.trim().parse().ok()?))
2647}
2648
2649fn vram_default_max_sequences(vram_bytes: u64) -> usize {
2650 match vram_bytes {
2651 bytes if bytes >= 20 * GIB => 32,
2652 bytes if bytes >= 12 * GIB => 16,
2653 bytes if bytes >= 8 * GIB => 8,
2654 _ => 4,
2655 }
2656}
2657
2658fn default_gpu_devices_for_backend(backend: &str) -> Option<Vec<usize>> {
2659 backend.eq_ignore_ascii_case("cuda").then(|| vec![0])
2660}
2661
2662fn ceil_div(value: usize, divisor: usize) -> usize {
2663 value.div_ceil(divisor)
2664}
2665
2666fn effective_admission_limit(
2667 authority: ExecutionResourceAuthority,
2668 max_sequences: Option<usize>,
2669 recurrent_state_max_slots: Option<usize>,
2670) -> Option<usize> {
2671 if authority == ExecutionResourceAuthority::PlanRuntime {
2672 return max_sequences;
2673 }
2674 match (max_sequences, recurrent_state_max_slots) {
2675 (Some(max_sequences), Some(recurrent_slots)) => Some(max_sequences.min(recurrent_slots)),
2676 (Some(max_sequences), None) => Some(max_sequences),
2677 (None, Some(recurrent_slots)) => Some(recurrent_slots),
2678 (None, None) => None,
2679 }
2680}
2681
2682fn floor_power_of_two(value: usize) -> usize {
2683 if value <= 1 {
2684 return 1;
2685 }
2686 1usize << (usize::BITS - 1 - value.leading_zeros())
2687}
2688
2689fn auto_config_source_from_runtime(source: RuntimeConfigSource) -> AutoConfigSource {
2690 match source {
2691 RuntimeConfigSource::Default => AutoConfigSource::Default,
2692 RuntimeConfigSource::ConfigFile => AutoConfigSource::ConfigFile,
2693 RuntimeConfigSource::Cli => AutoConfigSource::Cli,
2694 RuntimeConfigSource::Env => AutoConfigSource::Env,
2695 RuntimeConfigSource::ScriptCase => AutoConfigSource::ScriptCase,
2696 RuntimeConfigSource::MemoryProfile => AutoConfigSource::MemoryProfile,
2697 }
2698}
2699
2700#[cfg(test)]
2701mod tests {
2702 use super::*;
2703
2704 fn snapshot(vars: &[(&str, &str)]) -> RuntimeConfigSnapshot {
2705 RuntimeConfigSnapshot::from_env_vars(vars.iter().copied())
2706 }
2707
2708 fn snapshot_with_sources(vars: &[(&str, &str, RuntimeConfigSource)]) -> RuntimeConfigSnapshot {
2709 let mut entries: Vec<_> = vars
2710 .iter()
2711 .map(|(key, effective_value, source)| RuntimeConfigEntry {
2712 key: (*key).to_string(),
2713 effective_value: (*effective_value).to_string(),
2714 source: *source,
2715 affects: vec![RuntimeConfigEffect::Performance],
2716 })
2717 .collect();
2718 entries.sort_by(|a, b| a.key.cmp(&b.key));
2719 RuntimeConfigSnapshot { entries }
2720 }
2721
2722 fn m3(vars: &[(&str, &str)], features: CompiledKernelFeatures) -> FerrumConfigBuilder {
2723 FerrumConfigBuilder::new(snapshot(vars))
2724 .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
2725 .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(features))
2726 .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
2727 }
2728
2729 fn m3_with_hardware(
2730 vars: &[(&str, &str)],
2731 hardware: HardwareCapabilities,
2732 ) -> FerrumConfigBuilder {
2733 FerrumConfigBuilder::new(snapshot(vars))
2734 .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
2735 .with_hardware_capabilities(hardware)
2736 .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
2737 }
2738
2739 fn qwen35_moe_gptq_int4_model() -> ModelCapabilities {
2740 let mut model = ModelCapabilities::qwen3_30b_a3b_gptq_int4();
2741 model.architecture = "qwen3_5_moe".to_string();
2742 model.head_dim = Some(256);
2743 model.num_hidden_layers = Some(40);
2744 model.kv_heads = Some(8);
2745 model.estimated_weight_bytes = Some(24_419_939_760);
2746 model.recurrent_state_bytes_per_sequence = Some(32_931_840);
2747 model
2748 }
2749
2750 fn synthetic_tight_recurrent_state_model() -> ModelCapabilities {
2751 ModelCapabilities {
2752 architecture: "synthetic_recurrent_state".to_string(),
2753 quantization: None,
2754 moe: None,
2755 max_context_len: Some(262_144),
2756 num_hidden_layers: Some(40),
2757 head_dim: Some(256),
2758 kv_heads: Some(8),
2759 estimated_weight_bytes: Some(24_419_939_760),
2760 recurrent_state_bytes_per_sequence: Some(65_863_680),
2761 supported_dtypes: vec!["fp16".to_string()],
2762 graph_safe_moe: false,
2763 }
2764 }
2765
2766 fn qwen25_layer_split_runtime_entries(source: RuntimeConfigSource) -> RuntimeConfigSnapshot {
2767 snapshot_with_sources(&[
2768 ("FERRUM_REQUESTED_GPU_DEVICES", "0,1", source),
2769 ("FERRUM_SELECTED_GPU_DEVICES", "0,1", source),
2770 ("FERRUM_CUDA_DEVICE_COUNT", "2", source),
2771 (
2772 "FERRUM_SELECTED_DISTRIBUTED_STRATEGY",
2773 "layer_split",
2774 source,
2775 ),
2776 (
2777 "FERRUM_SELECTED_LAYER_SPLIT_PLAN",
2778 "stage0:cuda:0:layers=0-39;stage1:cuda:1:layers=40-79",
2779 source,
2780 ),
2781 ("FERRUM_LAYER_SPLIT_PIPELINE_MODE", "batch", source),
2782 ("FERRUM_MAX_MODEL_LEN", "4096", source),
2783 ("FERRUM_KV_MAX_BLOCKS", "1024", source),
2784 ("FERRUM_KV_CAPACITY", "1024", source),
2785 ("FERRUM_PAGED_MAX_SEQS", "16", source),
2786 ("FERRUM_MAX_BATCHED_TOKENS", "1536", source),
2787 ("FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE", "16", source),
2788 ])
2789 }
2790
2791 fn gemma3_gptq_model() -> ModelCapabilities {
2792 ModelCapabilities {
2793 architecture: "gemma3".to_string(),
2794 quantization: Some("gptq_int4".to_string()),
2795 moe: None,
2796 max_context_len: Some(131_072),
2797 num_hidden_layers: Some(62),
2798 head_dim: Some(256),
2799 kv_heads: Some(16),
2800 estimated_weight_bytes: Some(15 * GIB),
2801 recurrent_state_bytes_per_sequence: None,
2802 supported_dtypes: vec!["fp16".to_string()],
2803 graph_safe_moe: false,
2804 }
2805 }
2806
2807 fn expect_invalid_key(vars: &[(&str, &str)], key: &str) {
2808 expect_invalid_key_with_features(
2809 vars,
2810 key,
2811 CompiledKernelFeatures::m3_fast_path_without_fa2(),
2812 );
2813 }
2814
2815 fn expect_invalid_key_with_features(
2816 vars: &[(&str, &str)],
2817 key: &str,
2818 features: CompiledKernelFeatures,
2819 ) {
2820 expect_invalid_key_with_hardware(vars, key, HardwareCapabilities::rtx4090_cuda(features));
2821 }
2822
2823 fn expect_invalid_key_with_hardware(
2824 vars: &[(&str, &str)],
2825 key: &str,
2826 hardware: HardwareCapabilities,
2827 ) {
2828 let err = m3_with_hardware(vars, hardware)
2829 .resolve()
2830 .expect_err("override should fail");
2831 match err {
2832 AutoConfigError::InvalidOverride { key: actual, .. } => assert_eq!(actual, key),
2833 other => panic!("expected invalid override for {key}, got {other:?}"),
2834 }
2835 }
2836
2837 fn cpu_hardware_with_features(features: CompiledKernelFeatures) -> HardwareCapabilities {
2838 HardwareCapabilities {
2839 backend: "cpu".to_string(),
2840 supported_dtypes: vec!["fp32".to_string()],
2841 supported_kv_dtypes: vec!["fp16".to_string()],
2842 compiled_features: features,
2843 ..HardwareCapabilities::unknown()
2844 }
2845 }
2846
2847 #[test]
2848 fn m3_preset_selects_current_safe_fast_path_without_fa2() {
2849 let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
2850 .resolve()
2851 .unwrap();
2852 let decisions: BTreeMap<_, _> = resolved
2853 .decisions
2854 .iter()
2855 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
2856 .collect();
2857 assert_eq!(
2858 decisions["attention_prefill_mixed_backend"],
2859 "vllm_paged_varlen"
2860 );
2861 assert_eq!(
2862 decisions["attention_decode_backend"],
2863 "vllm_paged_attn_v1_short"
2864 );
2865 assert_eq!(
2866 decisions["moe_implementation"],
2867 "vllm_marlin_moe_device_route_pair_ids"
2868 );
2869 assert_eq!(decisions["moe_graph_policy"], "graph_disabled");
2870 assert_eq!(decisions["decode_graph_policy"], "graph_disabled");
2871 assert_eq!(decisions["prefix_cache_policy"], "prefix_cache_disabled");
2872 assert_eq!(decisions["sampling_readback_path"], "gpu_greedy_argmax");
2873 assert_eq!(
2874 resolved.preset.as_deref(),
2875 Some(M3_QWEN3_30B_A3B_INT4_PRESET)
2876 );
2877 }
2878
2879 #[test]
2880 fn cuda_gptq_moe_enables_vllm_marlin_without_m3_preset() {
2881 let hardware =
2887 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
2888 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
2889 let resolved = FerrumConfigBuilder::new(snapshot(&[]))
2890 .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
2891 .with_hardware_capabilities(hardware)
2892 .with_workload_profile(workload)
2893 .resolve()
2894 .unwrap();
2895 let decisions: BTreeMap<_, _> = resolved
2896 .decisions
2897 .iter()
2898 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
2899 .collect();
2900 assert_ne!(
2901 resolved.preset.as_deref(),
2902 Some(M3_QWEN3_30B_A3B_INT4_PRESET),
2903 "serving-default workload must not be the m3 preset"
2904 );
2905 assert_eq!(
2906 decisions["moe_implementation"], "vllm_marlin_moe_device_route_pair_ids",
2907 "CUDA GPTQ MoE should get the fast vLLM-Marlin path without the m3 preset"
2908 );
2909 let entry = resolved
2913 .runtime_config
2914 .entries
2915 .iter()
2916 .find(|e| e.key == "FERRUM_VLLM_MOE");
2917 assert_eq!(
2918 entry.map(|e| e.effective_value.as_str()),
2919 Some("1"),
2920 "resolved FERRUM_VLLM_MOE must be materialized into the effective config"
2921 );
2922 }
2923
2924 #[test]
2925 fn cuda_qwen3_moe_enables_vllm_paged_attn_without_m3_preset() {
2926 let hardware =
2932 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
2933 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
2934 let resolved = FerrumConfigBuilder::new(snapshot(&[]))
2935 .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
2936 .with_hardware_capabilities(hardware)
2937 .with_workload_profile(workload)
2938 .resolve()
2939 .unwrap();
2940 let decisions: BTreeMap<_, _> = resolved
2941 .decisions
2942 .iter()
2943 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
2944 .collect();
2945 assert_eq!(
2946 decisions["attention_decode_backend"], "vllm_paged_attn_v1_short",
2947 "CUDA Qwen3-MoE should get VPA decode without the m3 preset"
2948 );
2949 let entry = |key: &str| {
2950 resolved
2951 .runtime_config
2952 .entries
2953 .iter()
2954 .find(|entry| entry.key == key)
2955 .unwrap_or_else(|| panic!("missing runtime config entry {key}"))
2956 };
2957 assert_eq!(entry("FERRUM_USE_VLLM_PAGED_ATTN").effective_value, "1");
2958 assert_eq!(
2959 entry("FERRUM_VLLM_PAGED_ATTN_V1_SHORT").effective_value,
2960 "1"
2961 );
2962 }
2963
2964 #[test]
2965 fn cuda_qwen35_moe_enables_vllm_paged_attn_v2_without_m3_preset() {
2966 let mut hardware =
2971 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
2972 hardware.vram_bytes = Some(48 * GIB);
2973 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
2974 let model = qwen35_moe_gptq_int4_model();
2975 let resolved = FerrumConfigBuilder::new(snapshot(&[]))
2976 .with_model_capabilities(model)
2977 .with_hardware_capabilities(hardware)
2978 .with_workload_profile(workload)
2979 .resolve()
2980 .unwrap();
2981 let decisions: BTreeMap<_, _> = resolved
2982 .decisions
2983 .iter()
2984 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
2985 .collect();
2986 assert_eq!(
2987 decisions["attention_prefill_mixed_backend"],
2988 "vllm_paged_varlen"
2989 );
2990 assert_eq!(decisions["attention_decode_backend"], "vllm_paged_attn_v2");
2991 let entry = |key: &str| {
2992 resolved
2993 .runtime_config
2994 .entries
2995 .iter()
2996 .find(|entry| entry.key == key)
2997 .unwrap_or_else(|| panic!("missing runtime config entry {key}"))
2998 };
2999 assert_eq!(entry("FERRUM_USE_VLLM_PAGED_ATTN").effective_value, "1");
3000 assert_eq!(
3001 entry("FERRUM_VLLM_PAGED_ATTN_V1_SHORT").effective_value,
3002 "0"
3003 );
3004 }
3005
3006 #[test]
3007 fn recurrent_state_budget_caps_default_slots_without_model_vram_special_case() {
3008 let hardware =
3009 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3010 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3011 let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_PAGED_MAX_SEQS", "32")]))
3012 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3013 .with_hardware_capabilities(hardware)
3014 .with_workload_profile(workload)
3015 .resolve()
3016 .unwrap();
3017 let decision = |selection: &str| {
3018 resolved
3019 .decisions
3020 .iter()
3021 .find(|decision| decision.selection == selection)
3022 .unwrap_or_else(|| panic!("missing decision {selection}"))
3023 };
3024
3025 assert_eq!(decision("max_sequences").selected, "32");
3026 assert_eq!(decision("recurrent_state_max_slots").selected, "16");
3027 assert_eq!(
3028 decision("recurrent_state_max_slots").source,
3029 AutoConfigSource::MemoryProfile
3030 );
3031 let entry = resolved
3032 .runtime_config
3033 .entries
3034 .iter()
3035 .find(|entry| entry.key == "FERRUM_RECURRENT_STATE_MAX_SLOTS")
3036 .expect("memory-profile recurrent slot cap should reach effective runtime config");
3037 assert_eq!(entry.effective_value, "16");
3038 assert_eq!(entry.source, RuntimeConfigSource::MemoryProfile);
3039 let doc = resolved.effective_config_document();
3040 assert_eq!(doc["selected_max_sequences"], serde_json::json!(32));
3041 assert_eq!(
3042 doc["selected_recurrent_state_max_slots"],
3043 serde_json::json!(16)
3044 );
3045 assert_eq!(doc["selected_admission_limit"], serde_json::json!(16));
3046 assert_eq!(
3047 doc["admission"]["effective_max_concurrent"],
3048 serde_json::json!(16)
3049 );
3050 assert_eq!(
3051 doc["admission"]["recurrent_state_max_slots"],
3052 serde_json::json!(16)
3053 );
3054 assert_eq!(
3055 doc["admission"]["memory_estimate"]["recurrent_state_bytes_per_sequence"],
3056 serde_json::json!(65_863_680u64)
3057 );
3058 assert_eq!(
3059 doc["admission"]["memory_estimate"]["recurrent_state_budget_bytes"],
3060 serde_json::json!(24u64 * GIB - 24_419_939_760u64)
3061 );
3062 assert_eq!(
3063 doc["admission"]["memory_estimate"]["recurrent_state_budget_raw_slots"],
3064 serde_json::json!(20)
3065 );
3066 assert_eq!(
3067 doc["admission"]["memory_estimate"]["recurrent_state_budget_max_slots"],
3068 serde_json::json!(16)
3069 );
3070 assert_eq!(
3071 doc["admission"]["memory_estimate"]["recurrent_state_capacity_bytes"],
3072 serde_json::json!(16u64 * 65_863_680u64)
3073 );
3074 }
3075
3076 #[test]
3077 fn qwen35_fast_recurrent_state_budget_selects_default_slots_without_vram_special_case() {
3078 let hardware =
3079 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3080 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3081 let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_PAGED_MAX_SEQS", "32")]))
3082 .with_model_capabilities(qwen35_moe_gptq_int4_model())
3083 .with_hardware_capabilities(hardware)
3084 .with_workload_profile(workload)
3085 .resolve()
3086 .unwrap();
3087 let decision = |selection: &str| {
3088 resolved
3089 .decisions
3090 .iter()
3091 .find(|decision| decision.selection == selection)
3092 .unwrap_or_else(|| panic!("missing decision {selection}"))
3093 };
3094
3095 assert_eq!(decision("max_sequences").selected, "32");
3096 assert_eq!(decision("recurrent_state_max_slots").selected, "32");
3097 assert_eq!(
3098 decision("recurrent_state_max_slots").source,
3099 AutoConfigSource::Env
3100 );
3101 let doc = resolved.effective_config_document();
3102 assert_eq!(doc["selected_max_sequences"], serde_json::json!(32));
3103 assert_eq!(
3104 doc["selected_recurrent_state_max_slots"],
3105 serde_json::json!(32)
3106 );
3107 assert_eq!(doc["selected_admission_limit"], serde_json::json!(32));
3108 assert_eq!(
3109 doc["admission"]["memory_estimate"]["recurrent_state_bytes_per_sequence"],
3110 serde_json::json!(32_931_840u64)
3111 );
3112 assert_eq!(
3113 doc["admission"]["memory_estimate"]["recurrent_state_budget_raw_slots"],
3114 serde_json::json!(40)
3115 );
3116 assert_eq!(
3117 doc["admission"]["memory_estimate"]["recurrent_state_budget_max_slots"],
3118 serde_json::json!(32)
3119 );
3120 assert_eq!(
3121 doc["admission"]["memory_estimate"]["recurrent_state_capacity_bytes"],
3122 serde_json::json!(32u64 * 32_931_840u64)
3123 );
3124 }
3125
3126 #[test]
3127 fn recurrent_state_budget_rejects_explicit_slot_pool_above_budget() {
3128 let hardware =
3129 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3130 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3131 let err = FerrumConfigBuilder::new(snapshot(&[
3132 ("FERRUM_PAGED_MAX_SEQS", "32"),
3133 ("FERRUM_RECURRENT_STATE_MAX_SLOTS", "32"),
3134 ]))
3135 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3136 .with_hardware_capabilities(hardware)
3137 .with_workload_profile(workload)
3138 .resolve()
3139 .unwrap_err();
3140
3141 match err {
3142 AutoConfigError::InvalidOverride { key, reason } => {
3143 assert_eq!(key, "FERRUM_RECURRENT_STATE_MAX_SLOTS");
3144 assert!(
3145 reason.contains("recurrent-state slot pool exceeds"),
3146 "{reason}"
3147 );
3148 assert!(
3149 reason.contains("FERRUM_RECURRENT_STATE_MAX_SLOTS=16"),
3150 "{reason}"
3151 );
3152 }
3153 other => panic!("unexpected error: {other:?}"),
3154 }
3155 }
3156
3157 #[test]
3158 fn effective_admission_limit_respects_resource_authority() {
3159 let legacy = ExecutionResourceAuthority::LegacyEngine;
3160 let plan = ExecutionResourceAuthority::PlanRuntime;
3161 assert_eq!(
3162 effective_admission_limit(legacy, Some(32), Some(16)),
3163 Some(16)
3164 );
3165 assert_eq!(
3166 effective_admission_limit(legacy, Some(16), Some(32)),
3167 Some(16)
3168 );
3169 assert_eq!(effective_admission_limit(legacy, Some(32), None), Some(32));
3170 assert_eq!(effective_admission_limit(legacy, None, Some(16)), Some(16));
3171 assert_eq!(effective_admission_limit(legacy, None, None), None);
3172 assert_eq!(
3173 effective_admission_limit(plan, Some(32), Some(16)),
3174 Some(32)
3175 );
3176 }
3177
3178 #[test]
3179 fn plan_runtime_uses_dynamic_admission_and_typed_attention_authority() {
3180 let hardware =
3181 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3182 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3183 let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_PAGED_MAX_SEQS", "32")]))
3184 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3185 .with_hardware_capabilities(hardware)
3186 .with_workload_profile(workload)
3187 .with_execution_resource_authority(ExecutionResourceAuthority::PlanRuntime)
3188 .resolve()
3189 .unwrap();
3190
3191 assert_eq!(
3192 resolved.execution_resource_authority,
3193 ExecutionResourceAuthority::PlanRuntime
3194 );
3195 assert_eq!(
3196 resolved.requested_attention_policy,
3197 Some(AttentionExecutionPolicy::Auto)
3198 );
3199 assert_eq!(
3200 resolved.compiled_attention_policy,
3201 Some(AttentionExecutionPolicy::NativeAdaptive)
3202 );
3203 assert!(resolved
3204 .decisions
3205 .iter()
3206 .all(|decision| decision.selection != "recurrent_state_max_slots"));
3207 assert!(resolved
3208 .runtime_config
3209 .entries
3210 .iter()
3211 .all(|entry| !matches!(
3212 entry.key.as_str(),
3213 "FERRUM_RECURRENT_STATE_MAX_SLOTS"
3214 | "FERRUM_USE_VLLM_PAGED_ATTN"
3215 | "FERRUM_VLLM_PAGED_ATTN_V1_SHORT"
3216 )));
3217
3218 let document = resolved.effective_config_document();
3219 assert_eq!(document["selected_max_sequences"], serde_json::json!(32));
3220 assert_eq!(document["selected_admission_limit"], serde_json::json!(32));
3221 assert_eq!(
3222 document["selected_attention_policy"],
3223 serde_json::json!("native-adaptive")
3224 );
3225 assert_eq!(
3226 document["attention_execution"]["requested_policy"],
3227 serde_json::json!("auto")
3228 );
3229 assert_eq!(
3230 document["attention_execution"]["compiled_policy"],
3231 serde_json::json!("native-adaptive")
3232 );
3233 assert_eq!(
3234 document["attention_execution"]["legacy_attention_keys_apply"],
3235 serde_json::json!(false)
3236 );
3237 assert_eq!(
3238 document["admission"]["resource_authority"],
3239 serde_json::json!("plan_runtime")
3240 );
3241 assert_eq!(
3242 document["admission"]["effective_max_concurrent"],
3243 serde_json::json!(32)
3244 );
3245 assert_eq!(
3246 document["admission"]["legacy_recurrent_state_limit_applies"],
3247 serde_json::json!(false)
3248 );
3249 }
3250
3251 #[test]
3252 fn plan_runtime_rejects_legacy_attention_overrides() {
3253 let hardware =
3254 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3255 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3256 for key in [
3257 "FERRUM_USE_VLLM_PAGED_ATTN",
3258 "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
3259 ] {
3260 let error = FerrumConfigBuilder::new(snapshot(&[(key, "0")]))
3261 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3262 .with_hardware_capabilities(hardware.clone())
3263 .with_workload_profile(workload.clone())
3264 .with_execution_resource_authority(ExecutionResourceAuthority::PlanRuntime)
3265 .resolve()
3266 .unwrap_err();
3267 assert!(matches!(
3268 error,
3269 AutoConfigError::InvalidOverride {
3270 key: rejected_key,
3271 reason,
3272 } if rejected_key == key && reason.contains("FERRUM_ATTENTION_POLICY")
3273 ));
3274 }
3275 }
3276
3277 #[test]
3278 fn plan_runtime_rejects_legacy_recurrent_slot_override() {
3279 let hardware =
3280 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3281 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3282 let error = FerrumConfigBuilder::new(snapshot(&[
3283 ("FERRUM_PAGED_MAX_SEQS", "32"),
3284 ("FERRUM_RECURRENT_STATE_MAX_SLOTS", "16"),
3285 ]))
3286 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3287 .with_hardware_capabilities(hardware)
3288 .with_workload_profile(workload)
3289 .with_execution_resource_authority(ExecutionResourceAuthority::PlanRuntime)
3290 .resolve()
3291 .unwrap_err();
3292
3293 assert!(matches!(
3294 error,
3295 AutoConfigError::InvalidOverride { key, .. }
3296 if key == "FERRUM_RECURRENT_STATE_MAX_SLOTS"
3297 ));
3298 }
3299
3300 #[test]
3301 fn metal_plan_runtime_preflight_matches_portable_runtime_capability() {
3302 let mut hardware = HardwareCapabilities::unknown();
3303 hardware.backend = "metal".to_owned();
3304 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3305 let resolved = FerrumConfigBuilder::new(snapshot(&[]))
3306 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3307 .with_hardware_capabilities(hardware)
3308 .with_workload_profile(workload)
3309 .with_execution_resource_authority(ExecutionResourceAuthority::PlanRuntime)
3310 .resolve()
3311 .unwrap();
3312
3313 assert_eq!(
3314 resolved.compiled_attention_policy,
3315 Some(AttentionExecutionPolicy::Portable)
3316 );
3317 assert_eq!(
3318 resolved
3319 .decisions
3320 .iter()
3321 .find(|decision| decision.selection == "attention_decode_backend")
3322 .map(|decision| decision.selected.as_str()),
3323 Some("portable")
3324 );
3325 }
3326
3327 #[test]
3328 fn explicit_recurrent_state_slot_cap_can_keep_scheduler_width_above_state_pool() {
3329 let hardware =
3330 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3331 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3332 let resolved = FerrumConfigBuilder::new(snapshot(&[
3333 ("FERRUM_PAGED_MAX_SEQS", "32"),
3334 ("FERRUM_RECURRENT_STATE_MAX_SLOTS", "16"),
3335 ]))
3336 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3337 .with_hardware_capabilities(hardware)
3338 .with_workload_profile(workload)
3339 .resolve()
3340 .unwrap();
3341 let decision = |selection: &str| {
3342 resolved
3343 .decisions
3344 .iter()
3345 .find(|decision| decision.selection == selection)
3346 .unwrap_or_else(|| panic!("missing decision {selection}"))
3347 };
3348 assert_eq!(decision("max_sequences").selected, "32");
3349 assert_eq!(decision("recurrent_state_max_slots").selected, "16");
3350 let entry = resolved
3351 .runtime_config
3352 .entries
3353 .iter()
3354 .find(|entry| entry.key == "FERRUM_RECURRENT_STATE_MAX_SLOTS")
3355 .expect("recurrent-state slot cap should reach effective runtime config");
3356 assert_eq!(entry.effective_value, "16");
3357 }
3358
3359 #[test]
3360 fn recurrent_state_budget_ignores_removed_qwen35_slot_alias() {
3361 let hardware =
3362 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3363 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3364 let resolved = FerrumConfigBuilder::new(snapshot(&[
3365 ("FERRUM_PAGED_MAX_SEQS", "32"),
3366 ("FERRUM_QWEN35_LINEAR_STATE_MAX_SLOTS", "7"),
3367 ]))
3368 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3369 .with_hardware_capabilities(hardware)
3370 .with_workload_profile(workload)
3371 .resolve()
3372 .unwrap();
3373 let decision = resolved
3374 .decisions
3375 .iter()
3376 .find(|decision| decision.selection == "recurrent_state_max_slots")
3377 .expect("missing recurrent_state_max_slots decision");
3378 assert_eq!(decision.selected, "16");
3379 assert_eq!(decision.source, AutoConfigSource::MemoryProfile);
3380 assert_eq!(decision.source_key, None);
3381 assert!(resolved
3382 .runtime_config
3383 .entries
3384 .iter()
3385 .any(|entry| entry.key == "FERRUM_RECURRENT_STATE_MAX_SLOTS"
3386 && entry.effective_value == "16"));
3387 }
3388
3389 #[test]
3390 fn recurrent_state_budget_rejects_zero_slot_pool() {
3391 let hardware =
3392 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3393 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3394 let err = FerrumConfigBuilder::new(snapshot(&[
3395 ("FERRUM_PAGED_MAX_SEQS", "16"),
3396 ("FERRUM_RECURRENT_STATE_MAX_SLOTS", "0"),
3397 ]))
3398 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3399 .with_hardware_capabilities(hardware)
3400 .with_workload_profile(workload)
3401 .resolve()
3402 .unwrap_err();
3403
3404 assert!(matches!(
3405 err,
3406 AutoConfigError::InvalidOverride { key, .. }
3407 if key == "FERRUM_RECURRENT_STATE_MAX_SLOTS"
3408 ));
3409 }
3410
3411 #[test]
3412 fn recurrent_state_budget_allows_c32_when_memory_budget_fits() {
3413 let mut hardware =
3414 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3415 hardware.vram_bytes = Some(48 * GIB);
3416 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3417 let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_PAGED_MAX_SEQS", "32")]))
3418 .with_model_capabilities(synthetic_tight_recurrent_state_model())
3419 .with_hardware_capabilities(hardware)
3420 .with_workload_profile(workload)
3421 .resolve()
3422 .unwrap();
3423 let max_sequences = resolved
3424 .decisions
3425 .iter()
3426 .find(|decision| decision.selection == "max_sequences")
3427 .unwrap();
3428 assert_eq!(max_sequences.selected, "32");
3429 }
3430
3431 #[test]
3432 fn cuda_qwen3_moe_vllm_paged_attn_env_opt_out_is_materialized() {
3433 let hardware =
3434 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3435 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
3436 let resolved = FerrumConfigBuilder::new(snapshot(&[("FERRUM_USE_VLLM_PAGED_ATTN", "0")]))
3437 .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
3438 .with_hardware_capabilities(hardware)
3439 .with_workload_profile(workload)
3440 .resolve()
3441 .unwrap();
3442 let decisions: BTreeMap<_, _> = resolved
3443 .decisions
3444 .iter()
3445 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
3446 .collect();
3447 assert_eq!(decisions["attention_decode_backend"], "legacy_paged_decode");
3448 let entry = resolved
3449 .runtime_config
3450 .entries
3451 .iter()
3452 .find(|entry| entry.key == "FERRUM_USE_VLLM_PAGED_ATTN")
3453 .expect("env opt-out should stay in effective config");
3454 assert_eq!(entry.effective_value, "0");
3455 assert_eq!(entry.source, RuntimeConfigSource::Env);
3456 }
3457
3458 #[test]
3459 fn qwen25_72b_layer_split_preset_selects_batch_tuned_defaults() {
3460 let resolved = FerrumConfigBuilder::new(qwen25_layer_split_runtime_entries(
3461 RuntimeConfigSource::Default,
3462 ))
3463 .with_model_capabilities(ModelCapabilities::qwen25_72b_gptq_int4())
3464 .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
3465 CompiledKernelFeatures::m3_fast_path_without_fa2(),
3466 ))
3467 .with_workload_profile(WorkloadProfile::qwen25_72b_gptq_int4_2x4090_layer_split())
3468 .resolve()
3469 .unwrap();
3470 let decision = |selection: &str| {
3471 resolved
3472 .decisions
3473 .iter()
3474 .find(|decision| decision.selection == selection)
3475 .unwrap_or_else(|| panic!("missing decision {selection}"))
3476 };
3477
3478 assert_eq!(
3479 resolved.preset.as_deref(),
3480 Some(QWEN25_72B_GPTQ_INT4_2X4090_LAYER_SPLIT_PRESET)
3481 );
3482 assert_eq!(decision("kv_block_count").selected, "1024");
3483 assert_eq!(decision("max_sequences").selected, "16");
3484 assert_eq!(decision("max_batched_tokens").selected, "1536");
3485 assert_eq!(decision("max_model_len").selected, "4096");
3486 assert_eq!(
3487 decision("scheduler_admission_policy").selected,
3488 "prefill_first_until_active:16+prefill_token_budget:elastic"
3489 );
3490 assert_eq!(
3491 decision("scheduler_admission_policy").source,
3492 AutoConfigSource::Default
3493 );
3494
3495 let doc = resolved.effective_config_document();
3496 assert_eq!(doc["selected_pipeline_mode"], "batch");
3497 assert_eq!(doc["selected_microbatch_size"], 16);
3498 assert_eq!(doc["selected_kv_capacity"], 1024);
3499 }
3500
3501 #[test]
3502 fn source_fa2_is_rejected_even_when_legacy_feature_is_compiled() {
3503 let err = m3(
3504 &[("FERRUM_FA2_SOURCE", "1")],
3505 CompiledKernelFeatures::m3_fast_path_with_source_fa2(),
3506 )
3507 .resolve()
3508 .unwrap_err();
3509
3510 match err {
3511 AutoConfigError::InvalidOverride { key, reason } => {
3512 assert_eq!(key, "FERRUM_FA2_SOURCE");
3513 assert!(reason.contains("native operator artifact"));
3514 }
3515 AutoConfigError::UnsupportedCombination { .. } => {
3516 panic!("expected invalid FERRUM_FA2_SOURCE override")
3517 }
3518 }
3519 }
3520
3521 #[test]
3522 fn source_fa2_is_rejected_when_not_compiled() {
3523 expect_invalid_key(&[("FERRUM_FA2_SOURCE", "1")], "FERRUM_FA2_SOURCE");
3524 }
3525
3526 #[test]
3527 fn fa2_native_artifact_typed_config_is_recorded_without_selecting_runtime_path() {
3528 let resolved = m3(
3529 &[
3530 (
3531 FA2_NATIVE_MANIFEST_KEY,
3532 "/tmp/native/fa2/native_operator_manifest.json",
3533 ),
3534 (
3535 FA2_NATIVE_ARTIFACT_KEY,
3536 "/tmp/native/fa2/libferrum_native_fa2.a",
3537 ),
3538 (
3539 FA2_NATIVE_SOURCE_SHA256_KEY,
3540 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
3541 ),
3542 (
3543 FA2_NATIVE_INPUTS_SHA256_KEY,
3544 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
3545 ),
3546 ],
3547 CompiledKernelFeatures::m3_fast_path_with_native_fa2_artifact(),
3548 )
3549 .resolve()
3550 .unwrap();
3551
3552 let decisions: BTreeMap<_, _> = resolved
3553 .decisions
3554 .iter()
3555 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
3556 .collect();
3557 assert_eq!(decisions["fa2_native_operator_artifact"], "configured");
3558 assert_eq!(
3559 decisions["fa2_native_operator_runtime_selection"],
3560 "not_selected"
3561 );
3562 assert_ne!(decisions["attention_prefill_mixed_backend"], "fa2_native");
3563 assert!(resolved
3564 .runtime_config
3565 .entries
3566 .iter()
3567 .any(|entry| entry.key == FA2_NATIVE_MANIFEST_KEY
3568 && entry.source == RuntimeConfigSource::Env));
3569 }
3570
3571 #[test]
3572 fn fa2_native_runtime_selection_is_explicit_when_not_configured() {
3573 let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
3574 .resolve()
3575 .unwrap();
3576 let decision = resolved
3577 .decisions
3578 .iter()
3579 .find(|decision| decision.selection == "fa2_native_operator_runtime_selection")
3580 .unwrap();
3581 assert_eq!(decision.selected, "not_configured");
3582 assert!(decision.rejected.iter().any(|candidate| {
3583 candidate.value == "selected"
3584 && candidate
3585 .reason
3586 .contains("artifact-selected actual model smoke")
3587 }));
3588 }
3589
3590 #[test]
3591 fn fa2_native_artifact_requires_manifest_artifact_pair_and_valid_pins() {
3592 expect_invalid_key(
3593 &[(
3594 FA2_NATIVE_MANIFEST_KEY,
3595 "/tmp/native/fa2/native_operator_manifest.json",
3596 )],
3597 FA2_NATIVE_ARTIFACT_KEY,
3598 );
3599 expect_invalid_key(
3600 &[(
3601 FA2_NATIVE_ARTIFACT_KEY,
3602 "/tmp/native/fa2/libferrum_native_fa2.a",
3603 )],
3604 FA2_NATIVE_MANIFEST_KEY,
3605 );
3606 expect_invalid_key(
3607 &[
3608 (
3609 FA2_NATIVE_MANIFEST_KEY,
3610 "/tmp/native/fa2/native_operator_manifest.json",
3611 ),
3612 (
3613 FA2_NATIVE_ARTIFACT_KEY,
3614 "/tmp/native/fa2/libferrum_native_fa2.a",
3615 ),
3616 (FA2_NATIVE_SOURCE_SHA256_KEY, "not-a-sha"),
3617 ],
3618 FA2_NATIVE_SOURCE_SHA256_KEY,
3619 );
3620 expect_invalid_key(
3621 &[(
3622 FA2_NATIVE_INPUTS_SHA256_KEY,
3623 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
3624 )],
3625 FA2_NATIVE_INPUTS_SHA256_KEY,
3626 );
3627 }
3628
3629 #[test]
3630 fn fa2_native_artifact_requires_linked_binary_capability() {
3631 expect_invalid_key(
3632 &[
3633 (
3634 FA2_NATIVE_MANIFEST_KEY,
3635 "/tmp/native/fa2/native_operator_manifest.json",
3636 ),
3637 (
3638 FA2_NATIVE_ARTIFACT_KEY,
3639 "/tmp/native/fa2/libferrum_native_fa2.a",
3640 ),
3641 ],
3642 FA2_NATIVE_MANIFEST_KEY,
3643 );
3644 }
3645
3646 #[test]
3647 fn fa2_native_artifact_requires_linked_binary_metadata() {
3648 let mut features = CompiledKernelFeatures::m3_fast_path_without_fa2();
3649 features.fa2_native_operator_artifact = true;
3650
3651 let err = m3(
3652 &[
3653 (
3654 FA2_NATIVE_MANIFEST_KEY,
3655 "/tmp/native/fa2/native_operator_manifest.json",
3656 ),
3657 (
3658 FA2_NATIVE_ARTIFACT_KEY,
3659 "/tmp/native/fa2/libferrum_native_fa2.a",
3660 ),
3661 ],
3662 features,
3663 )
3664 .resolve()
3665 .unwrap_err();
3666 match err {
3667 AutoConfigError::InvalidOverride { key, reason } => {
3668 assert_eq!(key, FA2_NATIVE_MANIFEST_KEY);
3669 assert!(reason.contains("missing build metadata"));
3670 }
3671 other => panic!("expected invalid override, got {other:?}"),
3672 }
3673 }
3674
3675 #[test]
3676 fn fa2_native_artifact_must_match_linked_binary_metadata() {
3677 let features = CompiledKernelFeatures::m3_fast_path_with_native_fa2_artifact();
3678 expect_invalid_key_with_features(
3679 &[
3680 (
3681 FA2_NATIVE_MANIFEST_KEY,
3682 "/tmp/native/fa2/other_manifest.json",
3683 ),
3684 (
3685 FA2_NATIVE_ARTIFACT_KEY,
3686 "/tmp/native/fa2/libferrum_native_fa2.a",
3687 ),
3688 ],
3689 FA2_NATIVE_MANIFEST_KEY,
3690 features.clone(),
3691 );
3692 expect_invalid_key_with_features(
3693 &[
3694 (
3695 FA2_NATIVE_MANIFEST_KEY,
3696 "/tmp/native/fa2/native_operator_manifest.json",
3697 ),
3698 (
3699 FA2_NATIVE_ARTIFACT_KEY,
3700 "/tmp/native/fa2/libferrum_native_other.a",
3701 ),
3702 ],
3703 FA2_NATIVE_ARTIFACT_KEY,
3704 features.clone(),
3705 );
3706 expect_invalid_key_with_features(
3707 &[
3708 (
3709 FA2_NATIVE_MANIFEST_KEY,
3710 "/tmp/native/fa2/native_operator_manifest.json",
3711 ),
3712 (
3713 FA2_NATIVE_ARTIFACT_KEY,
3714 "/tmp/native/fa2/libferrum_native_fa2.a",
3715 ),
3716 (
3717 FA2_NATIVE_SOURCE_SHA256_KEY,
3718 "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
3719 ),
3720 ],
3721 FA2_NATIVE_SOURCE_SHA256_KEY,
3722 features.clone(),
3723 );
3724 expect_invalid_key_with_features(
3725 &[
3726 (
3727 FA2_NATIVE_MANIFEST_KEY,
3728 "/tmp/native/fa2/native_operator_manifest.json",
3729 ),
3730 (
3731 FA2_NATIVE_ARTIFACT_KEY,
3732 "/tmp/native/fa2/libferrum_native_fa2.a",
3733 ),
3734 (
3735 FA2_NATIVE_INPUTS_SHA256_KEY,
3736 "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
3737 ),
3738 ],
3739 FA2_NATIVE_INPUTS_SHA256_KEY,
3740 features,
3741 );
3742 }
3743
3744 #[test]
3745 fn fa2_native_artifact_requires_cuda_backend() {
3746 let hardware =
3747 cpu_hardware_with_features(CompiledKernelFeatures::m3_fast_path_without_fa2());
3748 expect_invalid_key_with_hardware(
3749 &[
3750 (
3751 FA2_NATIVE_MANIFEST_KEY,
3752 "/tmp/native/fa2/native_operator_manifest.json",
3753 ),
3754 (
3755 FA2_NATIVE_ARTIFACT_KEY,
3756 "/tmp/native/fa2/libferrum_native_fa2.a",
3757 ),
3758 ],
3759 FA2_NATIVE_MANIFEST_KEY,
3760 hardware,
3761 );
3762 }
3763
3764 #[test]
3765 fn hardware_capabilities_keep_m3_preset_on_compatible_backend_paths() {
3766 let resolved = m3_with_hardware(
3767 &[],
3768 cpu_hardware_with_features(CompiledKernelFeatures::m3_fast_path_with_source_fa2()),
3769 )
3770 .resolve()
3771 .unwrap();
3772 let decisions: BTreeMap<_, _> = resolved
3773 .decisions
3774 .iter()
3775 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
3776 .collect();
3777
3778 assert_eq!(
3779 decisions["attention_prefill_mixed_backend"],
3780 "legacy_paged_varlen"
3781 );
3782 assert_eq!(decisions["attention_decode_backend"], "legacy_paged_decode");
3783 assert_eq!(decisions["moe_implementation"], "legacy_moe");
3784 assert_eq!(decisions["moe_graph_policy"], "graph_disabled");
3785 assert_eq!(decisions["sampling_readback_path"], "logits_readback");
3786 }
3787
3788 #[test]
3789 fn effective_config_document_records_cuda_gpu_device_selection() {
3790 let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[
3791 (
3792 "FERRUM_REQUESTED_GPU_DEVICES",
3793 "0,1",
3794 RuntimeConfigSource::Cli,
3795 ),
3796 (
3797 "FERRUM_SELECTED_GPU_DEVICES",
3798 "0,1",
3799 RuntimeConfigSource::Cli,
3800 ),
3801 ("FERRUM_CUDA_DEVICE_COUNT", "2", RuntimeConfigSource::Cli),
3802 (
3803 "FERRUM_SELECTED_DISTRIBUTED_STRATEGY",
3804 "layer_split",
3805 RuntimeConfigSource::Cli,
3806 ),
3807 (
3808 "FERRUM_SELECTED_LAYER_SPLIT_PLAN",
3809 "stage0:cuda:0:layers=0-39;stage1:cuda:1:layers=40-79",
3810 RuntimeConfigSource::Cli,
3811 ),
3812 (
3813 "FERRUM_SELECTED_LAYER_SPLIT_STAGES",
3814 r#"[{"stage":0,"device":0,"layer_start":0,"layer_end":39},{"stage":1,"device":1,"layer_start":40,"layer_end":79}]"#,
3815 RuntimeConfigSource::Cli,
3816 ),
3817 ("FERRUM_KV_CAPACITY", "512", RuntimeConfigSource::Cli),
3818 ]))
3819 .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
3820 CompiledKernelFeatures::m3_fast_path_without_fa2(),
3821 ))
3822 .resolve()
3823 .unwrap();
3824
3825 let doc = resolved.effective_config_document();
3826 assert_eq!(doc["backend"], "cuda");
3827 assert_eq!(doc["requested_gpu_devices"], serde_json::json!([0, 1]));
3828 assert_eq!(doc["selected_gpu_devices"], serde_json::json!([0, 1]));
3829 assert_eq!(doc["cuda_device_count"], 2);
3830 assert_eq!(doc["selected_distributed_strategy"], "layer_split");
3831 assert_eq!(
3832 doc["selected_layer_split_plan"],
3833 "stage0:cuda:0:layers=0-39;stage1:cuda:1:layers=40-79"
3834 );
3835 assert_eq!(
3836 doc["selected_layer_split_stages"],
3837 serde_json::json!([
3838 {"stage": 0, "device": 0, "layer_start": 0, "layer_end": 39},
3839 {"stage": 1, "device": 1, "layer_start": 40, "layer_end": 79}
3840 ])
3841 );
3842 assert_eq!(doc["selected_weight_placement"], "layer_split");
3843 assert_eq!(doc["selected_pipeline_mode"], "overlapped");
3844 assert_eq!(doc["selected_stage_bridge"], "host");
3845 assert_eq!(
3846 doc["selected_microbatch_size"],
3847 serde_json::json!(doc["selected_max_sequences"].as_u64().unwrap().div_ceil(2))
3848 );
3849 assert_eq!(
3850 doc["selected_admission_limit"],
3851 doc["selected_max_sequences"]
3852 );
3853 assert_eq!(doc["selected_kv_capacity"], 512);
3854 }
3855
3856 #[test]
3857 fn effective_config_document_honors_explicit_layer_split_batch_mode() {
3858 let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[
3859 (
3860 "FERRUM_REQUESTED_GPU_DEVICES",
3861 "0,1",
3862 RuntimeConfigSource::Cli,
3863 ),
3864 (
3865 "FERRUM_SELECTED_GPU_DEVICES",
3866 "0,1",
3867 RuntimeConfigSource::Cli,
3868 ),
3869 (
3870 "FERRUM_SELECTED_DISTRIBUTED_STRATEGY",
3871 "layer_split",
3872 RuntimeConfigSource::Cli,
3873 ),
3874 (
3875 "FERRUM_SELECTED_LAYER_SPLIT_PLAN",
3876 "stage0:cuda:0:layers=0-39;stage1:cuda:1:layers=40-79",
3877 RuntimeConfigSource::Cli,
3878 ),
3879 (
3880 "FERRUM_LAYER_SPLIT_PIPELINE_MODE",
3881 "batch",
3882 RuntimeConfigSource::Cli,
3883 ),
3884 ("FERRUM_PAGED_MAX_SEQS", "16", RuntimeConfigSource::Cli),
3885 ]))
3886 .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
3887 CompiledKernelFeatures::m3_fast_path_without_fa2(),
3888 ))
3889 .resolve()
3890 .unwrap();
3891
3892 let doc = resolved.effective_config_document();
3893 assert_eq!(doc["selected_pipeline_mode"], "batch");
3894 assert_eq!(doc["selected_microbatch_size"], 16);
3895 }
3896
3897 #[test]
3898 fn invalid_layer_split_pipeline_mode_is_rejected() {
3899 expect_invalid_key_with_hardware(
3900 &[("FERRUM_LAYER_SPLIT_PIPELINE_MODE", "serial")],
3901 "FERRUM_LAYER_SPLIT_PIPELINE_MODE",
3902 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2()),
3903 );
3904 }
3905
3906 #[test]
3907 fn hardware_incompatible_attention_and_sampling_overrides_are_rejected() {
3908 let cpu =
3909 cpu_hardware_with_features(CompiledKernelFeatures::m3_fast_path_with_source_fa2());
3910 expect_invalid_key_with_hardware(
3911 &[("FERRUM_USE_VLLM_PAGED_ATTN", "1")],
3912 "FERRUM_USE_VLLM_PAGED_ATTN",
3913 cpu.clone(),
3914 );
3915 expect_invalid_key_with_hardware(
3916 &[("FERRUM_VLLM_MOE", "1")],
3917 "FERRUM_VLLM_MOE",
3918 cpu.clone(),
3919 );
3920 expect_invalid_key_with_hardware(
3921 &[("FERRUM_GREEDY_ARGMAX", "1")],
3922 "FERRUM_GREEDY_ARGMAX",
3923 cpu.clone(),
3924 );
3925 expect_invalid_key_with_hardware(&[("FERRUM_FA2_SOURCE", "1")], "FERRUM_FA2_SOURCE", cpu);
3926
3927 let mut old_cuda = HardwareCapabilities::rtx4090_cuda(
3928 CompiledKernelFeatures::m3_fast_path_with_source_fa2(),
3929 );
3930 old_cuda.compute_capability = Some("7.5".to_string());
3931 expect_invalid_key_with_hardware(
3932 &[("FERRUM_FA2_SOURCE", "1")],
3933 "FERRUM_FA2_SOURCE",
3934 old_cuda,
3935 );
3936 }
3937
3938 #[test]
3939 fn hardware_capacity_sizes_default_sequence_budget_without_overriding_user_values() {
3940 let mut small_gpu =
3941 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3942 small_gpu.sm_count = Some(16);
3943 small_gpu.vram_bytes = Some(24 * 1024 * 1024 * 1024);
3944
3945 let resolved = m3_with_hardware(&[], small_gpu.clone()).resolve().unwrap();
3946 let decision = |selection: &str| {
3947 resolved
3948 .decisions
3949 .iter()
3950 .find(|decision| decision.selection == selection)
3951 .unwrap()
3952 };
3953 let max_sequences = decision("max_sequences");
3954 assert_eq!(max_sequences.selected, "4");
3955 assert_eq!(max_sequences.source, AutoConfigSource::HardwareCapability);
3956 let max_batched_tokens = decision("max_batched_tokens");
3957 assert_eq!(max_batched_tokens.selected, "256");
3958 assert_eq!(
3959 max_batched_tokens.source,
3960 AutoConfigSource::HardwareCapability
3961 );
3962
3963 let resolved = m3_with_hardware(&[("FERRUM_PAGED_MAX_SEQS", "16")], small_gpu)
3964 .resolve()
3965 .unwrap();
3966 let max_sequences = resolved
3967 .decisions
3968 .iter()
3969 .find(|decision| decision.selection == "max_sequences")
3970 .unwrap();
3971 assert_eq!(max_sequences.selected, "16");
3972 assert_eq!(max_sequences.source, AutoConfigSource::Env);
3973 assert_eq!(
3974 max_sequences.source_key.as_deref(),
3975 Some("FERRUM_PAGED_MAX_SEQS")
3976 );
3977 }
3978
3979 #[test]
3980 fn vram_capacity_caps_m3_default_sequence_budget() {
3981 let mut low_vram_gpu =
3982 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
3983 low_vram_gpu.sm_count = Some(128);
3984 low_vram_gpu.vram_bytes = Some(7 * 1024 * 1024 * 1024);
3985
3986 let resolved = m3_with_hardware(&[], low_vram_gpu).resolve().unwrap();
3987 let max_sequences = resolved
3988 .decisions
3989 .iter()
3990 .find(|decision| decision.selection == "max_sequences")
3991 .unwrap();
3992 assert_eq!(max_sequences.selected, "4");
3993 assert_eq!(max_sequences.source, AutoConfigSource::HardwareCapability);
3994 }
3995
3996 #[test]
3997 fn memory_budget_keeps_rtx4090_m3_kv_blocks_but_caps_constrained_vram() {
3998 let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
3999 .resolve()
4000 .unwrap();
4001 let decision = |selection: &str| {
4002 resolved
4003 .decisions
4004 .iter()
4005 .find(|decision| decision.selection == selection)
4006 .unwrap()
4007 };
4008 assert_eq!(decision("kv_block_count").selected, "2048");
4009 assert_eq!(
4010 decision("kv_block_count").source,
4011 AutoConfigSource::WorkloadPreset
4012 );
4013
4014 let mut constrained =
4015 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4016 constrained.vram_bytes = Some(20 * 1024 * 1024 * 1024);
4017 let resolved = m3_with_hardware(&[], constrained).resolve().unwrap();
4018 let decision = |selection: &str| {
4019 resolved
4020 .decisions
4021 .iter()
4022 .find(|decision| decision.selection == selection)
4023 .unwrap()
4024 };
4025 assert_eq!(decision("kv_block_count").selected, "2");
4026 assert_eq!(
4027 decision("kv_block_count").source,
4028 AutoConfigSource::HardwareCapability
4029 );
4030 assert_eq!(decision("max_batched_tokens").selected, "32");
4031 assert_eq!(
4032 decision("max_batched_tokens").source,
4033 AutoConfigSource::HardwareCapability
4034 );
4035 }
4036
4037 #[test]
4038 fn compute_capability_parser_accepts_major_minor_and_major_only() {
4039 assert_eq!(parse_compute_capability("8.9"), Some((8, 9)));
4040 assert_eq!(parse_compute_capability("9"), Some((9, 0)));
4041 assert_eq!(parse_compute_capability("N/A"), None);
4042 }
4043
4044 #[test]
4045 fn vram_capacity_tiers_are_monotonic() {
4046 assert_eq!(vram_default_max_sequences(24 * 1024 * 1024 * 1024), 32);
4047 assert_eq!(vram_default_max_sequences(16 * 1024 * 1024 * 1024), 16);
4048 assert_eq!(vram_default_max_sequences(8 * 1024 * 1024 * 1024), 8);
4049 assert_eq!(vram_default_max_sequences(6 * 1024 * 1024 * 1024), 4);
4050 }
4051
4052 #[test]
4053 fn accelerator_serving_default_uses_hardware_concurrency_budget() {
4054 let hardware =
4055 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4056 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4057 assert_eq!(workload.target_concurrency, 32);
4058
4059 let resolved = FerrumConfigBuilder::new(snapshot(&[]))
4060 .with_model_capabilities(ModelCapabilities::unknown())
4061 .with_hardware_capabilities(hardware)
4062 .with_workload_profile(workload)
4063 .resolve()
4064 .unwrap();
4065 let max_sequences = resolved
4066 .decisions
4067 .iter()
4068 .find(|decision| decision.selection == "max_sequences")
4069 .unwrap();
4070 assert_eq!(max_sequences.selected, "32");
4071 let scheduler = resolved
4072 .decisions
4073 .iter()
4074 .find(|decision| decision.selection == "scheduler_admission_policy")
4075 .unwrap();
4076 assert_eq!(
4077 scheduler.selected,
4078 "prefill_first_until_active:32+prefill_token_budget:elastic"
4079 );
4080 assert_eq!(scheduler.source, AutoConfigSource::Default);
4081 let scheduler_entry = resolved
4082 .runtime_config
4083 .entries
4084 .iter()
4085 .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4086 .unwrap_or_else(|| panic!("missing FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE entry"));
4087 assert_eq!(scheduler_entry.effective_value, "32");
4088 assert_eq!(scheduler_entry.source, RuntimeConfigSource::Default);
4089 assert!(resolved
4090 .runtime_config
4091 .entries
4092 .iter()
4093 .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_STEP_CHUNK"));
4094 }
4095
4096 #[test]
4097 fn accelerator_serving_default_enables_greedy_argmax_when_compiled() {
4098 let hardware =
4099 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4100 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4101 let resolved = FerrumConfigBuilder::new(snapshot(&[]))
4102 .with_model_capabilities(ModelCapabilities::unknown())
4103 .with_hardware_capabilities(hardware)
4104 .with_workload_profile(workload)
4105 .resolve()
4106 .unwrap();
4107 let sampling = resolved
4108 .decisions
4109 .iter()
4110 .find(|decision| decision.selection == "sampling_readback_path")
4111 .unwrap();
4112 assert_eq!(sampling.selected, "gpu_greedy_argmax");
4113 assert_eq!(sampling.source, AutoConfigSource::HardwareCapability);
4114 let greedy_entry = resolved
4115 .runtime_config
4116 .entries
4117 .iter()
4118 .find(|entry| entry.key == "FERRUM_GREEDY_ARGMAX")
4119 .unwrap_or_else(|| panic!("missing FERRUM_GREEDY_ARGMAX entry"));
4120 assert_eq!(greedy_entry.effective_value, "1");
4121 }
4122
4123 #[test]
4124 fn explicit_greedy_argmax_disable_keeps_logits_readback() {
4125 let hardware =
4126 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4127 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4128 let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[(
4129 "FERRUM_GREEDY_ARGMAX",
4130 "0",
4131 RuntimeConfigSource::Cli,
4132 )]))
4133 .with_model_capabilities(ModelCapabilities::unknown())
4134 .with_hardware_capabilities(hardware)
4135 .with_workload_profile(workload)
4136 .resolve()
4137 .unwrap();
4138 let sampling = resolved
4139 .decisions
4140 .iter()
4141 .find(|decision| decision.selection == "sampling_readback_path")
4142 .unwrap();
4143 assert_eq!(sampling.selected, "logits_readback");
4144 assert_eq!(sampling.source, AutoConfigSource::Cli);
4145 let greedy_entry = resolved
4146 .runtime_config
4147 .entries
4148 .iter()
4149 .find(|entry| entry.key == "FERRUM_GREEDY_ARGMAX")
4150 .unwrap_or_else(|| panic!("missing FERRUM_GREEDY_ARGMAX entry"));
4151 assert_eq!(greedy_entry.effective_value, "0");
4152 }
4153
4154 #[test]
4155 fn cpu_serving_default_keeps_single_sequence_budget() {
4156 let hardware = HardwareCapabilities {
4157 backend: "cpu".to_string(),
4158 supported_dtypes: vec!["fp32".to_string()],
4159 ..HardwareCapabilities::unknown()
4160 };
4161 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4162 assert_eq!(workload.target_concurrency, 1);
4163 let resolved = FerrumConfigBuilder::new(snapshot(&[]))
4164 .with_model_capabilities(ModelCapabilities::unknown())
4165 .with_hardware_capabilities(hardware)
4166 .with_workload_profile(workload)
4167 .resolve()
4168 .unwrap();
4169 let scheduler = resolved
4170 .decisions
4171 .iter()
4172 .find(|decision| decision.selection == "scheduler_admission_policy")
4173 .unwrap();
4174 assert_eq!(scheduler.selected, "prompt_token_estimate");
4175 assert!(resolved
4176 .runtime_config
4177 .entries
4178 .iter()
4179 .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE"));
4180 assert!(resolved
4181 .runtime_config
4182 .entries
4183 .iter()
4184 .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_STEP_CHUNK"));
4185 }
4186
4187 #[test]
4188 fn validates_invalid_override_matrix() {
4189 expect_invalid_key(
4190 &[("FERRUM_USE_VLLM_PAGED_ATTN", "maybe")],
4191 "FERRUM_USE_VLLM_PAGED_ATTN",
4192 );
4193 expect_invalid_key(&[("FERRUM_PREFIX_CACHE", "maybe")], "FERRUM_PREFIX_CACHE");
4194 expect_invalid_key(
4195 &[
4196 ("FERRUM_FA_LAYOUT_VARLEN", "1"),
4197 ("FERRUM_USE_VLLM_PAGED_ATTN", "0"),
4198 ],
4199 "FERRUM_FA_LAYOUT_VARLEN",
4200 );
4201 expect_invalid_key(&[("FERRUM_FA2_DIRECT_FFI", "1")], "FERRUM_FA2_DIRECT_FFI");
4202 expect_invalid_key_with_features(
4203 &[("FERRUM_VLLM_MOE", "1")],
4204 "FERRUM_VLLM_MOE",
4205 CompiledKernelFeatures::default(),
4206 );
4207 expect_invalid_key(
4208 &[("FERRUM_MOE_DEVICE_ROUTE", "1"), ("FERRUM_VLLM_MOE", "0")],
4209 "FERRUM_MOE_DEVICE_ROUTE",
4210 );
4211 expect_invalid_key(
4212 &[("FERRUM_VLLM_MOE_PAIR_IDS", "1"), ("FERRUM_VLLM_MOE", "0")],
4213 "FERRUM_VLLM_MOE_PAIR_IDS",
4214 );
4215 expect_invalid_key(
4216 &[("FERRUM_MOE_GRAPH", "1"), ("FERRUM_VLLM_MOE", "0")],
4217 "FERRUM_MOE_GRAPH",
4218 );
4219 expect_invalid_key(&[("FERRUM_KV_MAX_BLOCKS", "0")], "FERRUM_KV_MAX_BLOCKS");
4220 expect_invalid_key(&[("FERRUM_PAGED_MAX_SEQS", "0")], "FERRUM_PAGED_MAX_SEQS");
4221 expect_invalid_key(
4222 &[
4223 ("FERRUM_PAGED_MAX_SEQS", "32"),
4224 ("FERRUM_MAX_BATCHED_TOKENS", "16"),
4225 ],
4226 "FERRUM_MAX_BATCHED_TOKENS",
4227 );
4228 expect_invalid_key(
4229 &[
4230 ("FERRUM_KV_MAX_BLOCKS", "16"),
4231 ("FERRUM_MAX_BATCHED_TOKENS", "512"),
4232 ],
4233 "FERRUM_MAX_BATCHED_TOKENS",
4234 );
4235 expect_invalid_key(&[("FERRUM_MAX_MODEL_LEN", "0")], "FERRUM_MAX_MODEL_LEN");
4236 expect_invalid_key(&[("FERRUM_MAX_MODEL_LEN", "50000")], "FERRUM_MAX_MODEL_LEN");
4237 expect_invalid_key(
4238 &[
4239 ("FERRUM_KV_MAX_BLOCKS", "16"),
4240 ("FERRUM_MAX_MODEL_LEN", "1024"),
4241 ],
4242 "FERRUM_KV_MAX_BLOCKS",
4243 );
4244 expect_invalid_key(&[("FERRUM_DTYPE", "bf16")], "FERRUM_DTYPE");
4245 expect_invalid_key(&[("FERRUM_KV_DTYPE", "fp8")], "FERRUM_KV_DTYPE");
4246 expect_invalid_key(
4247 &[
4248 ("FERRUM_VLLM_PAGED_ATTN_V1_SHORT", "1"),
4249 ("FERRUM_USE_VLLM_PAGED_ATTN", "0"),
4250 ],
4251 "FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
4252 );
4253 }
4254
4255 #[test]
4256 fn requested_max_model_len_is_optional_and_reflected_when_valid() {
4257 let default_resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
4258 .resolve()
4259 .unwrap();
4260 assert!(!default_resolved
4261 .decisions
4262 .iter()
4263 .any(|decision| decision.selection == "max_model_len"));
4264
4265 let resolved = m3(
4266 &[
4267 ("FERRUM_KV_MAX_BLOCKS", "64"),
4268 ("FERRUM_MAX_MODEL_LEN", "1024"),
4269 ],
4270 CompiledKernelFeatures::m3_fast_path_without_fa2(),
4271 )
4272 .resolve()
4273 .unwrap();
4274 let max_model_len = resolved
4275 .decisions
4276 .iter()
4277 .find(|decision| decision.selection == "max_model_len")
4278 .unwrap();
4279 assert_eq!(max_model_len.selected, "1024");
4280 assert_eq!(
4281 max_model_len.source_key.as_deref(),
4282 Some("FERRUM_MAX_MODEL_LEN")
4283 );
4284 }
4285
4286 #[test]
4287 fn graph_enabled_with_graph_unsafe_moe_is_rejected() {
4288 let mut model = ModelCapabilities::qwen3_30b_a3b_gptq_int4();
4289 model.graph_safe_moe = false;
4290 let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_MOE_GRAPH", "1")]))
4291 .with_model_capabilities(model)
4292 .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
4293 CompiledKernelFeatures::m3_fast_path_without_fa2(),
4294 ))
4295 .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
4296 .resolve()
4297 .expect_err("graph unsafe MoE must fail");
4298 assert!(matches!(
4299 err,
4300 AutoConfigError::UnsupportedCombination {
4301 selection,
4302 ..
4303 } if selection == "moe_graph_policy"
4304 ));
4305 }
4306
4307 #[test]
4308 fn batched_graph_override_materializes_decode_graph_policy() {
4309 let hardware =
4310 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4311 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4312 let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[(
4313 "FERRUM_BATCHED_GRAPH",
4314 "1",
4315 RuntimeConfigSource::Cli,
4316 )]))
4317 .with_model_capabilities(ModelCapabilities::unknown())
4318 .with_hardware_capabilities(hardware)
4319 .with_workload_profile(workload)
4320 .resolve()
4321 .unwrap();
4322 let decisions: BTreeMap<_, _> = resolved
4323 .decisions
4324 .iter()
4325 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4326 .collect();
4327 assert_eq!(
4328 decisions["decode_graph_policy"],
4329 "legacy_batched_decode_graph"
4330 );
4331 let entry = resolved
4332 .runtime_config
4333 .entries
4334 .iter()
4335 .find(|entry| entry.key == "FERRUM_BATCHED_GRAPH")
4336 .expect("batched graph entry");
4337 assert_eq!(entry.effective_value, "1");
4338 assert_eq!(
4339 resolved.effective_config_document()["selected_graph_mode"],
4340 "legacy_batched_decode_graph"
4341 );
4342 }
4343
4344 #[test]
4345 fn reusable_execution_defaults_on_without_enabling_legacy_graphs() {
4346 let hardware =
4347 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4348 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4349 let resolved = FerrumConfigBuilder::new(RuntimeConfigSnapshot::default())
4350 .with_model_capabilities(ModelCapabilities::unknown())
4351 .with_hardware_capabilities(hardware)
4352 .with_workload_profile(workload)
4353 .resolve()
4354 .unwrap();
4355 let entry = |key: &str| {
4356 resolved
4357 .runtime_config
4358 .entries
4359 .iter()
4360 .find(|entry| entry.key == key)
4361 .unwrap_or_else(|| panic!("missing {key} entry"))
4362 };
4363 let decisions: BTreeMap<_, _> = resolved
4364 .decisions
4365 .iter()
4366 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4367 .collect();
4368
4369 assert_eq!(entry("FERRUM_BATCHED_GRAPH").effective_value, "0");
4370 assert_eq!(entry("FERRUM_REUSABLE_EXECUTION").effective_value, "1");
4371 assert_eq!(decisions["decode_graph_policy"], "graph_disabled");
4372 assert_eq!(
4373 decisions["reusable_execution_policy"],
4374 "enabled_when_runtime_capable"
4375 );
4376 }
4377
4378 #[test]
4379 fn reusable_execution_explicit_disable_is_preserved() {
4380 let hardware =
4381 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4382 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4383 let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[(
4384 "FERRUM_REUSABLE_EXECUTION",
4385 "0",
4386 RuntimeConfigSource::Cli,
4387 )]))
4388 .with_model_capabilities(ModelCapabilities::unknown())
4389 .with_hardware_capabilities(hardware)
4390 .with_workload_profile(workload)
4391 .resolve()
4392 .unwrap();
4393 let entry = resolved
4394 .runtime_config
4395 .entries
4396 .iter()
4397 .find(|entry| entry.key == "FERRUM_REUSABLE_EXECUTION")
4398 .expect("reusable execution entry");
4399 let decision = resolved
4400 .decisions
4401 .iter()
4402 .find(|decision| decision.selection == "reusable_execution_policy")
4403 .expect("reusable execution decision");
4404
4405 assert_eq!(entry.effective_value, "0");
4406 assert_eq!(entry.source, RuntimeConfigSource::Cli);
4407 assert_eq!(decision.selected, "disabled");
4408 }
4409
4410 #[test]
4411 fn unified_graph_override_materializes_decode_graph_policy() {
4412 let hardware =
4413 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4414 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4415 let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[(
4416 "FERRUM_UNIFIED_GRAPH",
4417 "1",
4418 RuntimeConfigSource::Cli,
4419 )]))
4420 .with_model_capabilities(ModelCapabilities::unknown())
4421 .with_hardware_capabilities(hardware)
4422 .with_workload_profile(workload)
4423 .resolve()
4424 .unwrap();
4425 let decisions: BTreeMap<_, _> = resolved
4426 .decisions
4427 .iter()
4428 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4429 .collect();
4430 assert_eq!(decisions["decode_graph_policy"], "unified_decode_graph");
4431 let entry = resolved
4432 .runtime_config
4433 .entries
4434 .iter()
4435 .find(|entry| entry.key == "FERRUM_UNIFIED_GRAPH")
4436 .expect("unified graph entry");
4437 assert_eq!(entry.effective_value, "1");
4438 assert_eq!(
4439 resolved.effective_config_document()["selected_graph_mode"],
4440 "unified_decode_graph"
4441 );
4442 }
4443
4444 #[test]
4445 fn unified_graph_layers_only_override_materializes_decode_graph_policy() {
4446 let hardware =
4447 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4448 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4449 let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[
4450 ("FERRUM_UNIFIED_GRAPH", "1", RuntimeConfigSource::Cli),
4451 (
4452 "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY",
4453 "1",
4454 RuntimeConfigSource::Cli,
4455 ),
4456 ]))
4457 .with_model_capabilities(gemma3_gptq_model())
4458 .with_hardware_capabilities(hardware)
4459 .with_workload_profile(workload)
4460 .resolve()
4461 .unwrap();
4462 let decisions: BTreeMap<_, _> = resolved
4463 .decisions
4464 .iter()
4465 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4466 .collect();
4467 assert_eq!(
4468 decisions["decode_graph_policy"],
4469 "unified_decode_graph_layers_only"
4470 );
4471 let entry = resolved
4472 .runtime_config
4473 .entries
4474 .iter()
4475 .find(|entry| entry.key == "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY")
4476 .expect("unified graph layers-only entry");
4477 assert_eq!(entry.effective_value, "1");
4478 assert_eq!(
4479 resolved.effective_config_document()["selected_graph_mode"],
4480 "unified_decode_graph_layers_only"
4481 );
4482 }
4483
4484 #[test]
4485 fn unified_graph_lm_head_eager_override_materializes_decode_graph_policy() {
4486 let hardware =
4487 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4488 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4489 let resolved = FerrumConfigBuilder::new(snapshot_with_sources(&[
4490 ("FERRUM_UNIFIED_GRAPH", "1", RuntimeConfigSource::Cli),
4491 (
4492 "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
4493 "1",
4494 RuntimeConfigSource::Cli,
4495 ),
4496 ]))
4497 .with_model_capabilities(gemma3_gptq_model())
4498 .with_hardware_capabilities(hardware)
4499 .with_workload_profile(workload)
4500 .resolve()
4501 .unwrap();
4502 let decisions: BTreeMap<_, _> = resolved
4503 .decisions
4504 .iter()
4505 .map(|decision| (decision.selection.as_str(), decision.selected.as_str()))
4506 .collect();
4507 assert_eq!(
4508 decisions["decode_graph_policy"],
4509 "unified_decode_graph_lm_head_eager"
4510 );
4511 let entry = resolved
4512 .runtime_config
4513 .entries
4514 .iter()
4515 .find(|entry| entry.key == "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER")
4516 .expect("unified graph lm-head-eager entry");
4517 assert_eq!(entry.effective_value, "1");
4518 assert_eq!(
4519 resolved.effective_config_document()["selected_graph_mode"],
4520 "unified_decode_graph_lm_head_eager"
4521 );
4522 }
4523
4524 #[test]
4525 fn unified_graph_layers_only_requires_unified_graph() {
4526 let hardware =
4527 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4528 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4529 let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_UNIFIED_GRAPH_LAYERS_ONLY", "1")]))
4530 .with_model_capabilities(ModelCapabilities::unknown())
4531 .with_hardware_capabilities(hardware)
4532 .with_workload_profile(workload)
4533 .resolve()
4534 .expect_err("layers-only graph scope should require unified graph");
4535 assert!(matches!(
4536 err,
4537 AutoConfigError::InvalidOverride { key, .. }
4538 if key == "FERRUM_UNIFIED_GRAPH_LAYERS_ONLY"
4539 ));
4540 }
4541
4542 #[test]
4543 fn unified_graph_lm_head_eager_requires_unified_graph() {
4544 let hardware =
4545 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4546 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4547 let err =
4548 FerrumConfigBuilder::new(snapshot(&[("FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER", "1")]))
4549 .with_model_capabilities(ModelCapabilities::unknown())
4550 .with_hardware_capabilities(hardware)
4551 .with_workload_profile(workload)
4552 .resolve()
4553 .expect_err("lm-head-eager graph scope should require unified graph");
4554 assert!(matches!(
4555 err,
4556 AutoConfigError::InvalidOverride { key, .. }
4557 if key == "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER"
4558 ));
4559 }
4560
4561 #[test]
4562 fn unified_graph_lm_head_eager_conflicts_with_layers_only() {
4563 let hardware =
4564 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4565 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4566 let err = FerrumConfigBuilder::new(snapshot(&[
4567 ("FERRUM_UNIFIED_GRAPH", "1"),
4568 ("FERRUM_UNIFIED_GRAPH_LAYERS_ONLY", "1"),
4569 ("FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER", "1"),
4570 ]))
4571 .with_model_capabilities(ModelCapabilities::unknown())
4572 .with_hardware_capabilities(hardware)
4573 .with_workload_profile(workload)
4574 .resolve()
4575 .expect_err("lm-head-eager graph scope should conflict with layers-only graph scope");
4576 assert!(matches!(
4577 err,
4578 AutoConfigError::InvalidOverride { key, .. }
4579 if key == "FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER"
4580 ));
4581 }
4582
4583 #[test]
4584 fn batched_graph_requires_cuda_graph_support() {
4585 let mut features = CompiledKernelFeatures::m3_fast_path_without_fa2();
4586 features.cuda_graph = false;
4587 let hardware = HardwareCapabilities::rtx4090_cuda(features);
4588 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4589 let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_BATCHED_GRAPH", "1")]))
4590 .with_model_capabilities(ModelCapabilities::unknown())
4591 .with_hardware_capabilities(hardware)
4592 .with_workload_profile(workload)
4593 .resolve()
4594 .expect_err("batched graph should require compiled graph support");
4595 assert!(matches!(
4596 err,
4597 AutoConfigError::InvalidOverride { key, .. } if key == "FERRUM_BATCHED_GRAPH"
4598 ));
4599 }
4600
4601 #[test]
4602 fn unified_graph_requires_cuda_graph_support() {
4603 let mut features = CompiledKernelFeatures::m3_fast_path_without_fa2();
4604 features.cuda_graph = false;
4605 let hardware = HardwareCapabilities::rtx4090_cuda(features);
4606 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4607 let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_UNIFIED_GRAPH", "1")]))
4608 .with_model_capabilities(ModelCapabilities::unknown())
4609 .with_hardware_capabilities(hardware)
4610 .with_workload_profile(workload)
4611 .resolve()
4612 .expect_err("unified graph should require compiled graph support");
4613 assert!(matches!(
4614 err,
4615 AutoConfigError::InvalidOverride { key, .. } if key == "FERRUM_UNIFIED_GRAPH"
4616 ));
4617 }
4618
4619 #[test]
4620 fn unified_graph_rejects_gemma3_sandwich_models() {
4621 let hardware =
4622 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4623 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4624 let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_UNIFIED_GRAPH", "1")]))
4625 .with_model_capabilities(gemma3_gptq_model())
4626 .with_hardware_capabilities(hardware)
4627 .with_workload_profile(workload)
4628 .resolve()
4629 .expect_err("Gemma3 unified graph should stay disabled until graph instantiate fits");
4630 assert!(matches!(
4631 err,
4632 AutoConfigError::InvalidOverride { key, .. } if key == "FERRUM_UNIFIED_GRAPH"
4633 ));
4634 }
4635
4636 #[test]
4637 fn batched_graph_rejects_non_cuda_backend() {
4638 let hardware =
4639 cpu_hardware_with_features(CompiledKernelFeatures::m3_fast_path_without_fa2());
4640 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4641 let err = FerrumConfigBuilder::new(snapshot(&[("FERRUM_BATCHED_GRAPH", "1")]))
4642 .with_model_capabilities(ModelCapabilities::unknown())
4643 .with_hardware_capabilities(hardware)
4644 .with_workload_profile(workload)
4645 .resolve()
4646 .expect_err("batched graph should require CUDA");
4647 assert!(matches!(
4648 err,
4649 AutoConfigError::InvalidOverride { key, .. } if key == "FERRUM_BATCHED_GRAPH"
4650 ));
4651 }
4652
4653 #[test]
4654 fn scheduler_active_chunk_combines_with_accelerator_prefill_first_default() {
4655 let resolved = m3(
4656 &[("FERRUM_ACTIVE_DECODE_PREFILL_CHUNK", "64")],
4657 CompiledKernelFeatures::m3_fast_path_without_fa2(),
4658 )
4659 .resolve()
4660 .unwrap();
4661 let scheduler = resolved
4662 .decisions
4663 .iter()
4664 .find(|decision| decision.selection == "scheduler_admission_policy")
4665 .unwrap();
4666 assert_eq!(
4667 scheduler.selected,
4668 "prefill_first_until_active:32+active_decode_prefill_chunk:64+prefill_token_budget:elastic"
4669 );
4670 assert_eq!(
4671 scheduler.source_key.as_deref(),
4672 Some("FERRUM_ACTIVE_DECODE_PREFILL_CHUNK")
4673 );
4674 let prefill_entry = resolved
4675 .runtime_config
4676 .entries
4677 .iter()
4678 .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4679 .expect("accelerator default prefill-first should still be materialized");
4680 assert_eq!(prefill_entry.effective_value, "32");
4681 assert_eq!(prefill_entry.source, RuntimeConfigSource::Default);
4682 assert!(resolved
4683 .runtime_config
4684 .entries
4685 .iter()
4686 .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_STEP_CHUNK"));
4687 }
4688
4689 #[test]
4690 fn scheduler_prefill_step_chunk_override_is_reflected_in_decision_trace() {
4691 let resolved = m3(
4692 &[("FERRUM_SCHED_PREFILL_STEP_CHUNK", "128")],
4693 CompiledKernelFeatures::m3_fast_path_without_fa2(),
4694 )
4695 .resolve()
4696 .unwrap();
4697 let scheduler = resolved
4698 .decisions
4699 .iter()
4700 .find(|decision| decision.selection == "scheduler_admission_policy")
4701 .unwrap();
4702 assert_eq!(
4703 scheduler.selected,
4704 "prefill_first_until_active:32+prefill_step_chunk:128"
4705 );
4706 assert_eq!(scheduler.source, AutoConfigSource::Env);
4707 assert_eq!(
4708 scheduler.source_key.as_deref(),
4709 Some("FERRUM_SCHED_PREFILL_STEP_CHUNK")
4710 );
4711 let step_entry = resolved
4712 .runtime_config
4713 .entries
4714 .iter()
4715 .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_STEP_CHUNK")
4716 .expect("explicit prefill-step chunk should be preserved");
4717 assert_eq!(step_entry.effective_value, "128");
4718 assert_eq!(step_entry.source, RuntimeConfigSource::Env);
4719 }
4720
4721 #[test]
4722 fn cuda_gemma3_gptq_uses_generic_scheduler_default() {
4723 let hardware =
4724 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4725 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4726 let resolved = FerrumConfigBuilder::new(snapshot(&[]))
4727 .with_model_capabilities(gemma3_gptq_model())
4728 .with_hardware_capabilities(hardware)
4729 .with_workload_profile(workload)
4730 .resolve()
4731 .unwrap();
4732 let scheduler = resolved
4733 .decisions
4734 .iter()
4735 .find(|decision| decision.selection == "scheduler_admission_policy")
4736 .unwrap();
4737 assert_eq!(
4738 scheduler.selected,
4739 "prefill_first_until_active:32+prefill_token_budget:elastic"
4740 );
4741 assert_eq!(scheduler.source, AutoConfigSource::Default);
4742 assert!(resolved
4743 .runtime_config
4744 .entries
4745 .iter()
4746 .all(|entry| entry.key != "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK"));
4747 let entry = resolved
4748 .runtime_config
4749 .entries
4750 .iter()
4751 .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4752 .expect("generic scheduler default should be materialized");
4753 assert_eq!(entry.effective_value, "32");
4754 assert_eq!(entry.source, RuntimeConfigSource::Default);
4755 }
4756
4757 #[test]
4758 fn explicit_scheduler_prompt_estimate_is_reflected_for_gemma3() {
4759 let hardware =
4760 HardwareCapabilities::rtx4090_cuda(CompiledKernelFeatures::m3_fast_path_without_fa2());
4761 let workload = WorkloadProfile::serving_default_for_hardware(&hardware);
4762 let resolved =
4763 FerrumConfigBuilder::new(snapshot(&[("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE", "1")]))
4764 .with_model_capabilities(gemma3_gptq_model())
4765 .with_hardware_capabilities(hardware)
4766 .with_workload_profile(workload)
4767 .resolve()
4768 .unwrap();
4769 let scheduler = resolved
4770 .decisions
4771 .iter()
4772 .find(|decision| decision.selection == "scheduler_admission_policy")
4773 .unwrap();
4774 assert_eq!(scheduler.selected, "prompt_token_estimate");
4775 assert_eq!(
4776 scheduler.source_key.as_deref(),
4777 Some("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE")
4778 );
4779 assert!(resolved
4780 .runtime_config
4781 .entries
4782 .iter()
4783 .all(|entry| entry.key != "FERRUM_ACTIVE_DECODE_PREFILL_CHUNK"));
4784 }
4785
4786 #[test]
4787 fn scheduler_prefill_first_is_default_accelerator_policy() {
4788 let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
4789 .resolve()
4790 .unwrap();
4791 let scheduler = resolved
4792 .decisions
4793 .iter()
4794 .find(|decision| decision.selection == "scheduler_admission_policy")
4795 .unwrap();
4796 assert_eq!(
4797 scheduler.selected,
4798 "prefill_first_until_active:32+prefill_token_budget:elastic"
4799 );
4800 assert_eq!(scheduler.source, AutoConfigSource::Default);
4801 assert_eq!(scheduler.source_key, None);
4802 let entry = resolved
4803 .runtime_config
4804 .entries
4805 .iter()
4806 .find(|entry| entry.key == "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4807 .expect("generic scheduler default should be materialized");
4808 assert_eq!(entry.effective_value, "32");
4809 assert_eq!(entry.source, RuntimeConfigSource::Default);
4810 assert!(resolved
4811 .runtime_config
4812 .entries
4813 .iter()
4814 .all(|entry| entry.key != "FERRUM_SCHED_PREFILL_STEP_CHUNK"));
4815 }
4816
4817 #[test]
4818 fn scheduler_prompt_token_estimate_can_be_disabled_in_decision_trace() {
4819 let resolved = m3(
4820 &[("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE", "0")],
4821 CompiledKernelFeatures::m3_fast_path_without_fa2(),
4822 )
4823 .resolve()
4824 .unwrap();
4825 let scheduler = resolved
4826 .decisions
4827 .iter()
4828 .find(|decision| decision.selection == "scheduler_admission_policy")
4829 .unwrap();
4830 assert_eq!(scheduler.selected, "continuous_default");
4831 assert_eq!(scheduler.source, AutoConfigSource::Env);
4832 assert_eq!(
4833 scheduler.source_key.as_deref(),
4834 Some("FERRUM_SCHED_PROMPT_TOKEN_ESTIMATE")
4835 );
4836 }
4837
4838 #[test]
4839 fn prefix_cache_override_is_reflected_in_decision_trace() {
4840 let resolved = m3(
4841 &[("FERRUM_PREFIX_CACHE", "1")],
4842 CompiledKernelFeatures::m3_fast_path_without_fa2(),
4843 )
4844 .resolve()
4845 .unwrap();
4846 let prefix_cache = resolved
4847 .decisions
4848 .iter()
4849 .find(|decision| decision.selection == "prefix_cache_policy")
4850 .unwrap();
4851 assert_eq!(prefix_cache.selected, "prefix_cache_enabled");
4852 assert_eq!(
4853 prefix_cache.source_key.as_deref(),
4854 Some("FERRUM_PREFIX_CACHE")
4855 );
4856 }
4857
4858 #[test]
4859 fn non_env_runtime_sources_are_preserved_in_decision_trace() {
4860 let runtime_config = snapshot_with_sources(&[
4861 (
4862 "FERRUM_FA_LAYOUT_VARLEN",
4863 "1",
4864 RuntimeConfigSource::ConfigFile,
4865 ),
4866 ("FERRUM_PAGED_MAX_SEQS", "48", RuntimeConfigSource::Cli),
4867 (
4868 "FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE",
4869 "32",
4870 RuntimeConfigSource::ScriptCase,
4871 ),
4872 ]);
4873 let resolved = FerrumConfigBuilder::new(runtime_config)
4874 .with_model_capabilities(ModelCapabilities::qwen3_30b_a3b_gptq_int4())
4875 .with_hardware_capabilities(HardwareCapabilities::rtx4090_cuda(
4876 CompiledKernelFeatures::m3_fast_path_without_fa2(),
4877 ))
4878 .with_workload_profile(WorkloadProfile::m3_qwen3_30b_a3b_int4())
4879 .resolve()
4880 .unwrap();
4881
4882 let decision = |selection: &str| {
4883 resolved
4884 .decisions
4885 .iter()
4886 .find(|decision| decision.selection == selection)
4887 .unwrap()
4888 };
4889 let attention = decision("attention_prefill_mixed_backend");
4890 assert_eq!(attention.selected, "fa_layout_varlen");
4891 assert_eq!(attention.source, AutoConfigSource::ConfigFile);
4892 assert_eq!(
4893 attention.source_key.as_deref(),
4894 Some("FERRUM_FA_LAYOUT_VARLEN")
4895 );
4896
4897 let max_sequences = decision("max_sequences");
4898 assert_eq!(max_sequences.selected, "48");
4899 assert_eq!(max_sequences.source, AutoConfigSource::Cli);
4900 assert_eq!(
4901 max_sequences.source_key.as_deref(),
4902 Some("FERRUM_PAGED_MAX_SEQS")
4903 );
4904
4905 let scheduler = decision("scheduler_admission_policy");
4906 assert_eq!(
4907 scheduler.selected,
4908 "prefill_first_until_active:32+prefill_token_budget:elastic"
4909 );
4910 assert_eq!(scheduler.source, AutoConfigSource::ScriptCase);
4911 assert_eq!(
4912 scheduler.source_key.as_deref(),
4913 Some("FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE")
4914 );
4915 }
4916
4917 #[test]
4918 fn renders_effective_config_and_decision_trace_artifacts() {
4919 let resolved = m3(&[], CompiledKernelFeatures::m3_fast_path_without_fa2())
4920 .resolve()
4921 .unwrap();
4922 let effective = resolved.effective_config_document();
4923 assert_eq!(effective["schema_version"], 1);
4924 assert!(effective["env_hash"]
4925 .as_str()
4926 .unwrap()
4927 .starts_with("sha256:"));
4928 assert!(effective["entries"].is_array());
4929 assert_eq!(effective["model_capabilities"]["architecture"], "qwen3_moe");
4930 assert_eq!(effective["hardware_capabilities"]["backend"], "cuda");
4931 assert_eq!(
4932 effective["workload_profile"]["preset"],
4933 M3_QWEN3_30B_A3B_INT4_PRESET
4934 );
4935 assert_eq!(
4936 effective["decisions"].as_array().unwrap().len(),
4937 resolved.decisions.len()
4938 );
4939 let trace = resolved.decision_trace_jsonl().unwrap();
4940 assert_eq!(trace.lines().count(), resolved.decisions.len());
4941 assert!(trace.contains("\"attention_prefill_mixed_backend\""));
4942 }
4943
4944 #[test]
4945 fn auto_config_artifacts_match_locked_schema_shape() {
4946 let resolved = FerrumConfigBuilder::m3_qwen3_30b_a3b_int4(snapshot_with_sources(&[
4947 (
4948 "FERRUM_FA_LAYOUT_VARLEN",
4949 "1",
4950 RuntimeConfigSource::ScriptCase,
4951 ),
4952 ("FERRUM_PAGED_MAX_SEQS", "32", RuntimeConfigSource::Cli),
4953 ]))
4954 .resolve()
4955 .unwrap();
4956
4957 let effective = resolved.effective_config_document();
4958 assert_eq!(effective["schema_version"], 1);
4959 assert!(effective["env_hash"]
4960 .as_str()
4961 .unwrap()
4962 .starts_with("sha256:"));
4963
4964 let entries = effective["entries"].as_array().unwrap();
4965 let keys: Vec<_> = entries
4966 .iter()
4967 .map(|entry| entry["key"].as_str().unwrap())
4968 .collect();
4969 let mut sorted_keys = keys.clone();
4970 sorted_keys.sort_unstable();
4971 assert_eq!(keys, sorted_keys);
4972 for entry in entries {
4973 assert!(entry["key"].as_str().unwrap().starts_with("FERRUM_"));
4974 assert!(entry["effective_value"].is_string());
4975 assert!(matches!(
4976 entry["source"].as_str().unwrap(),
4977 "default" | "config_file" | "cli" | "env" | "script_case" | "memory_profile"
4978 ));
4979 assert!(!entry["affects"].as_array().unwrap().is_empty());
4980 }
4981 assert_eq!(
4982 effective["model_capabilities"]["quantization"].as_str(),
4983 Some("gptq_int4")
4984 );
4985 assert_eq!(
4986 effective["model_capabilities"]["moe"]["experts_per_token"].as_u64(),
4987 Some(8)
4988 );
4989 assert_eq!(
4990 effective["hardware_capabilities"]["compute_capability"].as_str(),
4991 Some("8.9")
4992 );
4993 assert_eq!(
4994 effective["hardware_capabilities"]["compiled_features"]["vllm_moe_marlin"].as_bool(),
4995 Some(true)
4996 );
4997 assert_eq!(
4998 effective["workload_profile"]["target_concurrency"].as_u64(),
4999 Some(32)
5000 );
5001 assert_eq!(
5002 effective["workload_profile"]["priority"].as_str(),
5003 Some("throughput")
5004 );
5005 let admission = &effective["admission"];
5006 for field in [
5007 "effective_max_concurrent",
5008 "queue_depth",
5009 "active_prefill",
5010 "active_decode",
5011 "current_batch_size",
5012 "rejected_requests_total",
5013 "failed_requests_total",
5014 "completed_requests_total",
5015 ] {
5016 assert!(admission[field].is_number(), "admission.{field} missing");
5017 }
5018
5019 let trace = resolved.decision_trace_jsonl().unwrap();
5020 let trace_decisions: Vec<AutoConfigDecision> = trace
5021 .lines()
5022 .map(|line| serde_json::from_str(line).unwrap())
5023 .collect();
5024 assert_eq!(trace_decisions, resolved.decisions);
5025 assert_eq!(
5026 serde_json::from_value::<Vec<AutoConfigDecision>>(effective["decisions"].clone())
5027 .unwrap(),
5028 trace_decisions
5029 );
5030
5031 for decision in &trace_decisions {
5032 assert_eq!(decision.schema_version, 1);
5033 assert!(!decision.selection.trim().is_empty());
5034 assert!(!decision.selected.trim().is_empty());
5035 assert!(!decision.candidates.is_empty());
5036 assert!(!decision.affects.is_empty());
5037 if let Some(source_key) = &decision.source_key {
5038 assert!(source_key.starts_with("FERRUM_"));
5039 }
5040 for rejected in &decision.rejected {
5041 assert!(!rejected.value.trim().is_empty());
5042 assert!(!rejected.reason.trim().is_empty());
5043 }
5044 }
5045 }
5046}