use ferrum_types::{
AttentionExecutionPolicy, Result, RuntimeConfigEntry, RuntimeConfigSource, SequenceFitPolicy,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use tokio::fs;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CliConfig {
pub server: ServerCliConfig,
pub models: ModelCliConfig,
pub benchmark: BenchmarkConfig,
pub client: ClientConfig,
pub dev: DevConfig,
#[serde(default)]
pub runtime: RuntimeCliConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerCliConfig {
pub host: String,
pub port: u16,
pub config_path: String,
pub log_level: String,
pub hot_reload: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelCliConfig {
pub model_dir: String,
pub cache_dir: String,
pub default_model: Option<String>,
pub aliases: HashMap<String, String>,
pub download: DownloadConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DownloadConfig {
pub hf_cache_dir: String,
pub timeout_seconds: u64,
pub max_concurrent: usize,
pub retry_attempts: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchmarkConfig {
pub num_requests: usize,
pub concurrency: usize,
pub prompt_length: usize,
pub max_tokens: usize,
pub warmup_requests: usize,
pub output_dir: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientConfig {
pub base_url: String,
pub api_key: Option<String>,
pub timeout_seconds: u64,
pub retry: RetryConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryConfig {
pub max_attempts: u32,
pub initial_delay_ms: u64,
pub max_delay_ms: u64,
pub backoff_multiplier: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevConfig {
pub debug: bool,
pub profile_memory: bool,
pub profile_gpu: bool,
pub mock_backends: bool,
pub test_data_dir: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RuntimeCliConfig {
#[serde(default)]
pub preset: Option<String>,
#[serde(default)]
pub kv_dtype: Option<String>,
#[serde(default)]
pub kv_max_blocks: Option<usize>,
#[serde(default)]
pub kv_capacity: Option<usize>,
#[serde(default)]
pub paged_max_seqs: Option<usize>,
#[serde(default)]
pub recurrent_state_max_slots: Option<usize>,
#[serde(default)]
pub attention_policy: Option<AttentionExecutionPolicy>,
#[serde(default)]
pub max_batched_tokens: Option<usize>,
#[serde(default)]
pub scheduler_prefill_first_until_active: Option<usize>,
#[serde(default)]
pub scheduler_active_decode_prefill_chunk: Option<usize>,
#[serde(default)]
pub prefix_cache: Option<bool>,
#[serde(default)]
pub layer_split_pipeline_mode: Option<String>,
#[serde(default)]
pub moe_graph: Option<bool>,
#[serde(default)]
pub batched_graph: Option<bool>,
#[serde(default)]
pub reusable_execution: Option<bool>,
#[serde(default)]
pub reusable_execution_exact_decode_widths: Option<Vec<usize>>,
#[serde(default)]
pub reusable_execution_max_automatic_exact_decode_width: Option<usize>,
#[serde(default)]
pub unified_graph: Option<bool>,
#[serde(default)]
pub unified_graph_layers_only: Option<bool>,
#[serde(default)]
pub unified_graph_lm_head_eager: Option<bool>,
#[serde(default)]
pub batch_decode_prof: Option<bool>,
#[serde(default)]
pub batch_prefill_prof: Option<bool>,
#[serde(default)]
pub next_batch_prof: Option<bool>,
#[serde(default)]
pub rbd_prof: Option<bool>,
#[serde(default)]
pub unified_post_prof: Option<bool>,
#[serde(default)]
pub decode_op_profile: Option<bool>,
#[serde(default)]
pub prefill_op_profile: Option<bool>,
#[serde(default)]
pub marlin_profile: Option<bool>,
#[serde(default)]
pub marlin_trace_shapes: Option<bool>,
#[serde(default)]
pub marlin_trace_shapes_max: Option<usize>,
#[serde(default)]
pub use_vllm_paged_attn: Option<bool>,
#[serde(default)]
pub vllm_paged_attn_v1_short: Option<bool>,
#[serde(default)]
pub vllm_moe: Option<bool>,
#[serde(default)]
pub vllm_moe_pair_ids: Option<bool>,
#[serde(default)]
pub greedy_argmax: Option<bool>,
#[serde(default)]
pub fa_layout_varlen: Option<bool>,
#[serde(default)]
pub fa2_source: Option<bool>,
#[serde(default)]
pub fa2_direct_ffi: Option<bool>,
#[serde(default)]
pub fa2_direct_ffi_shim: Option<String>,
#[serde(default)]
pub fa2_native_manifest: Option<String>,
#[serde(default)]
pub fa2_native_artifact: Option<String>,
#[serde(default)]
pub fa2_native_source_sha256: Option<String>,
#[serde(default)]
pub fa2_native_inputs_sha256: Option<String>,
#[serde(default)]
pub max_model_len: Option<usize>,
#[serde(default)]
pub sequence_fit_policy: Option<SequenceFitPolicy>,
#[serde(default)]
pub moe_batch_threshold: Option<usize>,
}
impl RuntimeCliConfig {
pub fn runtime_config_entries(&self) -> Vec<RuntimeConfigEntry> {
let mut entries = Vec::new();
push_string_entry(&mut entries, "FERRUM_KV_DTYPE", self.kv_dtype.as_deref());
push_usize_entry(&mut entries, "FERRUM_KV_MAX_BLOCKS", self.kv_max_blocks);
push_usize_entry(&mut entries, "FERRUM_KV_CAPACITY", self.kv_capacity);
push_usize_entry(&mut entries, "FERRUM_PAGED_MAX_SEQS", self.paged_max_seqs);
push_usize_entry(
&mut entries,
"FERRUM_RECURRENT_STATE_MAX_SLOTS",
self.recurrent_state_max_slots,
);
push_string_entry(
&mut entries,
"FERRUM_ATTENTION_POLICY",
self.attention_policy
.map(AttentionExecutionPolicy::as_runtime_value),
);
push_usize_entry(
&mut entries,
"FERRUM_MAX_BATCHED_TOKENS",
self.max_batched_tokens,
);
push_usize_entry(
&mut entries,
"FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE",
self.scheduler_prefill_first_until_active,
);
push_usize_entry(
&mut entries,
"FERRUM_ACTIVE_DECODE_PREFILL_CHUNK",
self.scheduler_active_decode_prefill_chunk,
);
push_bool_entry(&mut entries, "FERRUM_PREFIX_CACHE", self.prefix_cache);
push_string_entry(
&mut entries,
"FERRUM_LAYER_SPLIT_PIPELINE_MODE",
self.layer_split_pipeline_mode.as_deref(),
);
push_bool_entry(&mut entries, "FERRUM_MOE_GRAPH", self.moe_graph);
push_bool_entry(&mut entries, "FERRUM_BATCHED_GRAPH", self.batched_graph);
push_bool_entry(
&mut entries,
"FERRUM_REUSABLE_EXECUTION",
self.reusable_execution,
);
push_usize_list_entry(
&mut entries,
"FERRUM_REUSABLE_EXECUTION_EXACT_DECODE_WIDTHS",
self.reusable_execution_exact_decode_widths.as_deref(),
);
push_usize_entry(
&mut entries,
"FERRUM_REUSABLE_EXECUTION_MAX_AUTOMATIC_EXACT_DECODE_WIDTH",
self.reusable_execution_max_automatic_exact_decode_width,
);
push_bool_entry(&mut entries, "FERRUM_UNIFIED_GRAPH", self.unified_graph);
push_bool_entry(
&mut entries,
"FERRUM_UNIFIED_GRAPH_LAYERS_ONLY",
self.unified_graph_layers_only,
);
push_bool_entry(
&mut entries,
"FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER",
self.unified_graph_lm_head_eager,
);
push_true_entry(
&mut entries,
"FERRUM_BATCH_DECODE_PROF",
self.batch_decode_prof,
);
push_true_entry(
&mut entries,
"FERRUM_BATCH_PREFILL_PROF",
self.batch_prefill_prof,
);
push_true_entry(&mut entries, "FERRUM_NEXT_BATCH_PROF", self.next_batch_prof);
push_true_entry(&mut entries, "FERRUM_RBD_PROF", self.rbd_prof);
push_true_entry(
&mut entries,
"FERRUM_UNIFIED_POST_PROF",
self.unified_post_prof,
);
push_true_entry(
&mut entries,
"FERRUM_DECODE_OP_PROFILE",
self.decode_op_profile,
);
push_true_entry(
&mut entries,
"FERRUM_PREFILL_OP_PROFILE",
self.prefill_op_profile,
);
push_true_entry(&mut entries, "FERRUM_MARLIN_PROFILE", self.marlin_profile);
push_true_entry(
&mut entries,
"FERRUM_MARLIN_TRACE_SHAPES",
self.marlin_trace_shapes,
);
push_usize_entry(
&mut entries,
"FERRUM_MARLIN_TRACE_SHAPES_MAX",
self.marlin_trace_shapes_max,
);
push_bool_entry(
&mut entries,
"FERRUM_USE_VLLM_PAGED_ATTN",
self.use_vllm_paged_attn,
);
push_bool_entry(
&mut entries,
"FERRUM_VLLM_PAGED_ATTN_V1_SHORT",
self.vllm_paged_attn_v1_short,
);
push_bool_entry(&mut entries, "FERRUM_VLLM_MOE", self.vllm_moe);
push_bool_entry(
&mut entries,
"FERRUM_VLLM_MOE_PAIR_IDS",
self.vllm_moe_pair_ids,
);
push_bool_entry(&mut entries, "FERRUM_GREEDY_ARGMAX", self.greedy_argmax);
push_bool_entry(
&mut entries,
"FERRUM_FA_LAYOUT_VARLEN",
self.fa_layout_varlen,
);
push_bool_entry(&mut entries, "FERRUM_FA2_SOURCE", self.fa2_source);
push_bool_entry(&mut entries, "FERRUM_FA2_DIRECT_FFI", self.fa2_direct_ffi);
push_string_entry(
&mut entries,
"FERRUM_FA2_DIRECT_FFI_SHIM",
self.fa2_direct_ffi_shim.as_deref(),
);
push_string_entry(
&mut entries,
"FERRUM_FA2_NATIVE_MANIFEST",
self.fa2_native_manifest.as_deref(),
);
push_string_entry(
&mut entries,
"FERRUM_FA2_NATIVE_ARTIFACT",
self.fa2_native_artifact.as_deref(),
);
push_string_entry(
&mut entries,
"FERRUM_FA2_NATIVE_SOURCE_SHA256",
self.fa2_native_source_sha256.as_deref(),
);
push_string_entry(
&mut entries,
"FERRUM_FA2_NATIVE_INPUTS_SHA256",
self.fa2_native_inputs_sha256.as_deref(),
);
push_usize_entry(&mut entries, "FERRUM_MAX_MODEL_LEN", self.max_model_len);
push_string_entry(
&mut entries,
"FERRUM_SEQUENCE_FIT_POLICY",
self.sequence_fit_policy
.map(SequenceFitPolicy::as_runtime_value),
);
push_usize_entry(
&mut entries,
"FERRUM_MOE_BATCH_THRESHOLD",
self.moe_batch_threshold,
);
entries
}
}
fn push_string_entry(entries: &mut Vec<RuntimeConfigEntry>, key: &str, value: Option<&str>) {
if let Some(value) = value.filter(|value| !value.trim().is_empty()) {
entries.push(RuntimeConfigEntry::new(
key,
value.to_string(),
RuntimeConfigSource::ConfigFile,
));
}
}
fn push_usize_entry(entries: &mut Vec<RuntimeConfigEntry>, key: &str, value: Option<usize>) {
if let Some(value) = value {
entries.push(RuntimeConfigEntry::new(
key,
value.to_string(),
RuntimeConfigSource::ConfigFile,
));
}
}
fn push_usize_list_entry(
entries: &mut Vec<RuntimeConfigEntry>,
key: &str,
value: Option<&[usize]>,
) {
if let Some(value) = value {
entries.push(RuntimeConfigEntry::new(
key,
value
.iter()
.map(usize::to_string)
.collect::<Vec<_>>()
.join(","),
RuntimeConfigSource::ConfigFile,
));
}
}
fn push_bool_entry(entries: &mut Vec<RuntimeConfigEntry>, key: &str, value: Option<bool>) {
if let Some(value) = value {
entries.push(RuntimeConfigEntry::new(
key,
if value { "1" } else { "0" },
RuntimeConfigSource::ConfigFile,
));
}
}
fn push_true_entry(entries: &mut Vec<RuntimeConfigEntry>, key: &str, value: Option<bool>) {
if value == Some(true) {
entries.push(RuntimeConfigEntry::new(
key,
"1".to_string(),
RuntimeConfigSource::ConfigFile,
));
}
}
impl CliConfig {
pub async fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
if !path.exists() {
return Ok(Self::default());
}
let content = fs::read_to_string(path).await.map_err(|e| {
ferrum_types::FerrumError::io_str(format!("Failed to read config file: {}", e))
})?;
toml::from_str(&content).map_err(|e| {
ferrum_types::FerrumError::configuration(format!("Failed to parse config: {}", e))
})
}
pub async fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let content = toml::to_string_pretty(self).map_err(|e| {
ferrum_types::FerrumError::configuration(format!("Failed to serialize config: {}", e))
})?;
fs::write(path, content).await.map_err(|e| {
ferrum_types::FerrumError::io_str(format!("Failed to write config file: {}", e))
})
}
pub fn validate(&self) -> Result<()> {
if self.server.port == 0 {
return Err(ferrum_types::FerrumError::configuration(
"Server port cannot be 0".to_string(),
));
}
if !Path::new(&self.models.model_dir).exists() {
return Err(ferrum_types::FerrumError::configuration(format!(
"Model directory does not exist: {}",
self.models.model_dir
)));
}
if self.benchmark.num_requests == 0 {
return Err(ferrum_types::FerrumError::configuration(
"Number of requests cannot be 0".to_string(),
));
}
if self.benchmark.concurrency == 0 {
return Err(ferrum_types::FerrumError::configuration(
"Concurrency cannot be 0".to_string(),
));
}
Ok(())
}
}
impl Default for ServerCliConfig {
fn default() -> Self {
Self {
host: "127.0.0.1".to_string(),
port: 8000,
config_path: "server.toml".to_string(),
log_level: "info".to_string(),
hot_reload: false,
}
}
}
impl Default for ModelCliConfig {
fn default() -> Self {
Self {
model_dir: "./models".to_string(),
cache_dir: "./cache".to_string(),
default_model: None,
aliases: HashMap::new(),
download: DownloadConfig::default(),
}
}
}
impl Default for DownloadConfig {
fn default() -> Self {
Self {
hf_cache_dir: std::env::var("HF_HOME")
.ok()
.or_else(|| {
dirs::home_dir()
.map(|h| h.join(".cache/huggingface").to_string_lossy().to_string())
})
.unwrap_or_else(|| "./hf_cache".to_string()),
timeout_seconds: 300,
max_concurrent: 4,
retry_attempts: 3,
}
}
}
impl Default for BenchmarkConfig {
fn default() -> Self {
Self {
num_requests: 100,
concurrency: 10,
prompt_length: 512,
max_tokens: 256,
warmup_requests: 10,
output_dir: "./benchmark_results".to_string(),
}
}
}
impl Default for ClientConfig {
fn default() -> Self {
Self {
base_url: "http://127.0.0.1:8000".to_string(),
api_key: None,
timeout_seconds: 30,
retry: RetryConfig::default(),
}
}
}
impl Default for RetryConfig {
fn default() -> Self {
Self {
max_attempts: 3,
initial_delay_ms: 100,
max_delay_ms: 5000,
backoff_multiplier: 2.0,
}
}
}
impl Default for DevConfig {
fn default() -> Self {
Self {
debug: false,
profile_memory: false,
profile_gpu: false,
mock_backends: false,
test_data_dir: "./test_data".to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ferrum_types::RuntimeConfigEffect;
#[tokio::test]
async fn missing_optional_config_uses_defaults_without_creating_a_file() {
let nonce = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let path = std::env::temp_dir().join(format!(
"ferrum-missing-config-{}-{nonce}.toml",
std::process::id()
));
assert!(!path.exists());
let config = CliConfig::load(&path).await.unwrap();
assert!(
!path.exists(),
"loading an optional config must be read-only"
);
assert_eq!(config.server.port, 8000);
assert!(config.models.default_model.is_none());
}
#[test]
fn runtime_cli_config_emits_config_file_source_entries() {
let runtime = RuntimeCliConfig {
preset: Some("m3_qwen3_30b_a3b_int4".to_string()),
kv_dtype: Some("int8".to_string()),
kv_max_blocks: Some(4096),
kv_capacity: Some(2048),
paged_max_seqs: Some(64),
recurrent_state_max_slots: Some(16),
attention_policy: Some(AttentionExecutionPolicy::NativeAdaptive),
max_batched_tokens: Some(2048),
scheduler_prefill_first_until_active: Some(16),
scheduler_active_decode_prefill_chunk: Some(24),
prefix_cache: Some(false),
layer_split_pipeline_mode: Some("batch".to_string()),
moe_graph: Some(true),
batched_graph: Some(true),
reusable_execution: Some(false),
reusable_execution_exact_decode_widths: Some(vec![1, 2, 4, 8, 16, 24, 32]),
reusable_execution_max_automatic_exact_decode_width: Some(32),
unified_graph: Some(true),
unified_graph_layers_only: Some(true),
unified_graph_lm_head_eager: Some(true),
batch_decode_prof: Some(true),
batch_prefill_prof: Some(true),
next_batch_prof: Some(true),
rbd_prof: Some(true),
unified_post_prof: Some(true),
decode_op_profile: Some(true),
prefill_op_profile: Some(true),
marlin_profile: Some(true),
marlin_trace_shapes: Some(true),
marlin_trace_shapes_max: Some(17),
use_vllm_paged_attn: Some(true),
vllm_paged_attn_v1_short: Some(false),
vllm_moe: Some(true),
vllm_moe_pair_ids: Some(true),
greedy_argmax: Some(true),
fa_layout_varlen: Some(true),
fa2_source: Some(true),
fa2_direct_ffi: Some(false),
fa2_direct_ffi_shim: Some("/tmp/libferrum_fa2_shim.so".to_string()),
fa2_native_manifest: Some("/tmp/native/fa2/native_operator_manifest.json".to_string()),
fa2_native_artifact: Some("/tmp/native/fa2/libferrum_native_fa2.a".to_string()),
fa2_native_source_sha256: Some(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_string(),
),
fa2_native_inputs_sha256: Some(
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb".to_string(),
),
max_model_len: Some(4096),
sequence_fit_policy: Some(SequenceFitPolicy::FullInputMustFit),
moe_batch_threshold: Some(4),
..Default::default()
};
let entries = runtime.runtime_config_entries();
assert_eq!(entries.len(), 45);
let entry = |key: &str| {
entries
.iter()
.find(|entry| entry.key == key)
.unwrap_or_else(|| panic!("missing {key}"))
};
assert_eq!(entry("FERRUM_KV_DTYPE").effective_value, "int8");
assert_eq!(
entry("FERRUM_KV_DTYPE").source,
RuntimeConfigSource::ConfigFile
);
assert!(entry("FERRUM_KV_DTYPE")
.affects
.contains(&RuntimeConfigEffect::Correctness));
assert_eq!(entry("FERRUM_KV_MAX_BLOCKS").effective_value, "4096");
assert_eq!(entry("FERRUM_KV_CAPACITY").effective_value, "2048");
assert_eq!(entry("FERRUM_PAGED_MAX_SEQS").effective_value, "64");
assert_eq!(
entry("FERRUM_RECURRENT_STATE_MAX_SLOTS").effective_value,
"16"
);
assert!(entry("FERRUM_RECURRENT_STATE_MAX_SLOTS")
.affects
.contains(&RuntimeConfigEffect::Memory));
assert_eq!(
entry("FERRUM_ATTENTION_POLICY").effective_value,
"native-adaptive"
);
assert!(entry("FERRUM_ATTENTION_POLICY")
.affects
.contains(&RuntimeConfigEffect::Correctness));
assert!(entry("FERRUM_ATTENTION_POLICY")
.affects
.contains(&RuntimeConfigEffect::Performance));
assert_eq!(entry("FERRUM_MAX_BATCHED_TOKENS").effective_value, "2048");
assert_eq!(
entry("FERRUM_SCHED_PREFILL_FIRST_UNTIL_ACTIVE").effective_value,
"16"
);
assert_eq!(
entry("FERRUM_ACTIVE_DECODE_PREFILL_CHUNK").effective_value,
"24"
);
assert_eq!(
entry("FERRUM_LAYER_SPLIT_PIPELINE_MODE").effective_value,
"batch"
);
assert_eq!(entry("FERRUM_PREFIX_CACHE").effective_value, "0");
assert_eq!(entry("FERRUM_MOE_GRAPH").effective_value, "1");
assert_eq!(entry("FERRUM_BATCHED_GRAPH").effective_value, "1");
assert_eq!(entry("FERRUM_REUSABLE_EXECUTION").effective_value, "0");
assert_eq!(
entry("FERRUM_REUSABLE_EXECUTION_EXACT_DECODE_WIDTHS").effective_value,
"1,2,4,8,16,24,32"
);
assert_eq!(
entry("FERRUM_REUSABLE_EXECUTION_MAX_AUTOMATIC_EXACT_DECODE_WIDTH").effective_value,
"32"
);
assert_eq!(entry("FERRUM_UNIFIED_GRAPH").effective_value, "1");
assert_eq!(
entry("FERRUM_UNIFIED_GRAPH_LAYERS_ONLY").effective_value,
"1"
);
assert_eq!(
entry("FERRUM_UNIFIED_GRAPH_LM_HEAD_EAGER").effective_value,
"1"
);
assert_eq!(entry("FERRUM_BATCH_DECODE_PROF").effective_value, "1");
assert!(entry("FERRUM_BATCH_DECODE_PROF")
.affects
.contains(&RuntimeConfigEffect::Diagnostics));
assert_eq!(entry("FERRUM_BATCH_PREFILL_PROF").effective_value, "1");
assert_eq!(entry("FERRUM_NEXT_BATCH_PROF").effective_value, "1");
assert_eq!(entry("FERRUM_RBD_PROF").effective_value, "1");
assert_eq!(entry("FERRUM_UNIFIED_POST_PROF").effective_value, "1");
assert_eq!(entry("FERRUM_DECODE_OP_PROFILE").effective_value, "1");
assert_eq!(entry("FERRUM_PREFILL_OP_PROFILE").effective_value, "1");
assert_eq!(entry("FERRUM_MARLIN_PROFILE").effective_value, "1");
assert!(entry("FERRUM_MARLIN_PROFILE")
.affects
.contains(&RuntimeConfigEffect::Diagnostics));
assert_eq!(entry("FERRUM_MARLIN_TRACE_SHAPES").effective_value, "1");
assert_eq!(
entry("FERRUM_MARLIN_TRACE_SHAPES_MAX").effective_value,
"17"
);
assert_eq!(entry("FERRUM_USE_VLLM_PAGED_ATTN").effective_value, "1");
assert_eq!(
entry("FERRUM_VLLM_PAGED_ATTN_V1_SHORT").effective_value,
"0"
);
assert_eq!(entry("FERRUM_VLLM_MOE").effective_value, "1");
assert_eq!(entry("FERRUM_VLLM_MOE_PAIR_IDS").effective_value, "1");
assert_eq!(entry("FERRUM_GREEDY_ARGMAX").effective_value, "1");
assert_eq!(entry("FERRUM_FA_LAYOUT_VARLEN").effective_value, "1");
assert_eq!(entry("FERRUM_FA2_SOURCE").effective_value, "1");
assert_eq!(entry("FERRUM_FA2_DIRECT_FFI").effective_value, "0");
assert_eq!(
entry("FERRUM_FA2_DIRECT_FFI_SHIM").effective_value,
"/tmp/libferrum_fa2_shim.so"
);
assert_eq!(
entry("FERRUM_FA2_NATIVE_MANIFEST").effective_value,
"/tmp/native/fa2/native_operator_manifest.json"
);
assert_eq!(
entry("FERRUM_FA2_NATIVE_ARTIFACT").effective_value,
"/tmp/native/fa2/libferrum_native_fa2.a"
);
assert_eq!(
entry("FERRUM_FA2_NATIVE_SOURCE_SHA256").effective_value,
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
);
assert_eq!(
entry("FERRUM_FA2_NATIVE_INPUTS_SHA256").effective_value,
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
);
assert_eq!(entry("FERRUM_MAX_MODEL_LEN").effective_value, "4096");
assert_eq!(
entry("FERRUM_SEQUENCE_FIT_POLICY").effective_value,
"full-input-must-fit"
);
assert_eq!(entry("FERRUM_MOE_BATCH_THRESHOLD").effective_value, "4");
}
#[test]
fn runtime_cli_config_diagnostic_presence_flags_are_opt_in() {
let entries = RuntimeCliConfig {
batch_decode_prof: Some(false),
batch_prefill_prof: Some(false),
next_batch_prof: Some(false),
rbd_prof: Some(false),
unified_post_prof: Some(false),
decode_op_profile: Some(false),
prefill_op_profile: Some(false),
marlin_profile: Some(false),
marlin_trace_shapes: Some(false),
..Default::default()
}
.runtime_config_entries();
assert!(
entries.is_empty(),
"false diagnostic presence flags must not materialize as FERRUM_*_PROF=0"
);
}
#[test]
fn runtime_cli_config_defaults_when_missing_from_toml() {
let config: CliConfig = toml::from_str(
r#"
[server]
host = "127.0.0.1"
port = 8000
config_path = "server.toml"
log_level = "info"
hot_reload = false
[models]
model_dir = "./models"
cache_dir = "./cache"
[models.aliases]
[models.download]
hf_cache_dir = "./hf_cache"
timeout_seconds = 300
max_concurrent = 4
retry_attempts = 3
[benchmark]
num_requests = 100
concurrency = 10
prompt_length = 512
max_tokens = 256
warmup_requests = 10
output_dir = "./benchmark_results"
[client]
base_url = "http://127.0.0.1:8000"
timeout_seconds = 30
[client.retry]
max_attempts = 3
initial_delay_ms = 100
max_delay_ms = 5000
backoff_multiplier = 2.0
[dev]
debug = false
profile_memory = false
profile_gpu = false
mock_backends = false
test_data_dir = "./test_data"
"#,
)
.unwrap();
assert!(config.runtime.preset.is_none());
assert!(config.runtime.kv_dtype.is_none());
assert!(config.runtime.runtime_config_entries().is_empty());
}
}