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("--upgrade".to_string());
171 args.push("deepspeed".to_string());
172 args.push("einops".to_string());
173
174 PipCommand {
175 program: self.config.python_bin.clone(),
176 args,
177 env: vec![], }
179 }
180
181 pub fn build_no_deps_command(&self) -> PipCommand {
185 let mut args = vec![
186 "-m".to_string(),
187 "pip".to_string(),
188 "install".to_string(),
189 "--break-system-packages".to_string(),
190 "--no-deps".to_string(),
191 ];
192 args.push("deepspeed".to_string());
193
194 PipCommand {
195 program: self.config.python_bin.clone(),
196 args,
197 env: vec![],
198 }
199 }
200
201 pub fn build_force_reinstall_command(&self) -> PipCommand {
206 let mut args = vec![
207 "-m".to_string(),
208 "pip".to_string(),
209 "install".to_string(),
210 "--break-system-packages".to_string(),
211 "--force-reinstall".to_string(),
212 "--no-deps".to_string(),
213 ];
214 args.push("deepspeed".to_string());
215
216 PipCommand {
217 program: self.config.python_bin.clone(),
218 args,
219 env: vec![],
220 }
221 }
222
223 pub fn required_dependencies(&self) -> &[&str] {
225 &[
226 "packaging",
227 "ninja",
228 "pydantic",
229 "jsonschema",
230 "einops",
231 "deepspeed",
232 ]
233 }
234}
235
236#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
247 fn test_build_install_command_global() {
248 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
249 method: InstallMethod::Global,
250 ..Default::default()
251 });
252 let cmd = installer.build_install_command();
253 assert_eq!(cmd.program, "python3");
254 assert!(cmd.args.contains(&"-m".to_string()));
255 assert!(cmd.args.contains(&"pip".to_string()));
256 assert!(cmd.args.contains(&"install".to_string()));
257 assert!(cmd.args.contains(&"--break-system-packages".to_string()));
258 assert!(cmd.args.contains(&"deepspeed".to_string()));
259 assert!(cmd.args.contains(&"einops".to_string()));
260 }
261
262 #[test]
263 fn test_build_install_command_venv() {
264 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
265 method: InstallMethod::Venv,
266 ..Default::default()
267 });
268 let cmd = installer.build_install_command();
269 assert!(!cmd.args.contains(&"--break-system-packages".to_string()));
271 assert!(cmd.args.contains(&"deepspeed".to_string()));
272 }
273
274 #[test]
275 fn test_build_install_command_force_reinstall() {
276 let installer = DeepSpeedInstaller::new(DeepSpeedConfig {
277 force_reinstall: true,
278 ..Default::default()
279 });
280 let cmd = installer.build_install_command();
281 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
282 }
283
284 #[test]
285 fn test_build_deps_command() {
286 let installer = DeepSpeedInstaller::with_defaults();
287 let cmd = installer.build_deps_install_command();
288 assert!(cmd.args.contains(&"packaging".to_string()));
289 assert!(cmd.args.contains(&"ninja".to_string()));
290 assert!(cmd.args.contains(&"pydantic".to_string()));
291 assert!(cmd.args.contains(&"jsonschema".to_string()));
292 assert!(cmd.args.contains(&"einops".to_string()));
293 }
294
295 #[test]
296 fn test_build_no_deps_command() {
297 let installer = DeepSpeedInstaller::with_defaults();
298 let cmd = installer.build_no_deps_command();
299 assert!(cmd.args.contains(&"--no-deps".to_string()));
300 assert!(cmd.args.contains(&"deepspeed".to_string()));
301 }
302
303 #[test]
304 fn test_build_force_reinstall_command() {
305 let installer = DeepSpeedInstaller::with_defaults();
306 let cmd = installer.build_force_reinstall_command();
307 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
308 assert!(cmd.args.contains(&"deepspeed".to_string()));
309 }
310
311 #[test]
312 fn test_required_dependencies() {
313 let installer = DeepSpeedInstaller::with_defaults();
314 let deps = installer.required_dependencies();
315 assert!(deps.contains(&"deepspeed"));
316 assert!(deps.contains(&"einops"));
317 assert!(deps.contains(&"ninja"));
318 }
319
320 #[test]
321 fn test_command_string_format() {
322 let cmd = PipCommand {
323 program: "python3".to_string(),
324 args: vec![
325 "-m".to_string(),
326 "pip".to_string(),
327 "install".to_string(),
328 "deepspeed".to_string(),
329 ],
330 env: vec![("ROCM_HOME".to_string(), "/opt/rocm".to_string())],
331 };
332 let s = cmd.to_command_string();
333 assert!(s.contains("ROCM_HOME=/opt/rocm"));
334 assert!(s.contains("python3 -m pip install deepspeed"));
335 }
336}