use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AnomalySeverity {
Info,
Warning,
Critical,
}
impl std::fmt::Display for AnomalySeverity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AnomalySeverity::Info => write!(f, "INFO"),
AnomalySeverity::Warning => write!(f, "WARNING"),
AnomalySeverity::Critical => write!(f, "CRITICAL"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Anomaly {
pub description: String,
pub affected_rows: usize,
pub severity: AnomalySeverity,
pub suggested_action: String,
}
impl std::fmt::Display for Anomaly {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[{}] {} ({} rows affected)\n → {}",
self.severity, self.description, self.affected_rows, self.suggested_action
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TriageAction {
Accept,
Correct(String),
Abort,
RetryWithModel(String),
}
#[derive(Debug)]
pub struct TriageSession {
pub anomalies: Vec<Anomaly>,
pub actions: Vec<TriageAction>,
}
impl TriageSession {
pub fn new(anomalies: Vec<Anomaly>) -> Self {
Self {
anomalies,
actions: Vec::new(),
}
}
pub fn record_action(&mut self, action: TriageAction) {
self.actions.push(action);
}
pub fn is_complete(&self) -> bool {
self.actions.len() >= self.anomalies.len()
}
pub fn summary(&self) -> String {
let mut lines = vec!["═══ Semantic Anomaly Triage ═══".to_string()];
for (i, anomaly) in self.anomalies.iter().enumerate() {
lines.push(format!("\n[Anomaly {}]", i + 1));
lines.push(format!(" Severity: {}", anomaly.severity));
lines.push(format!(" {}", anomaly.description));
lines.push(format!(" Affected rows: {}", anomaly.affected_rows));
lines.push(format!(" Suggested: {}", anomaly.suggested_action));
if let Some(action) = self.actions.get(i) {
lines.push(format!(" Action: {:?}", action));
}
}
lines.join("\n")
}
}