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