use anyhow::Result; use serde::{Deserialize, Serialize}; use std::path::PathBuf; /// Configuration for all training run paths - NO hardcoded defaults #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TrainingPaths { /// Base directory for all training outputs (e.g., /runpod-volume) pub base_dir: PathBuf, /// Model name (mamba2, tft, dqn, ppo) pub model_name: String, /// Unique run ID (generated or provided) pub run_id: String, } impl TrainingPaths { /// Create new training paths configuration pub fn new, S1: Into, S2: Into>( base_dir: P, model_name: S1, run_id: S2, ) -> Self { Self { base_dir: base_dir.into(), model_name: model_name.into(), run_id: run_id.into(), } } /// Get training run directory: {base_dir}/training_runs/{model_name}/run_{run_id} pub fn run_dir(&self) -> PathBuf { self.base_dir .join("training_runs") .join(&self.model_name) .join(format!("run_{}", self.run_id)) } /// Get checkpoints directory pub fn checkpoints_dir(&self) -> PathBuf { self.run_dir().join("checkpoints") } /// Get logs directory pub fn logs_dir(&self) -> PathBuf { self.run_dir().join("logs") } /// Get hyperopt directory pub fn hyperopt_dir(&self) -> PathBuf { self.run_dir().join("hyperopt") } /// Get metrics directory pub fn metrics_dir(&self) -> PathBuf { self.run_dir().join("metrics") } /// Create all directories pub fn create_all(&self) -> Result<()> { std::fs::create_dir_all(self.checkpoints_dir())?; std::fs::create_dir_all(self.logs_dir())?; std::fs::create_dir_all(self.hyperopt_dir())?; std::fs::create_dir_all(self.metrics_dir())?; Ok(()) } } /// Generate run ID with timestamp: YYYYMMDD_HHMMSS_type pub fn generate_run_id(run_type: &str) -> String { let now = chrono::Utc::now(); format!("{}_{}", now.format("%Y%m%d_%H%M%S"), run_type) } #[cfg(test)] mod tests { use super::*; #[test] fn test_training_paths_creation() { let paths = TrainingPaths::new("/tmp/test", "mamba2", "20251028_223000_hyperopt"); assert_eq!( paths.run_dir(), PathBuf::from("/tmp/test/training_runs/mamba2/run_20251028_223000_hyperopt") ); assert_eq!( paths.checkpoints_dir(), PathBuf::from( "/tmp/test/training_runs/mamba2/run_20251028_223000_hyperopt/checkpoints" ) ); } #[test] fn test_run_id_generation() { let run_id = generate_run_id("hyperopt"); assert!(run_id.contains("hyperopt")); assert!(run_id.len() > 15); // YYYYMMDD_HHMMSS + _hyperopt } }