Skip to main content

lc_agents/plan_execute/
plan.rs

1//! Plan / PlanStep types
2
3use serde::{Deserialize, Serialize};
4
5/// Step status
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
7pub enum StepStatus {
8    /// Awaiting execution
9    #[default]
10    Pending,
11    /// Running
12    Running,
13    /// Completed
14    Completed,
15    /// Execution failed
16    Failed {
17        /// Failure reason
18        error: String,
19    },
20}
21
22/// An execution-plan step
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct PlanStep {
25    /// Step ID
26    pub id: usize,
27    /// Step description
28    pub description: String,
29    /// Step status
30    #[serde(default)]
31    pub status: StepStatus,
32    /// Step execution result
33    #[serde(default)]
34    pub result: Option<String>,
35}
36
37impl PlanStep {
38    /// Creates a new plan step, initially Pending.
39    pub fn new(id: usize, description: impl Into<String>) -> Self {
40        Self {
41            id,
42            description: description.into(),
43            status: StepStatus::Pending,
44            result: None,
45        }
46    }
47}
48
49/// Execution plan
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct Plan {
52    /// Plan objective
53    pub objective: String,
54    /// Step list
55    pub steps: Vec<PlanStep>,
56}
57
58impl Plan {
59    /// Creates a new execution plan.
60    pub fn new(objective: impl Into<String>, steps: Vec<PlanStep>) -> Self {
61        Self {
62            objective: objective.into(),
63            steps,
64        }
65    }
66
67    /// Constructs from a list of step descriptions (ids increment from 0)
68    pub fn from_descriptions(objective: impl Into<String>, descs: Vec<String>) -> Self {
69        let steps = descs
70            .into_iter()
71            .enumerate()
72            .map(|(i, d)| PlanStep::new(i, d))
73            .collect();
74        Self::new(objective, steps)
75    }
76
77    /// The next pending step
78    pub fn next_pending(&self) -> Option<&PlanStep> {
79        self.steps.iter().find(|s| s.status == StepStatus::Pending)
80    }
81
82    /// Whether all steps are completed
83    pub fn is_complete(&self) -> bool {
84        self.steps.iter().all(|s| s.status == StepStatus::Completed)
85    }
86
87    /// Marks the given step as completed and records the result.
88    pub fn mark_completed(&mut self, id: usize, result: impl Into<String>) {
89        if let Some(s) = self.steps.iter_mut().find(|s| s.id == id) {
90            s.status = StepStatus::Completed;
91            s.result = Some(result.into());
92        }
93    }
94
95    /// Marks the given step as failed and records the error.
96    pub fn mark_failed(&mut self, id: usize, error: impl Into<String>) {
97        if let Some(s) = self.steps.iter_mut().find(|s| s.id == id) {
98            s.status = StepStatus::Failed {
99                error: error.into(),
100            };
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_from_descriptions() {
111        let plan = Plan::from_descriptions("目标", vec!["步骤1".to_string(), "步骤2".to_string()]);
112        assert_eq!(plan.objective, "目标");
113        assert_eq!(plan.steps.len(), 2);
114        assert_eq!(plan.steps[0].id, 0);
115        assert_eq!(plan.steps[1].id, 1);
116        assert_eq!(plan.steps[0].status, StepStatus::Pending);
117    }
118
119    #[test]
120    fn test_next_pending() {
121        let mut plan = Plan::from_descriptions("obj", vec!["a".to_string(), "b".to_string()]);
122        assert_eq!(plan.next_pending().unwrap().id, 0);
123        plan.mark_completed(0, "result".to_string());
124        assert_eq!(plan.next_pending().unwrap().id, 1);
125        plan.mark_completed(1, "result".to_string());
126        assert!(plan.next_pending().is_none());
127    }
128
129    #[test]
130    fn test_is_complete() {
131        let mut plan = Plan::from_descriptions("obj", vec!["a".to_string(), "b".to_string()]);
132        assert!(!plan.is_complete());
133        plan.mark_completed(0, "r".to_string());
134        assert!(!plan.is_complete());
135        plan.mark_completed(1, "r".to_string());
136        assert!(plan.is_complete());
137    }
138
139    #[test]
140    fn test_mark_failed() {
141        let mut plan = Plan::from_descriptions("obj", vec!["a".to_string()]);
142        plan.mark_failed(0, "出错".to_string());
143        assert_eq!(
144            plan.steps[0].status,
145            StepStatus::Failed {
146                error: "出错".to_string()
147            }
148        );
149        assert!(!plan.is_complete());
150    }
151
152    #[test]
153    fn test_empty_plan_complete() {
154        let plan = Plan::from_descriptions("obj", vec![]);
155        assert!(plan.is_complete());
156    }
157}