rusty_stack/installers/components/
mod.rs1pub mod aiter;
63pub mod amdgpu_drivers;
64pub mod bitsandbytes_multi;
65pub mod comfyui;
66pub mod deepspeed;
67pub mod fastvideo;
68pub mod flash_attention_ck;
69pub mod llama_cpp;
70pub mod megatron;
71pub mod migraphx_multi;
72pub mod migraphx_python;
73pub mod ml_stack;
74pub mod mpi4py;
75pub mod onnxruntime;
76pub mod permanent_env;
77pub mod pytorch;
78pub mod pytorch_profiler;
79pub mod repair;
80pub mod rocm;
81pub mod rocm_smi;
82pub mod textgen;
83pub mod triton;
84pub mod vllm_multi;
85pub mod vllm_studio;
86pub mod wandb;
87
88pub use aiter::{AiterConfig, AiterInstaller};
90pub use amdgpu_drivers::{AmdgpuConfig, AmdgpuInstaller};
91pub use bitsandbytes_multi::{BitsAndBytesConfig, BitsAndBytesInstaller};
92pub use comfyui::{ComfyuiConfig, ComfyuiInstaller};
93pub use deepspeed::{DeepSpeedConfig, DeepSpeedInstaller};
94pub use flash_attention_ck::{FlashAttentionConfig, FlashAttentionInstaller, GpuArch};
95pub use llama_cpp::{LlamaCppConfig, LlamaCppInstaller};
96pub use megatron::{MegatronConfig, MegatronInstaller};
97pub use migraphx_multi::{MigraphxConfig, MigraphxInstaller, MigraphxSupport};
98pub use migraphx_python::{
99 InstallMethod as MigraphxPythonInstallMethod, MigraphxPythonConfig, MigraphxPythonInstaller,
100};
101pub use ml_stack::{MlStackConfig, MlStackInstaller};
102pub use mpi4py::{Mpi4PyConfig, Mpi4PyInstaller, MpiImplementation};
103pub use onnxruntime::{HipArchs, OnnxRuntimeConfig, OnnxRuntimeInstaller};
104pub use permanent_env::{PermanentEnvConfig, PermanentEnvInstaller};
105pub use pytorch::{PyTorchConfig, PyTorchInstaller, TorchChannel};
106pub use pytorch_profiler::{PytorchProfilerConfig, PytorchProfilerInstaller};
107pub use repair::{RepairConfig, RepairInstaller, RepairResult, RepairStep};
108pub use rocm::{RocmChannel, RocmConfig, RocmInstallType, RocmInstaller};
109pub use rocm_smi::{RocmSmiConfig, RocmSmiInstaller};
110pub use textgen::{TextgenConfig, TextgenInstaller};
111pub use triton::{TritonBranch, TritonConfig, TritonInstaller};
112pub use vllm_multi::{VllmConfig, VllmInstaller};
113pub use vllm_studio::{VllmStudioConfig, VllmStudioInstaller};
114pub use wandb::{WandbConfig, WandbInstaller};
115
116pub const NATIVE_COMPONENT_IDS: &[&str] = &[
129 "permanent-env",
130 "rocm",
131 "pytorch",
132 "triton",
133 "mpi4py",
134 "deepspeed",
135 "ml-stack-core",
136 "flash-attn",
137 "repair-stack",
138 "megatron",
139 "vllm",
140 "aiter",
141 "vllm-studio",
142 "comfyui",
143 "textgen",
144 "onnx",
145 "bitsandbytes",
146 "rocm-smi",
147 "migraphx",
148 "pytorch-profiler",
149 "wandb",
150 "amdgpu-drivers",
152 "migraphx-python",
153 "enhanced-env",
154 "mlperf-inference",
156 "rocm-benchmarks",
157 "gpu-memory-bandwidth",
158 "rocm-smi-bench",
159 "pytorch-performance",
160 "vllm-performance",
161 "deepspeed-performance",
162 "megatron-performance",
163 "all-benchmarks",
164 "fastvideo",
166 "llama-cpp",
168];
169
170pub fn is_native_component(component_id: &str) -> bool {
179 NATIVE_COMPONENT_IDS.contains(&component_id)
180}
181
182pub fn get_dependencies(component_id: &str) -> &'static [&'static str] {
198 match component_id {
199 "megatron" => &["pytorch", "mpi4py"],
200 "vllm" => &["pytorch"],
201 "aiter" => &["pytorch", "rocm"],
202 "flash-attn" => &["pytorch", "rocm"],
203 "onnx" => &["rocm"],
204 "deepspeed" => &["pytorch"],
205 "comfyui" => &["pytorch"],
206 "pytorch" => &["rocm"], "pytorch-profiler" => &["pytorch"],
208 "llama-cpp" => &["rocm"], _ => &[],
211 }
212}
213
214pub fn topological_sort(component_ids: &[String]) -> Result<Vec<String>, String> {
228 use std::collections::{HashMap, HashSet, VecDeque};
229
230 let id_set: HashSet<&str> = component_ids.iter().map(|s| s.as_str()).collect();
231
232 let mut adj: HashMap<&str, Vec<&str>> = HashMap::new();
234 let mut in_degree: HashMap<&str, usize> = HashMap::new();
235
236 for id in component_ids.iter().map(|s| s.as_str()) {
237 in_degree.entry(id).or_insert(0);
238 adj.entry(id).or_default();
239 }
240
241 for id in component_ids.iter().map(|s| s.as_str()) {
242 for dep in get_dependencies(id) {
243 if id_set.contains(dep) {
244 adj.entry(dep).or_default().push(id);
246 *in_degree.entry(id).or_insert(0) += 1;
247 }
248 }
249 }
250
251 let mut queue: VecDeque<&str> = VecDeque::new();
252 for (&id, °ree) in &in_degree {
253 if degree == 0 {
254 queue.push_back(id);
255 }
256 }
257
258 let mut sorted = Vec::with_capacity(component_ids.len());
259 while let Some(id) = queue.pop_front() {
260 sorted.push(id.to_string());
261 if let Some(dependents) = adj.get(id) {
262 for &dependent in dependents {
263 if let Some(degree) = in_degree.get_mut(dependent) {
264 *degree -= 1;
265 if *degree == 0 {
266 queue.push_back(dependent);
267 }
268 }
269 }
270 }
271 }
272
273 if sorted.len() != component_ids.len() {
274 let remaining: Vec<&str> = in_degree
275 .iter()
276 .filter(|(_, &d)| d > 0)
277 .map(|(&id, _)| id)
278 .collect();
279 return Err(format!(
280 "Dependency cycle detected among: {}",
281 remaining.join(", ")
282 ));
283 }
284
285 Ok(sorted)
286}
287
288#[cfg(test)]
289mod dispatch_tests {
290 use super::*;
291
292 #[test]
293 fn test_all_24_native_components_listed() {
294 assert_eq!(NATIVE_COMPONENT_IDS.len(), 35);
296 }
297
298 #[test]
299 fn test_is_native_component_true_for_ported() {
300 for id in NATIVE_COMPONENT_IDS {
301 assert!(is_native_component(id), "Expected '{}' to be native", id);
302 }
303 }
304
305 #[test]
306 fn test_is_native_component_false_for_verification() {
307 assert!(!is_native_component("verify-basic"));
308 assert!(!is_native_component("verify-enhanced"));
309 assert!(!is_native_component("verify-build"));
310 }
311
312 #[test]
313 fn test_is_native_component_true_for_performance() {
314 assert!(is_native_component("mlperf-inference"));
315 assert!(is_native_component("rocm-benchmarks"));
316 assert!(is_native_component("all-benchmarks"));
317 assert!(is_native_component("fastvideo"));
318 }
319
320 #[test]
321 fn test_is_native_component_false_for_unknown() {
322 assert!(!is_native_component("nonexistent"));
323 assert!(!is_native_component(""));
324 }
325
326 #[test]
327 fn test_megatron_dependencies() {
328 let deps = get_dependencies("megatron");
329 assert!(deps.contains(&"pytorch"));
330 assert!(deps.contains(&"mpi4py"));
331 }
332
333 #[test]
334 fn test_vllm_dependencies() {
335 let deps = get_dependencies("vllm");
336 assert!(deps.contains(&"pytorch"));
337 }
338
339 #[test]
340 fn test_deepspeed_dependencies() {
341 let deps = get_dependencies("deepspeed");
342 assert!(deps.contains(&"pytorch"));
343 }
344
345 #[test]
346 fn test_flash_attention_dependencies() {
347 let deps = get_dependencies("flash-attn");
348 assert!(deps.contains(&"pytorch"));
349 assert!(deps.contains(&"rocm"));
350 }
351
352 #[test]
353 fn test_onnx_dependencies() {
354 let deps = get_dependencies("onnx");
355 assert!(deps.contains(&"rocm"));
356 }
357
358 #[test]
359 fn test_aiter_dependencies() {
360 let deps = get_dependencies("aiter");
361 assert!(deps.contains(&"pytorch"));
362 assert!(deps.contains(&"rocm"));
363 }
364
365 #[test]
366 fn test_comfyui_dependencies() {
367 let deps = get_dependencies("comfyui");
368 assert!(deps.contains(&"pytorch"));
369 }
370
371 #[test]
372 fn test_llama_cpp_dependencies() {
373 let deps = get_dependencies("llama-cpp");
374 assert!(deps.contains(&"rocm"), "llama-cpp must depend on ROCm");
375 }
376
377 #[test]
378 fn test_no_deps_components() {
379 for id in &[
380 "rocm",
381 "triton",
382 "mpi4py",
383 "ml-stack-core",
384 "permanent-env",
385 "rocm-smi",
386 "bitsandbytes",
387 "wandb",
388 "migraphx",
389 "vllm-studio",
390 "textgen",
391 "repair-stack",
392 "amdgpu-drivers",
393 "migraphx-python",
394 ] {
395 assert!(
396 get_dependencies(id).is_empty(),
397 "Expected no dependencies for '{}', got {:?}",
398 id,
399 get_dependencies(id)
400 );
401 }
402 }
403
404 #[test]
405 fn test_pytorch_depends_on_rocm() {
406 let deps = get_dependencies("pytorch");
407 assert!(
408 deps.contains(&"rocm"),
409 "PyTorch for ROCm must depend on ROCm"
410 );
411 }
412
413 #[test]
414 fn test_topological_sort_empty() {
415 let result = topological_sort(&[]);
416 assert!(result.is_ok());
417 assert!(result.unwrap().is_empty());
418 }
419
420 #[test]
421 fn test_topological_sort_no_deps() {
422 let ids = vec![
423 "rocm".to_string(),
424 "pytorch".to_string(),
425 "triton".to_string(),
426 ];
427 let result = topological_sort(&ids);
428 assert!(result.is_ok());
429 assert_eq!(result.unwrap().len(), 3);
430 }
431
432 #[test]
433 fn test_topological_sort_respects_dependencies() {
434 let ids = vec![
435 "megatron".to_string(),
436 "pytorch".to_string(),
437 "mpi4py".to_string(),
438 ];
439 let result = topological_sort(&ids).unwrap();
440 let pytorch_pos = result.iter().position(|s| s == "pytorch").unwrap();
441 let mpi4py_pos = result.iter().position(|s| s == "mpi4py").unwrap();
442 let megatron_pos = result.iter().position(|s| s == "megatron").unwrap();
443 assert!(
444 pytorch_pos < megatron_pos,
445 "pytorch must come before megatron"
446 );
447 assert!(
448 mpi4py_pos < megatron_pos,
449 "mpi4py must come before megatron"
450 );
451 }
452
453 #[test]
454 fn test_topological_sort_complex_graph() {
455 let ids = vec![
456 "megatron".to_string(),
457 "flash-attn".to_string(),
458 "deepspeed".to_string(),
459 "vllm".to_string(),
460 "aiter".to_string(),
461 "onnx".to_string(),
462 "comfyui".to_string(),
463 "pytorch".to_string(),
464 "rocm".to_string(),
465 "mpi4py".to_string(),
466 "triton".to_string(),
467 ];
468 let result = topological_sort(&ids).unwrap();
469 assert_eq!(result.len(), 11);
470
471 let pos = |id: &str| result.iter().position(|s| s == id).unwrap();
473 assert!(pos("rocm") < pos("flash-attn"));
474 assert!(pos("rocm") < pos("onnx"));
475 assert!(pos("rocm") < pos("aiter"));
476 assert!(pos("pytorch") < pos("megatron"));
477 assert!(pos("mpi4py") < pos("megatron"));
478 assert!(pos("pytorch") < pos("vllm"));
479 assert!(pos("pytorch") < pos("deepspeed"));
480 assert!(pos("pytorch") < pos("flash-attn"));
481 assert!(pos("pytorch") < pos("aiter"));
482 assert!(pos("pytorch") < pos("comfyui"));
483 }
484
485 #[test]
486 fn test_topological_sort_filters_unrelated_deps() {
487 let ids = vec!["vllm".to_string(), "pytorch".to_string()];
489 let result = topological_sort(&ids).unwrap();
490 assert_eq!(result.len(), 2);
491 assert_eq!(result[0], "pytorch");
492 assert_eq!(result[1], "vllm");
493 }
494
495 #[test]
496 fn test_dependency_graph_is_acyclic() {
497 let all_ids: Vec<String> = NATIVE_COMPONENT_IDS.iter().map(|s| s.to_string()).collect();
499 let result = topological_sort(&all_ids);
500 assert!(
501 result.is_ok(),
502 "Dependency graph must be acyclic: {:?}",
503 result
504 );
505 let sorted = result.unwrap();
506 assert_eq!(sorted.len(), NATIVE_COMPONENT_IDS.len());
507 }
508
509 #[test]
510 fn test_native_ids_match_state_component_ids() {
511 for id in NATIVE_COMPONENT_IDS {
514 assert!(!id.is_empty(), "Component ID must not be empty");
516 assert!(
517 id.chars().all(|c| c == '-' || c.is_ascii_alphanumeric()),
518 "Component ID '{}' must be alphanumeric with hyphens",
519 id
520 );
521 }
522 }
523}