1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub enum LoopPhase {
12 Perceive,
14 Reason,
16 Act {
18 tool_name: String,
20 },
21 Done,
23 Error {
25 message: String,
27 },
28}
29
30impl std::fmt::Display for LoopPhase {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 Self::Perceive => write!(f, "perceive"),
34 Self::Reason => write!(f, "reason"),
35 Self::Act { tool_name } => write!(f, "act:{tool_name}"),
36 Self::Done => write!(f, "done"),
37 Self::Error { message } => write!(f, "error:{message}"),
38 }
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn test_phase_display() {
48 assert_eq!(LoopPhase::Perceive.to_string(), "perceive");
49 assert_eq!(LoopPhase::Reason.to_string(), "reason");
50 assert_eq!(LoopPhase::Act { tool_name: "rag".to_string() }.to_string(), "act:rag");
51 assert_eq!(LoopPhase::Done.to_string(), "done");
52 assert_eq!(LoopPhase::Error { message: "budget".to_string() }.to_string(), "error:budget");
53 }
54
55 #[test]
56 fn test_phase_equality() {
57 assert_eq!(LoopPhase::Perceive, LoopPhase::Perceive);
58 assert_ne!(LoopPhase::Perceive, LoopPhase::Reason);
59 assert_ne!(
60 LoopPhase::Act { tool_name: "a".into() },
61 LoopPhase::Act { tool_name: "b".into() }
62 );
63 }
64
65 #[test]
66 fn test_phase_serialization_roundtrip() {
67 let phases = vec![
68 LoopPhase::Perceive,
69 LoopPhase::Reason,
70 LoopPhase::Act { tool_name: "memory".into() },
71 LoopPhase::Done,
72 LoopPhase::Error { message: "out of budget".into() },
73 ];
74 for phase in &phases {
75 let json = serde_json::to_string(phase).expect("serialize failed");
76 let back: LoopPhase = serde_json::from_str(&json).expect("deserialize failed");
77 assert_eq!(*phase, back);
78 }
79 }
80}