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