rusty_stack/installers/components/
deepspeed.rs1use std::fmt;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum InstallMethod {
19 Global,
21 Venv,
23 Auto,
25}
26
27impl fmt::Display for InstallMethod {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 InstallMethod::Global => write!(f, "global"),
31 InstallMethod::Venv => write!(f, "venv"),
32 InstallMethod::Auto => write!(f, "auto"),
33 }
34 }
35}
36
37#[derive(Debug, Clone)]
39pub struct DeepSpeedConfig {
40 pub method: InstallMethod,
42 pub python_bin: String,
44 pub force_reinstall: bool,
46 pub dry_run: bool,
48}
49
50impl Default for DeepSpeedConfig {
51 fn default() -> Self {
52 Self {
53 method: InstallMethod::Auto,
54 python_bin: "python3".to_string(),
55 force_reinstall: false,
56 dry_run: false,
57 }
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct PipCommand {
64 pub program: String,
66 pub args: Vec<String>,
68 pub env: Vec<(String, String)>,
70}
71
72impl PipCommand {
73 pub fn to_command_string(&self) -> String {
75 let env_prefix = self
76 .env
77 .iter()
78 .map(|(k, v)| format!("{k}={v}"))
79 .collect::<Vec<_>>()
80 .join(" ");
81 let cmd = if self.args.is_empty() {
82 self.program.clone()
83 } else {
84 format!("{} {}", self.program, self.args.join(" "))
85 };
86 if env_prefix.is_empty() {
87 cmd
88 } else {
89 format!("{env_prefix} {cmd}")
90 }
91 }
92}
93
94pub struct DeepSpeedInstaller {
96 config: DeepSpeedConfig,
97}
98
99impl DeepSpeedInstaller {
100 pub fn new(config: DeepSpeedConfig) -> Self {
102 Self { config }
103 }
104
105 pub fn with_defaults() -> Self {
107 Self::new(DeepSpeedConfig::default())
108 }
109
110 pub fn build_deps_install_command(&self) -> PipCommand {
118 let is_global = self.config.method == InstallMethod::Global
119 || self.config.method == InstallMethod::Auto;
120
121 let mut args = vec!["-m".to_string(), "pip".to_string(), "install".to_string()];
122 if is_global {
123 args.push("--break-system-packages".to_string());
124 }
125 args.extend([
126 "packaging".to_string(),
127 "ninja".to_string(),
128 "pydantic".to_string(),
129 "jsonschema".to_string(),
130 "einops".to_string(),
131 ]);
132
133 PipCommand {
134 program: self.config.python_bin.clone(),
135 args,
136 env: vec![],
137 }
138 }
139
140 pub fn build_install_command(&self) -> PipCommand {
146 let is_global = self.config.method == InstallMethod::Global
147 || self.config.method == InstallMethod::Auto;
148
149 let mut args = vec!["-m".to_string(), "pip".to_string(), "install".to_string()];
150 if is_global {
151 args.push("--break-system-packages".to_string());
152 }
153 if self.config.force_reinstall {
154 args.push("--force-reinstall".to_string());
159 args.push("--no-deps".to_string());
160 }
161 args.push("deepspeed".to_string());
169 args.push("einops".to_string());
170
171 PipCommand {
172 program: self.config.python_bin.clone(),
173 args,
174 env: vec![], }
176 }
177
178 pub fn build_no_deps_command(&self) -> PipCommand {
182 let mut args = vec![
183 "-m".to_string(),
184 "pip".to_string(),
185 "install".to_string(),
186 "--break-system-packages".to_string(),
187 "--no-deps".to_string(),
188 ];
189 args.push("deepspeed".to_string());
190
191 PipCommand {
192 program: self.config.python_bin.clone(),
193 args,
194 env: vec![],
195 }
196 }
197
198 pub fn build_force_reinstall_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 "--force-reinstall".to_string(),
209 "--no-deps".to_string(),
210 ];
211 args.push("deepspeed".to_string());
212
213 PipCommand {
214 program: self.config.python_bin.clone(),
215 args,
216 env: vec![],
217 }
218 }
219
220 pub fn required_dependencies(&self) -> &[&str] {
222 &[
223 "packaging",
224 "ninja",
225 "pydantic",
226 "jsonschema",
227 "einops",
228 "deepspeed",
229 ]
230 }
231}
232
233#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
244 fn test_build_install_command_global() {
245 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
246 method: InstallMethod::Global,
247 ..Default::default()
248 });
249 let cmd = installer.build_install_command();
250 assert_eq!(cmd.program, "python3");
251 assert!(cmd.args.contains(&"-m".to_string()));
252 assert!(cmd.args.contains(&"pip".to_string()));
253 assert!(cmd.args.contains(&"install".to_string()));
254 assert!(cmd.args.contains(&"--break-system-packages".to_string()));
255 assert!(cmd.args.contains(&"deepspeed".to_string()));
256 assert!(cmd.args.contains(&"einops".to_string()));
257 }
258
259 #[test]
260 fn test_build_install_command_venv() {
261 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
262 method: InstallMethod::Venv,
263 ..Default::default()
264 });
265 let cmd = installer.build_install_command();
266 assert!(!cmd.args.contains(&"--break-system-packages".to_string()));
268 assert!(cmd.args.contains(&"deepspeed".to_string()));
269 }
270
271 #[test]
272 fn test_build_install_command_force_reinstall() {
273 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
274 force_reinstall: true,
275 ..Default::default()
276 });
277 let cmd = installer.build_install_command();
278 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
279 }
280
281 #[test]
282 fn test_build_deps_command() {
283 let installer = DeepSpeedInstaller::with_defaults();
284 let cmd = installer.build_deps_install_command();
285 assert!(cmd.args.contains(&"packaging".to_string()));
286 assert!(cmd.args.contains(&"ninja".to_string()));
287 assert!(cmd.args.contains(&"pydantic".to_string()));
288 assert!(cmd.args.contains(&"jsonschema".to_string()));
289 assert!(cmd.args.contains(&"einops".to_string()));
290 }
291
292 #[test]
293 fn test_build_no_deps_command() {
294 let installer = DeepSpeedInstaller::with_defaults();
295 let cmd = installer.build_no_deps_command();
296 assert!(cmd.args.contains(&"--no-deps".to_string()));
297 assert!(cmd.args.contains(&"deepspeed".to_string()));
298 }
299
300 #[test]
301 fn test_build_force_reinstall_command() {
302 let installer = DeepSpeedInstaller::with_defaults();
303 let cmd = installer.build_force_reinstall_command();
304 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
305 assert!(cmd.args.contains(&"deepspeed".to_string()));
306 }
307
308 #[test]
309 fn test_required_dependencies() {
310 let installer = DeepSpeedInstaller::with_defaults();
311 let deps = installer.required_dependencies();
312 assert!(deps.contains(&"deepspeed"));
313 assert!(deps.contains(&"einops"));
314 assert!(deps.contains(&"ninja"));
315 }
316
317 #[test]
318 fn test_command_string_format() {
319 let cmd = PipCommand {
320 program: "python3".to_string(),
321 args: vec![
322 "-m".to_string(),
323 "pip".to_string(),
324 "install".to_string(),
325 "deepspeed".to_string(),
326 ],
327 env: vec![("ROCM_HOME".to_string(), "/opt/rocm".to_string())],
328 };
329 let s = cmd.to_command_string();
330 assert!(s.contains("ROCM_HOME=/opt/rocm"));
331 assert!(s.contains("python3 -m pip install deepspeed"));
332 }
333}