use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum LoopPhase {
Perceive,
Reason,
Act {
tool_name: String,
},
Done,
Error {
message: String,
},
}
impl std::fmt::Display for LoopPhase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Perceive => write!(f, "perceive"),
Self::Reason => write!(f, "reason"),
Self::Act { tool_name } => write!(f, "act:{tool_name}"),
Self::Done => write!(f, "done"),
Self::Error { message } => write!(f, "error:{message}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_phase_display() {
assert_eq!(LoopPhase::Perceive.to_string(), "perceive");
assert_eq!(LoopPhase::Reason.to_string(), "reason");
assert_eq!(LoopPhase::Act { tool_name: "rag".to_string() }.to_string(), "act:rag");
assert_eq!(LoopPhase::Done.to_string(), "done");
assert_eq!(LoopPhase::Error { message: "budget".to_string() }.to_string(), "error:budget");
}
#[test]
fn test_phase_equality() {
assert_eq!(LoopPhase::Perceive, LoopPhase::Perceive);
assert_ne!(LoopPhase::Perceive, LoopPhase::Reason);
assert_ne!(
LoopPhase::Act { tool_name: "a".into() },
LoopPhase::Act { tool_name: "b".into() }
);
}
#[test]
fn test_phase_serialization_roundtrip() {
let phases = vec![
LoopPhase::Perceive,
LoopPhase::Reason,
LoopPhase::Act { tool_name: "memory".into() },
LoopPhase::Done,
LoopPhase::Error { message: "out of budget".into() },
];
for phase in &phases {
let json = serde_json::to_string(phase).expect("serialize failed");
let back: LoopPhase = serde_json::from_str(&json).expect("deserialize failed");
assert_eq!(*phase, back);
}
}
}