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 if self.config.force_reinstall {
181 args.push("--force-reinstall".to_string());
182 }
183 args.push("deepspeed".to_string());
184 args.push("einops".to_string());
185
186 PipCommand {
187 program: self.config.python_bin.clone(),
188 args,
189 env: vec![], }
191 }
192
193 pub fn build_no_deps_command(&self) -> PipCommand {
197 let mut args = vec![
198 "-m".to_string(),
199 "pip".to_string(),
200 "install".to_string(),
201 "--break-system-packages".to_string(),
202 "--no-deps".to_string(),
203 ];
204 args.push("deepspeed".to_string());
205
206 PipCommand {
207 program: self.config.python_bin.clone(),
208 args,
209 env: vec![],
210 }
211 }
212
213 pub fn build_force_reinstall_command(&self) -> PipCommand {
215 let mut args = vec![
216 "-m".to_string(),
217 "pip".to_string(),
218 "install".to_string(),
219 "--break-system-packages".to_string(),
220 "--force-reinstall".to_string(),
221 ];
222 args.push("deepspeed".to_string());
223
224 PipCommand {
225 program: self.config.python_bin.clone(),
226 args,
227 env: vec![],
228 }
229 }
230
231 pub fn required_dependencies(&self) -> &[&str] {
233 &[
234 "packaging",
235 "ninja",
236 "pydantic",
237 "jsonschema",
238 "einops",
239 "deepspeed",
240 ]
241 }
242}
243
244#[cfg(test)]
249mod tests {
250 use super::*;
251 use std::path::PathBuf;
252
253 #[test]
256 fn test_build_install_command_global() {
257 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
258 method: InstallMethod::Global,
259 ..Default::default()
260 });
261 let cmd = installer.build_install_command();
262 assert_eq!(cmd.program, "python3");
263 assert!(cmd.args.contains(&"-m".to_string()));
264 assert!(cmd.args.contains(&"pip".to_string()));
265 assert!(cmd.args.contains(&"install".to_string()));
266 assert!(cmd.args.contains(&"--break-system-packages".to_string()));
267 assert!(cmd.args.contains(&"deepspeed".to_string()));
268 assert!(cmd.args.contains(&"einops".to_string()));
269 }
270
271 #[test]
272 fn test_build_install_command_venv() {
273 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
274 method: InstallMethod::Venv,
275 ..Default::default()
276 });
277 let cmd = installer.build_install_command();
278 assert!(!cmd.args.contains(&"--break-system-packages".to_string()));
280 assert!(cmd.args.contains(&"deepspeed".to_string()));
281 }
282
283 #[test]
284 fn test_build_install_command_force_reinstall() {
285 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
286 force_reinstall: true,
287 ..Default::default()
288 });
289 let cmd = installer.build_install_command();
290 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
291 }
292
293 #[test]
294 fn test_build_deps_command() {
295 let installer = DeepSpeedInstaller::with_defaults();
296 let cmd = installer.build_deps_install_command();
297 assert!(cmd.args.contains(&"packaging".to_string()));
298 assert!(cmd.args.contains(&"ninja".to_string()));
299 assert!(cmd.args.contains(&"pydantic".to_string()));
300 assert!(cmd.args.contains(&"jsonschema".to_string()));
301 assert!(cmd.args.contains(&"einops".to_string()));
302 }
303
304 #[test]
305 fn test_build_no_deps_command() {
306 let installer = DeepSpeedInstaller::with_defaults();
307 let cmd = installer.build_no_deps_command();
308 assert!(cmd.args.contains(&"--no-deps".to_string()));
309 assert!(cmd.args.contains(&"deepspeed".to_string()));
310 }
311
312 #[test]
313 fn test_build_force_reinstall_command() {
314 let installer = DeepSpeedInstaller::with_defaults();
315 let cmd = installer.build_force_reinstall_command();
316 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
317 assert!(cmd.args.contains(&"deepspeed".to_string()));
318 }
319
320 #[test]
321 fn test_rocm_build_env() {
322 let installer = DeepSpeedInstaller::with_defaults();
323 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
324 let env = installer.rocm_build_env(&rocm_env);
325 assert!(env
326 .iter()
327 .any(|(k, v)| k == "ROCM_HOME" && v == "/opt/rocm"));
328 assert!(env.iter().any(|(k, v)| k == "HIP_PATH" && v == "/opt/rocm"));
329 assert!(env
330 .iter()
331 .any(|(k, v)| k == "HSA_OVERRIDE_GFX_VERSION" && v == "11.0.0"));
332 assert!(env
333 .iter()
334 .any(|(k, v)| k == "PYTORCH_ROCM_ARCH" && v == "gfx1100"));
335 }
336
337 #[test]
338 fn test_rocm_build_env_no_rocm() {
339 let installer = DeepSpeedInstaller::with_defaults();
340 let rocm_env = RocmEnv::none();
341 let env = installer.rocm_build_env(&rocm_env);
342 assert!(env
343 .iter()
344 .any(|(k, v)| k == "ROCM_HOME" && v == "/opt/rocm"));
345 }
346
347 #[test]
348 fn test_required_dependencies() {
349 let installer = DeepSpeedInstaller::with_defaults();
350 let deps = installer.required_dependencies();
351 assert!(deps.contains(&"deepspeed"));
352 assert!(deps.contains(&"einops"));
353 assert!(deps.contains(&"ninja"));
354 }
355
356 #[test]
357 fn test_command_string_format() {
358 let cmd = PipCommand {
359 program: "python3".to_string(),
360 args: vec![
361 "-m".to_string(),
362 "pip".to_string(),
363 "install".to_string(),
364 "deepspeed".to_string(),
365 ],
366 env: vec![("ROCM_HOME".to_string(), "/opt/rocm".to_string())],
367 };
368 let s = cmd.to_command_string();
369 assert!(s.contains("ROCM_HOME=/opt/rocm"));
370 assert!(s.contains("python3 -m pip install deepspeed"));
371 }
372}