rusty_stack/installers/components/
deepspeed.rs1use crate::installers::common::RocmEnv;
11use std::fmt;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum InstallMethod {
20 Global,
22 Venv,
24 Auto,
26}
27
28impl fmt::Display for InstallMethod {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 match self {
31 InstallMethod::Global => write!(f, "global"),
32 InstallMethod::Venv => write!(f, "venv"),
33 InstallMethod::Auto => write!(f, "auto"),
34 }
35 }
36}
37
38#[derive(Debug, Clone)]
40pub struct DeepSpeedConfig {
41 pub method: InstallMethod,
43 pub python_bin: String,
45 pub force_reinstall: bool,
47 pub dry_run: bool,
49}
50
51impl Default for DeepSpeedConfig {
52 fn default() -> Self {
53 Self {
54 method: InstallMethod::Auto,
55 python_bin: "python3".to_string(),
56 force_reinstall: false,
57 dry_run: false,
58 }
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct PipCommand {
65 pub program: String,
67 pub args: Vec<String>,
69 pub env: Vec<(String, String)>,
71}
72
73impl PipCommand {
74 pub fn to_command_string(&self) -> String {
76 let env_prefix = self
77 .env
78 .iter()
79 .map(|(k, v)| format!("{k}={v}"))
80 .collect::<Vec<_>>()
81 .join(" ");
82 let cmd = if self.args.is_empty() {
83 self.program.clone()
84 } else {
85 format!("{} {}", self.program, self.args.join(" "))
86 };
87 if env_prefix.is_empty() {
88 cmd
89 } else {
90 format!("{env_prefix} {cmd}")
91 }
92 }
93}
94
95pub struct DeepSpeedInstaller {
97 config: DeepSpeedConfig,
98}
99
100impl DeepSpeedInstaller {
101 pub fn new(config: DeepSpeedConfig) -> Self {
103 Self { config }
104 }
105
106 pub fn with_defaults() -> Self {
108 Self::new(DeepSpeedConfig::default())
109 }
110
111 pub fn rocm_build_env(&self, rocm_env: &RocmEnv) -> Vec<(String, String)> {
124 let rocm_path = rocm_env
125 .path()
126 .map(|p| p.to_string_lossy().to_string())
127 .unwrap_or_else(|| "/opt/rocm".to_string());
128
129 vec![
130 ("ROCM_HOME".to_string(), rocm_path.clone()),
131 ("HIP_PATH".to_string(), rocm_path),
132 ("HSA_OVERRIDE_GFX_VERSION".to_string(), "11.0.0".to_string()),
133 ("PYTORCH_ROCM_ARCH".to_string(), "gfx1100".to_string()),
134 ]
135 }
136
137 pub fn build_deps_install_command(&self) -> PipCommand {
145 let is_global = self.config.method == InstallMethod::Global
146 || self.config.method == InstallMethod::Auto;
147
148 let mut args = vec!["-m".to_string(), "pip".to_string(), "install".to_string()];
149 if is_global {
150 args.push("--break-system-packages".to_string());
151 }
152 args.extend([
153 "packaging".to_string(),
154 "ninja".to_string(),
155 "pydantic".to_string(),
156 "jsonschema".to_string(),
157 "einops".to_string(),
158 ]);
159
160 PipCommand {
161 program: self.config.python_bin.clone(),
162 args,
163 env: vec![],
164 }
165 }
166
167 pub fn build_install_command(&self) -> PipCommand {
173 let is_global = self.config.method == InstallMethod::Global
174 || self.config.method == InstallMethod::Auto;
175
176 let mut args = vec!["-m".to_string(), "pip".to_string(), "install".to_string()];
177 if is_global {
178 args.push("--break-system-packages".to_string());
179 }
180 args.push("--no-deps".to_string());
186 if self.config.force_reinstall {
187 args.push("--force-reinstall".to_string());
188 }
189 args.push("deepspeed".to_string());
190 args.push("einops".to_string());
191
192 PipCommand {
193 program: self.config.python_bin.clone(),
194 args,
195 env: vec![], }
197 }
198
199 pub fn build_no_deps_command(&self) -> PipCommand {
203 let mut args = vec![
204 "-m".to_string(),
205 "pip".to_string(),
206 "install".to_string(),
207 "--break-system-packages".to_string(),
208 "--no-deps".to_string(),
209 ];
210 args.push("deepspeed".to_string());
211
212 PipCommand {
213 program: self.config.python_bin.clone(),
214 args,
215 env: vec![],
216 }
217 }
218
219 pub fn build_force_reinstall_command(&self) -> PipCommand {
224 let mut args = vec![
225 "-m".to_string(),
226 "pip".to_string(),
227 "install".to_string(),
228 "--break-system-packages".to_string(),
229 "--force-reinstall".to_string(),
230 "--no-deps".to_string(),
231 ];
232 args.push("deepspeed".to_string());
233
234 PipCommand {
235 program: self.config.python_bin.clone(),
236 args,
237 env: vec![],
238 }
239 }
240
241 pub fn required_dependencies(&self) -> &[&str] {
243 &[
244 "packaging",
245 "ninja",
246 "pydantic",
247 "jsonschema",
248 "einops",
249 "deepspeed",
250 ]
251 }
252}
253
254#[cfg(test)]
259mod tests {
260 use super::*;
261 use std::path::PathBuf;
262
263 #[test]
266 fn test_build_install_command_global() {
267 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
268 method: InstallMethod::Global,
269 ..Default::default()
270 });
271 let cmd = installer.build_install_command();
272 assert_eq!(cmd.program, "python3");
273 assert!(cmd.args.contains(&"-m".to_string()));
274 assert!(cmd.args.contains(&"pip".to_string()));
275 assert!(cmd.args.contains(&"install".to_string()));
276 assert!(cmd.args.contains(&"--break-system-packages".to_string()));
277 assert!(cmd.args.contains(&"deepspeed".to_string()));
278 assert!(cmd.args.contains(&"einops".to_string()));
279 }
280
281 #[test]
282 fn test_build_install_command_venv() {
283 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
284 method: InstallMethod::Venv,
285 ..Default::default()
286 });
287 let cmd = installer.build_install_command();
288 assert!(!cmd.args.contains(&"--break-system-packages".to_string()));
290 assert!(cmd.args.contains(&"deepspeed".to_string()));
291 }
292
293 #[test]
294 fn test_build_install_command_force_reinstall() {
295 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
296 force_reinstall: true,
297 ..Default::default()
298 });
299 let cmd = installer.build_install_command();
300 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
301 }
302
303 #[test]
304 fn test_build_deps_command() {
305 let installer = DeepSpeedInstaller::with_defaults();
306 let cmd = installer.build_deps_install_command();
307 assert!(cmd.args.contains(&"packaging".to_string()));
308 assert!(cmd.args.contains(&"ninja".to_string()));
309 assert!(cmd.args.contains(&"pydantic".to_string()));
310 assert!(cmd.args.contains(&"jsonschema".to_string()));
311 assert!(cmd.args.contains(&"einops".to_string()));
312 }
313
314 #[test]
315 fn test_build_no_deps_command() {
316 let installer = DeepSpeedInstaller::with_defaults();
317 let cmd = installer.build_no_deps_command();
318 assert!(cmd.args.contains(&"--no-deps".to_string()));
319 assert!(cmd.args.contains(&"deepspeed".to_string()));
320 }
321
322 #[test]
323 fn test_build_force_reinstall_command() {
324 let installer = DeepSpeedInstaller::with_defaults();
325 let cmd = installer.build_force_reinstall_command();
326 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
327 assert!(cmd.args.contains(&"deepspeed".to_string()));
328 }
329
330 #[test]
331 fn test_rocm_build_env() {
332 let installer = DeepSpeedInstaller::with_defaults();
333 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
334 let env = installer.rocm_build_env(&rocm_env);
335 assert!(env
336 .iter()
337 .any(|(k, v)| k == "ROCM_HOME" && v == "/opt/rocm"));
338 assert!(env.iter().any(|(k, v)| k == "HIP_PATH" && v == "/opt/rocm"));
339 assert!(env
340 .iter()
341 .any(|(k, v)| k == "HSA_OVERRIDE_GFX_VERSION" && v == "11.0.0"));
342 assert!(env
343 .iter()
344 .any(|(k, v)| k == "PYTORCH_ROCM_ARCH" && v == "gfx1100"));
345 }
346
347 #[test]
348 fn test_rocm_build_env_no_rocm() {
349 let installer = DeepSpeedInstaller::with_defaults();
350 let rocm_env = RocmEnv::none();
351 let env = installer.rocm_build_env(&rocm_env);
352 assert!(env
353 .iter()
354 .any(|(k, v)| k == "ROCM_HOME" && v == "/opt/rocm"));
355 }
356
357 #[test]
358 fn test_required_dependencies() {
359 let installer = DeepSpeedInstaller::with_defaults();
360 let deps = installer.required_dependencies();
361 assert!(deps.contains(&"deepspeed"));
362 assert!(deps.contains(&"einops"));
363 assert!(deps.contains(&"ninja"));
364 }
365
366 #[test]
367 fn test_command_string_format() {
368 let cmd = PipCommand {
369 program: "python3".to_string(),
370 args: vec![
371 "-m".to_string(),
372 "pip".to_string(),
373 "install".to_string(),
374 "deepspeed".to_string(),
375 ],
376 env: vec![("ROCM_HOME".to_string(), "/opt/rocm".to_string())],
377 };
378 let s = cmd.to_command_string();
379 assert!(s.contains("ROCM_HOME=/opt/rocm"));
380 assert!(s.contains("python3 -m pip install deepspeed"));
381 }
382}