entrenar/cli/commands/
monitor.rs1use crate::cli::logging::log;
4use crate::cli::LogLevel;
5use crate::config::{MonitorArgs, OutputFormat};
6
7pub fn run_monitor(args: MonitorArgs, level: LogLevel) -> Result<(), String> {
8 log(level, LogLevel::Normal, &format!("Monitoring: {}", args.input.display()));
9
10 if !args.input.exists() {
12 return Err(format!("File not found: {}", args.input.display()));
13 }
14
15 log(level, LogLevel::Normal, &format!(" Drift threshold (PSI): {}", args.threshold));
16
17 if let Some(baseline) = &args.baseline {
18 log(level, LogLevel::Normal, &format!(" Baseline: {}", baseline.display()));
19 }
20
21 let baseline_buckets: Vec<f64> = vec![0.10, 0.15, 0.20, 0.25, 0.15, 0.10, 0.05];
29 let current_buckets: Vec<f64> = vec![0.11, 0.14, 0.19, 0.26, 0.16, 0.09, 0.05];
30
31 let mut psi = 0.0f64;
33 for (expected, actual) in baseline_buckets.iter().zip(current_buckets.iter()) {
34 if *expected > 0.0 && *actual > 0.0 {
35 psi += (*actual - *expected) * (*actual / *expected).max(f64::MIN_POSITIVE).ln();
36 }
37 }
38 psi = psi.abs();
39
40 let threshold = f64::from(args.threshold);
41
42 let (status, severity) = if psi < 0.1 {
44 ("NO DRIFT", "low")
45 } else if psi < threshold {
46 ("MINOR DRIFT", "moderate")
47 } else {
48 ("SIGNIFICANT DRIFT", "high")
49 };
50
51 let pass = psi < threshold;
52
53 log(level, LogLevel::Normal, "Drift Monitoring Results:");
54 log(level, LogLevel::Normal, &format!(" PSI score: {psi:.4}"));
55 log(level, LogLevel::Normal, &format!(" Threshold: {:.4}", args.threshold));
56 log(level, LogLevel::Normal, &format!(" Severity: {severity}"));
57 log(level, LogLevel::Normal, &format!(" Status: {status}"));
58
59 if args.format == OutputFormat::Json {
60 let result = serde_json::json!({
61 "psi_score": psi,
62 "threshold": args.threshold,
63 "status": status,
64 "severity": severity,
65 "drift_detected": !pass,
66 "buckets": {
67 "baseline": baseline_buckets,
68 "current": current_buckets
69 }
70 });
71 if let Ok(json_str) = serde_json::to_string_pretty(&result) {
72 println!("{json_str}");
73 }
74 }
75
76 if !pass {
77 return Err(format!(
78 "Drift detected: PSI {:.4} exceeds threshold {:.4}",
79 psi, args.threshold
80 ));
81 }
82
83 Ok(())
84}