1use serde::{Deserialize, Serialize};
8use std::sync::RwLock;
9use std::{collections::BTreeMap, path::PathBuf};
10
11static ACTIVE_SNAPSHOT: RwLock<Option<RuntimeConfigSnapshot>> = RwLock::new(None);
21
22pub fn install_runtime_snapshot(snapshot: RuntimeConfigSnapshot) {
24 *ACTIVE_SNAPSHOT
25 .write()
26 .expect("runtime snapshot lock poisoned") = Some(snapshot);
27}
28
29pub fn active_runtime_snapshot() -> RuntimeConfigSnapshot {
32 ACTIVE_SNAPSHOT
33 .read()
34 .expect("runtime snapshot lock poisoned")
35 .clone()
36 .unwrap_or_default()
37}
38
39#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
41pub struct RuntimeConfigSnapshot {
42 pub entries: Vec<RuntimeConfigEntry>,
44}
45
46impl RuntimeConfigSnapshot {
47 pub fn capture_current() -> Self {
49 Self::from_env_vars(std::env::vars())
50 }
51
52 pub fn from_env_vars<I, K, V>(vars: I) -> Self
54 where
55 I: IntoIterator<Item = (K, V)>,
56 K: Into<String>,
57 V: Into<String>,
58 {
59 let mut sorted = BTreeMap::new();
60 for (key, value) in vars {
61 let key = key.into();
62 if key.starts_with("FERRUM_") {
63 sorted.insert(key, value.into());
64 }
65 }
66
67 Self {
68 entries: sorted
69 .into_iter()
70 .map(|(key, effective_value)| RuntimeConfigEntry {
71 affects: infer_effects(&key),
72 key,
73 effective_value,
74 source: RuntimeConfigSource::Env,
75 })
76 .collect(),
77 }
78 }
79
80 pub fn from_entries<I>(entries: I) -> Self
83 where
84 I: IntoIterator<Item = RuntimeConfigEntry>,
85 {
86 let mut sorted = BTreeMap::new();
87 for entry in entries {
88 sorted.insert(entry.key.clone(), entry);
89 }
90 Self {
91 entries: sorted.into_values().collect(),
92 }
93 }
94
95 pub fn upsert(
97 &mut self,
98 key: impl Into<String>,
99 effective_value: impl Into<String>,
100 source: RuntimeConfigSource,
101 ) {
102 self.upsert_entry(RuntimeConfigEntry::new(key, effective_value, source));
103 }
104
105 pub fn upsert_entry(&mut self, entry: RuntimeConfigEntry) {
107 let mut entries = std::mem::take(&mut self.entries);
108 entries.retain(|existing| existing.key != entry.key);
109 entries.push(entry);
110 *self = Self::from_entries(entries);
111 }
112
113 pub fn with_entry(
115 mut self,
116 key: impl Into<String>,
117 effective_value: impl Into<String>,
118 source: RuntimeConfigSource,
119 ) -> Self {
120 self.upsert(key, effective_value, source);
121 self
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub struct RuntimeConfigEntry {
128 pub key: String,
129 pub effective_value: String,
130 pub source: RuntimeConfigSource,
131 pub affects: Vec<RuntimeConfigEffect>,
132}
133
134impl RuntimeConfigEntry {
135 pub fn new(
136 key: impl Into<String>,
137 effective_value: impl Into<String>,
138 source: RuntimeConfigSource,
139 ) -> Self {
140 let key = key.into();
141 Self {
142 affects: infer_effects(&key),
143 key,
144 effective_value: effective_value.into(),
145 source,
146 }
147 }
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
152#[serde(rename_all = "snake_case")]
153pub enum RuntimeConfigSource {
154 Default,
155 ConfigFile,
156 Cli,
157 Env,
158 ScriptCase,
159 MemoryProfile,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
164#[serde(rename_all = "snake_case")]
165pub enum RuntimeConfigEffect {
166 Correctness,
167 Performance,
168 Memory,
169 Diagnostics,
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
174#[serde(rename_all = "snake_case")]
175pub enum EnvTriState {
176 Default,
177 ForcedOff,
178 ForcedOn,
179}
180
181pub fn parse_bool_env_value(raw: &str) -> Result<bool, String> {
182 match raw.trim().to_ascii_lowercase().as_str() {
183 "1" | "true" | "yes" | "on" => Ok(true),
184 "0" | "false" | "no" | "off" => Ok(false),
185 other => Err(format!("invalid boolean env value: {other:?}")),
186 }
187}
188
189pub fn parse_usize_env_value(raw: &str) -> Result<usize, String> {
190 raw.trim()
191 .parse::<usize>()
192 .map_err(|_| format!("invalid integer env value: {raw:?}"))
193}
194
195pub fn parse_path_env_value(raw: &str) -> Result<PathBuf, String> {
196 let trimmed = raw.trim();
197 if trimmed.is_empty() {
198 return Err("path env value must not be empty".to_string());
199 }
200 Ok(PathBuf::from(trimmed))
201}
202
203pub fn parse_tri_state_env_value(raw: Option<&str>) -> Result<EnvTriState, String> {
204 let Some(raw) = raw else {
205 return Ok(EnvTriState::Default);
206 };
207 if raw.trim().is_empty() {
208 return Ok(EnvTriState::Default);
209 }
210 Ok(if parse_bool_env_value(raw)? {
211 EnvTriState::ForcedOn
212 } else {
213 EnvTriState::ForcedOff
214 })
215}
216
217fn infer_effects(key: &str) -> Vec<RuntimeConfigEffect> {
218 let mut effects = Vec::new();
219
220 if key.contains("DIAG")
221 || key.contains("PROF")
222 || key.contains("TRACE")
223 || key.contains("DUMP")
224 || key.contains("LOG_CONFIG")
225 || key.contains("CAPTURE")
226 || key.contains("DEBUG")
227 {
228 effects.push(RuntimeConfigEffect::Diagnostics);
229 }
230
231 if key.contains("KV")
232 || key.contains("BATCHED_TOKENS")
233 || key.contains("PAGED_MAX_SEQS")
234 || key.contains("MODEL_LEN")
235 || key.contains("FIT_POLICY")
236 || key.contains("STATE_MAX_SLOTS")
237 || key.contains("REUSABLE_EXECUTION")
238 || key.contains("MEMORY")
239 {
240 effects.push(RuntimeConfigEffect::Memory);
241 }
242
243 if key.contains("PREFIX_CACHE")
244 || key.contains("MODEL_PATH")
245 || key.contains("MODEL_LEN")
246 || key.contains("FIT_POLICY")
247 || key.contains("RUNTIME_MEMORY_BUDGET")
248 || key.contains("NATIVE")
249 || key.contains("ARTIFACT")
250 || key.contains("SPEC_")
251 || key.contains("REF_")
252 || key.contains("DTYPE")
253 || key.contains("ATTENTION_POLICY")
254 || key.contains("REUSABLE_EXECUTION")
255 {
256 effects.push(RuntimeConfigEffect::Correctness);
257 }
258
259 if effects.is_empty()
260 || key.contains("MOE")
261 || key.contains("VLLM")
262 || key.contains("MARLIN")
263 || key.contains("PAGED")
264 || key.contains("GRAPH")
265 || key.contains("SCHED")
266 || key.contains("BATCH")
267 || key.contains("ATTN")
268 || key.contains("ATTENTION_POLICY")
269 || key.contains("FLASH")
270 || key.contains("CUDA")
271 || key.contains("TRITON")
272 || key.contains("GREEDY")
273 || key.contains("REUSABLE_EXECUTION")
274 || key.contains("FA")
275 {
276 effects.push(RuntimeConfigEffect::Performance);
277 }
278
279 effects.sort();
280 effects.dedup();
281 effects
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
289 fn parses_boolean_values() {
290 assert_eq!(parse_bool_env_value("1").unwrap(), true);
291 assert_eq!(parse_bool_env_value("off").unwrap(), false);
292 assert!(parse_bool_env_value("maybe").is_err());
293 }
294
295 #[test]
296 fn parses_integer_values() {
297 assert_eq!(parse_usize_env_value("4096").unwrap(), 4096);
298 assert!(parse_usize_env_value("-1").is_err());
299 assert!(parse_usize_env_value("many").is_err());
300 }
301
302 #[test]
303 fn parses_path_values() {
304 assert_eq!(
305 parse_path_env_value("/tmp/model").unwrap(),
306 PathBuf::from("/tmp/model")
307 );
308 assert!(parse_path_env_value(" ").is_err());
309 }
310
311 #[test]
312 fn parses_tri_state_values() {
313 assert_eq!(
314 parse_tri_state_env_value(None).unwrap(),
315 EnvTriState::Default
316 );
317 assert_eq!(
318 parse_tri_state_env_value(Some("0")).unwrap(),
319 EnvTriState::ForcedOff
320 );
321 assert_eq!(
322 parse_tri_state_env_value(Some("on")).unwrap(),
323 EnvTriState::ForcedOn
324 );
325 assert!(parse_tri_state_env_value(Some("auto")).is_err());
326 }
327
328 #[test]
329 fn attention_policy_is_a_correctness_and_performance_input() {
330 let snapshot =
331 RuntimeConfigSnapshot::from_env_vars([("FERRUM_ATTENTION_POLICY", "native-adaptive")]);
332 let entry = snapshot.entries.first().expect("attention policy entry");
333 assert!(entry.affects.contains(&RuntimeConfigEffect::Correctness));
334 assert!(entry.affects.contains(&RuntimeConfigEffect::Performance));
335 }
336
337 #[test]
338 fn snapshot_is_sorted_and_classified() {
339 let snapshot = RuntimeConfigSnapshot::from_env_vars([
340 ("OTHER_ENV", "ignored"),
341 ("FERRUM_FA2_NATIVE_ARTIFACT", "/tmp/libferrum_native_fa2.a"),
342 ("FERRUM_PREFIX_CACHE", "1"),
343 ("FERRUM_MOE_GRAPH", "1"),
344 ("FERRUM_REUSABLE_EXECUTION", "1"),
345 ]);
346 let keys: Vec<_> = snapshot
347 .entries
348 .iter()
349 .map(|entry| entry.key.as_str())
350 .collect();
351 assert_eq!(
352 keys,
353 vec![
354 "FERRUM_FA2_NATIVE_ARTIFACT",
355 "FERRUM_MOE_GRAPH",
356 "FERRUM_PREFIX_CACHE",
357 "FERRUM_REUSABLE_EXECUTION"
358 ]
359 );
360 assert_eq!(snapshot.entries[0].source, RuntimeConfigSource::Env);
361 assert!(snapshot.entries[0]
362 .affects
363 .contains(&RuntimeConfigEffect::Correctness));
364 assert!(snapshot.entries[0]
365 .affects
366 .contains(&RuntimeConfigEffect::Performance));
367 assert!(snapshot.entries[1]
368 .affects
369 .contains(&RuntimeConfigEffect::Performance));
370 assert!(snapshot.entries[2]
371 .affects
372 .contains(&RuntimeConfigEffect::Correctness));
373 for effect in [
374 RuntimeConfigEffect::Correctness,
375 RuntimeConfigEffect::Performance,
376 RuntimeConfigEffect::Memory,
377 ] {
378 assert!(snapshot.entries[3].affects.contains(&effect));
379 }
380 }
381
382 #[test]
383 fn upsert_preserves_non_env_source_and_stable_order() {
384 let mut snapshot = RuntimeConfigSnapshot::from_env_vars([
385 ("FERRUM_KV_DTYPE", "fp16"),
386 ("FERRUM_MOE_GRAPH", "1"),
387 ]);
388 snapshot.upsert("FERRUM_KV_DTYPE", "int8", RuntimeConfigSource::Cli);
389 snapshot.upsert(
390 "FERRUM_PROFILE_JSONL",
391 "/tmp/profile.jsonl",
392 RuntimeConfigSource::Cli,
393 );
394
395 let keys: Vec<_> = snapshot
396 .entries
397 .iter()
398 .map(|entry| entry.key.as_str())
399 .collect();
400 assert_eq!(
401 keys,
402 [
403 "FERRUM_KV_DTYPE",
404 "FERRUM_MOE_GRAPH",
405 "FERRUM_PROFILE_JSONL"
406 ]
407 );
408 let kv = snapshot
409 .entries
410 .iter()
411 .find(|entry| entry.key == "FERRUM_KV_DTYPE")
412 .unwrap();
413 assert_eq!(kv.effective_value, "int8");
414 assert_eq!(kv.source, RuntimeConfigSource::Cli);
415 assert!(kv.affects.contains(&RuntimeConfigEffect::Correctness));
416
417 let profile = snapshot
418 .entries
419 .iter()
420 .find(|entry| entry.key == "FERRUM_PROFILE_JSONL")
421 .unwrap();
422 assert_eq!(profile.source, RuntimeConfigSource::Cli);
423 assert!(profile.affects.contains(&RuntimeConfigEffect::Diagnostics));
424 }
425}