1pub mod llm_planner;
9
10pub use llm_planner::{AchievementResult, LlmPlanner, Planner, PreAnalysis};
11
12use serde::{Deserialize, Serialize};
13use std::fmt;
14use std::str::FromStr;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
22#[serde(rename_all = "snake_case")]
23pub enum TaskStatus {
24 #[default]
26 Pending,
27 InProgress,
29 Completed,
31 Failed,
33 Skipped,
35 Cancelled,
37}
38
39impl fmt::Display for TaskStatus {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 TaskStatus::Pending => write!(f, "pending"),
43 TaskStatus::InProgress => write!(f, "in_progress"),
44 TaskStatus::Completed => write!(f, "completed"),
45 TaskStatus::Failed => write!(f, "failed"),
46 TaskStatus::Skipped => write!(f, "skipped"),
47 TaskStatus::Cancelled => write!(f, "cancelled"),
48 }
49 }
50}
51
52impl FromStr for TaskStatus {
53 type Err = std::convert::Infallible;
54
55 fn from_str(s: &str) -> Result<Self, Self::Err> {
56 Ok(match s.to_lowercase().as_str() {
57 "pending" => TaskStatus::Pending,
58 "in_progress" | "inprogress" => TaskStatus::InProgress,
59 "completed" | "done" => TaskStatus::Completed,
60 "failed" => TaskStatus::Failed,
61 "skipped" => TaskStatus::Skipped,
62 "cancelled" | "canceled" => TaskStatus::Cancelled,
63 _ => TaskStatus::Pending,
64 })
65 }
66}
67
68impl TaskStatus {
69 pub fn is_active(&self) -> bool {
71 matches!(self, TaskStatus::Pending | TaskStatus::InProgress)
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
81#[serde(rename_all = "snake_case")]
82pub enum TaskPriority {
83 High,
85 #[default]
87 Medium,
88 Low,
90}
91
92impl fmt::Display for TaskPriority {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 match self {
95 TaskPriority::High => write!(f, "high"),
96 TaskPriority::Medium => write!(f, "medium"),
97 TaskPriority::Low => write!(f, "low"),
98 }
99 }
100}
101
102impl FromStr for TaskPriority {
103 type Err = std::convert::Infallible;
104
105 fn from_str(s: &str) -> Result<Self, Self::Err> {
106 Ok(match s.to_lowercase().as_str() {
107 "high" | "h" | "1" => TaskPriority::High,
108 "medium" | "med" | "m" | "2" => TaskPriority::Medium,
109 "low" | "l" | "3" => TaskPriority::Low,
110 _ => TaskPriority::Medium,
111 })
112 }
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct Task {
122 pub id: String,
124 pub content: String,
126 pub status: TaskStatus,
128 #[serde(default)]
130 pub priority: TaskPriority,
131 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub tool: Option<String>,
134 #[serde(default, skip_serializing_if = "Vec::is_empty")]
136 pub dependencies: Vec<String>,
137 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub success_criteria: Option<String>,
140}
141
142impl Task {
143 pub fn new(id: impl Into<String>, content: impl Into<String>) -> Self {
145 Self {
146 id: id.into(),
147 content: content.into(),
148 status: TaskStatus::Pending,
149 priority: TaskPriority::Medium,
150 tool: None,
151 dependencies: Vec::new(),
152 success_criteria: None,
153 }
154 }
155
156 pub fn with_priority(mut self, priority: TaskPriority) -> Self {
158 self.priority = priority;
159 self
160 }
161
162 pub fn with_status(mut self, status: TaskStatus) -> Self {
164 self.status = status;
165 self
166 }
167
168 pub fn with_tool(mut self, tool: impl Into<String>) -> Self {
170 self.tool = Some(tool.into());
171 self
172 }
173
174 pub fn with_dependencies(mut self, deps: Vec<String>) -> Self {
176 self.dependencies = deps;
177 self
178 }
179
180 pub fn with_success_criteria(mut self, criteria: impl Into<String>) -> Self {
182 self.success_criteria = Some(criteria.into());
183 self
184 }
185
186 pub fn is_active(&self) -> bool {
188 self.status.is_active()
189 }
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
198pub enum Complexity {
199 Simple,
201 Medium,
203 Complex,
205 VeryComplex,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct ExecutionPlan {
212 pub goal: String,
214 pub steps: Vec<Task>,
216 pub complexity: Complexity,
218 pub required_tools: Vec<String>,
220 pub estimated_steps: usize,
222}
223
224impl ExecutionPlan {
225 pub fn new(goal: impl Into<String>, complexity: Complexity) -> Self {
226 Self {
227 goal: goal.into(),
228 steps: Vec::new(),
229 complexity,
230 required_tools: Vec::new(),
231 estimated_steps: 0,
232 }
233 }
234
235 pub fn add_step(&mut self, step: Task) {
236 self.steps.push(step);
237 self.estimated_steps = self.steps.len();
238 }
239
240 pub fn add_required_tool(&mut self, tool: impl Into<String>) {
241 let tool_str = tool.into();
242 if !self.required_tools.contains(&tool_str) {
243 self.required_tools.push(tool_str);
244 }
245 }
246
247 pub fn get_ready_steps(&self) -> Vec<&Task> {
249 self.steps
250 .iter()
251 .filter(|step| {
252 step.status == TaskStatus::Pending
253 && step.dependencies.iter().all(|dep_id| {
254 self.steps
255 .iter()
256 .find(|s| &s.id == dep_id)
257 .map(|s| s.status == TaskStatus::Completed)
258 .unwrap_or(false)
259 })
260 })
261 .collect()
262 }
263
264 pub fn mark_status(&mut self, step_id: &str, status: TaskStatus) {
266 if let Some(step) = self.steps.iter_mut().find(|s| s.id == step_id) {
267 step.status = status;
268 }
269 }
270
271 pub fn pending_count(&self) -> usize {
273 self.steps
274 .iter()
275 .filter(|s| s.status == TaskStatus::Pending)
276 .count()
277 }
278
279 pub fn has_deadlock(&self) -> bool {
284 self.pending_count() > 0 && self.get_ready_steps().is_empty()
285 }
286
287 pub fn progress(&self) -> f32 {
289 if self.steps.is_empty() {
290 return 0.0;
291 }
292 let completed = self
293 .steps
294 .iter()
295 .filter(|s| s.status == TaskStatus::Completed)
296 .count();
297 completed as f32 / self.steps.len() as f32
298 }
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct AgentGoal {
308 pub description: String,
310 pub success_criteria: Vec<String>,
312 pub progress: f32,
314 pub achieved: bool,
316 pub created_at: i64,
318 pub achieved_at: Option<i64>,
320}
321
322impl AgentGoal {
323 pub fn new(description: impl Into<String>) -> Self {
324 Self {
325 description: description.into(),
326 success_criteria: Vec::new(),
327 progress: 0.0,
328 achieved: false,
329 created_at: chrono::Utc::now().timestamp(),
330 achieved_at: None,
331 }
332 }
333
334 pub fn with_criteria(mut self, criteria: Vec<String>) -> Self {
335 self.success_criteria = criteria;
336 self
337 }
338
339 pub fn update_progress(&mut self, progress: f32) {
340 self.progress = progress.clamp(0.0, 1.0);
341 }
342
343 pub fn mark_achieved(&mut self) {
344 self.achieved = true;
345 self.progress = 1.0;
346 self.achieved_at = Some(chrono::Utc::now().timestamp());
347 }
348}
349
350#[cfg(test)]
355mod tests {
356 use super::*;
357
358 #[test]
363 fn test_task_status_display() {
364 assert_eq!(TaskStatus::Pending.to_string(), "pending");
365 assert_eq!(TaskStatus::InProgress.to_string(), "in_progress");
366 assert_eq!(TaskStatus::Completed.to_string(), "completed");
367 assert_eq!(TaskStatus::Failed.to_string(), "failed");
368 assert_eq!(TaskStatus::Skipped.to_string(), "skipped");
369 assert_eq!(TaskStatus::Cancelled.to_string(), "cancelled");
370 }
371
372 #[test]
373 fn test_task_status_from_str() {
374 assert_eq!(
375 TaskStatus::from_str("pending").unwrap(),
376 TaskStatus::Pending
377 );
378 assert_eq!(
379 TaskStatus::from_str("in_progress").unwrap(),
380 TaskStatus::InProgress
381 );
382 assert_eq!(
383 TaskStatus::from_str("inprogress").unwrap(),
384 TaskStatus::InProgress
385 );
386 assert_eq!(
387 TaskStatus::from_str("completed").unwrap(),
388 TaskStatus::Completed
389 );
390 assert_eq!(TaskStatus::from_str("done").unwrap(), TaskStatus::Completed);
391 assert_eq!(TaskStatus::from_str("failed").unwrap(), TaskStatus::Failed);
392 assert_eq!(
393 TaskStatus::from_str("skipped").unwrap(),
394 TaskStatus::Skipped
395 );
396 assert_eq!(
397 TaskStatus::from_str("cancelled").unwrap(),
398 TaskStatus::Cancelled
399 );
400 assert_eq!(
401 TaskStatus::from_str("canceled").unwrap(),
402 TaskStatus::Cancelled
403 );
404 assert_eq!(
405 TaskStatus::from_str("unknown").unwrap(),
406 TaskStatus::Pending
407 );
408 }
409
410 #[test]
411 fn test_task_status_is_active() {
412 assert!(TaskStatus::Pending.is_active());
413 assert!(TaskStatus::InProgress.is_active());
414 assert!(!TaskStatus::Completed.is_active());
415 assert!(!TaskStatus::Failed.is_active());
416 assert!(!TaskStatus::Skipped.is_active());
417 assert!(!TaskStatus::Cancelled.is_active());
418 }
419
420 #[test]
421 fn test_task_status_serialization() {
422 assert_eq!(
423 serde_json::to_string(&TaskStatus::InProgress).unwrap(),
424 "\"in_progress\""
425 );
426 assert_eq!(
427 serde_json::to_string(&TaskStatus::Failed).unwrap(),
428 "\"failed\""
429 );
430 }
431
432 #[test]
437 fn test_task_priority_display() {
438 assert_eq!(TaskPriority::High.to_string(), "high");
439 assert_eq!(TaskPriority::Medium.to_string(), "medium");
440 assert_eq!(TaskPriority::Low.to_string(), "low");
441 }
442
443 #[test]
444 fn test_task_priority_from_str() {
445 assert_eq!(TaskPriority::from_str("high").unwrap(), TaskPriority::High);
446 assert_eq!(TaskPriority::from_str("h").unwrap(), TaskPriority::High);
447 assert_eq!(
448 TaskPriority::from_str("medium").unwrap(),
449 TaskPriority::Medium
450 );
451 assert_eq!(TaskPriority::from_str("med").unwrap(), TaskPriority::Medium);
452 assert_eq!(TaskPriority::from_str("low").unwrap(), TaskPriority::Low);
453 assert_eq!(TaskPriority::from_str("l").unwrap(), TaskPriority::Low);
454 assert_eq!(
455 TaskPriority::from_str("unknown").unwrap(),
456 TaskPriority::Medium
457 );
458 }
459
460 #[test]
465 fn test_task_new() {
466 let task = Task::new("1", "Test task");
467 assert_eq!(task.id, "1");
468 assert_eq!(task.content, "Test task");
469 assert_eq!(task.status, TaskStatus::Pending);
470 assert_eq!(task.priority, TaskPriority::Medium);
471 assert!(task.tool.is_none());
472 assert!(task.dependencies.is_empty());
473 assert!(task.success_criteria.is_none());
474 }
475
476 #[test]
477 fn test_task_builder() {
478 let task = Task::new("1", "Test task")
479 .with_priority(TaskPriority::High)
480 .with_status(TaskStatus::InProgress)
481 .with_tool("bash")
482 .with_dependencies(vec!["step-0".to_string()])
483 .with_success_criteria("Command exits with 0");
484
485 assert_eq!(task.priority, TaskPriority::High);
486 assert_eq!(task.status, TaskStatus::InProgress);
487 assert_eq!(task.tool, Some("bash".to_string()));
488 assert_eq!(task.dependencies, vec!["step-0".to_string()]);
489 assert_eq!(
490 task.success_criteria,
491 Some("Command exits with 0".to_string())
492 );
493 }
494
495 #[test]
496 fn test_task_is_active() {
497 let pending = Task::new("1", "Pending task");
498 let in_progress = Task::new("2", "In progress").with_status(TaskStatus::InProgress);
499 let completed = Task::new("3", "Completed").with_status(TaskStatus::Completed);
500 let failed = Task::new("4", "Failed").with_status(TaskStatus::Failed);
501 let cancelled = Task::new("5", "Cancelled").with_status(TaskStatus::Cancelled);
502
503 assert!(pending.is_active());
504 assert!(in_progress.is_active());
505 assert!(!completed.is_active());
506 assert!(!failed.is_active());
507 assert!(!cancelled.is_active());
508 }
509
510 #[test]
511 fn test_task_serialization() {
512 let task = Task::new("1", "Test task")
513 .with_priority(TaskPriority::High)
514 .with_status(TaskStatus::InProgress);
515
516 let json = serde_json::to_string(&task).unwrap();
517 let parsed: Task = serde_json::from_str(&json).unwrap();
518
519 assert_eq!(parsed.id, task.id);
520 assert_eq!(parsed.content, task.content);
521 assert_eq!(parsed.status, task.status);
522 assert_eq!(parsed.priority, task.priority);
523 }
524
525 #[test]
530 fn test_execution_plan() {
531 let mut plan = ExecutionPlan::new("Test goal", Complexity::Medium);
532
533 plan.add_step(Task::new("step-1", "First step"));
534 plan.add_step(
535 Task::new("step-2", "Second step").with_dependencies(vec!["step-1".to_string()]),
536 );
537
538 assert_eq!(plan.steps.len(), 2);
539 assert_eq!(plan.estimated_steps, 2);
540 assert_eq!(plan.progress(), 0.0);
541
542 plan.steps[0].status = TaskStatus::Completed;
544 assert_eq!(plan.progress(), 0.5);
545
546 let ready = plan.get_ready_steps();
548 assert_eq!(ready.len(), 1);
549 assert_eq!(ready[0].id, "step-2");
550 }
551
552 #[test]
557 fn test_mark_status() {
558 let mut plan = ExecutionPlan::new("Test", Complexity::Simple);
559 plan.add_step(Task::new("s1", "Step 1"));
560 plan.add_step(Task::new("s2", "Step 2"));
561
562 assert_eq!(plan.steps[0].status, TaskStatus::Pending);
563 plan.mark_status("s1", TaskStatus::InProgress);
564 assert_eq!(plan.steps[0].status, TaskStatus::InProgress);
565 plan.mark_status("s1", TaskStatus::Completed);
566 assert_eq!(plan.steps[0].status, TaskStatus::Completed);
567 plan.mark_status("s999", TaskStatus::Failed);
569 assert_eq!(plan.steps[1].status, TaskStatus::Pending);
570 }
571
572 #[test]
573 fn test_pending_count() {
574 let mut plan = ExecutionPlan::new("Test", Complexity::Simple);
575 plan.add_step(Task::new("s1", "Step 1"));
576 plan.add_step(Task::new("s2", "Step 2"));
577 plan.add_step(Task::new("s3", "Step 3"));
578
579 assert_eq!(plan.pending_count(), 3);
580 plan.mark_status("s1", TaskStatus::Completed);
581 assert_eq!(plan.pending_count(), 2);
582 plan.mark_status("s2", TaskStatus::Failed);
583 assert_eq!(plan.pending_count(), 1);
584 plan.mark_status("s3", TaskStatus::InProgress);
585 assert_eq!(plan.pending_count(), 0);
586 }
587
588 #[test]
589 fn test_has_deadlock() {
590 let mut plan = ExecutionPlan::new("Test", Complexity::Simple);
592 plan.add_step(Task::new("s1", "Step 1").with_dependencies(vec!["s2".to_string()]));
593 plan.add_step(Task::new("s2", "Step 2").with_dependencies(vec!["s1".to_string()]));
594
595 assert!(plan.has_deadlock());
596
597 let mut plan2 = ExecutionPlan::new("Test", Complexity::Simple);
599 plan2.add_step(Task::new("s1", "Step 1"));
600 assert!(!plan2.has_deadlock());
601
602 let mut plan3 = ExecutionPlan::new("Test", Complexity::Simple);
604 plan3.add_step(Task::new("s1", "Step 1"));
605 plan3.add_step(Task::new("s2", "Step 2").with_dependencies(vec!["s1".to_string()]));
606 plan3.mark_status("s1", TaskStatus::Failed);
607 assert!(plan3.has_deadlock()); }
609
610 #[test]
611 fn test_get_ready_steps_parallel() {
612 let mut plan = ExecutionPlan::new("Test", Complexity::Medium);
614 plan.add_step(Task::new("s1", "Step 1"));
615 plan.add_step(Task::new("s2", "Step 2"));
616 plan.add_step(Task::new("s3", "Step 3"));
617
618 let ready = plan.get_ready_steps();
619 assert_eq!(ready.len(), 3);
620 }
621
622 #[test]
623 fn test_get_ready_steps_wave() {
624 let mut plan = ExecutionPlan::new("Test", Complexity::Medium);
626 plan.add_step(Task::new("s1", "Step 1"));
627 plan.add_step(Task::new("s2", "Step 2"));
628 plan.add_step(
629 Task::new("s3", "Step 3").with_dependencies(vec!["s1".to_string(), "s2".to_string()]),
630 );
631
632 let ready = plan.get_ready_steps();
634 assert_eq!(ready.len(), 2);
635 let ids: Vec<&str> = ready.iter().map(|s| s.id.as_str()).collect();
636 assert!(ids.contains(&"s1"));
637 assert!(ids.contains(&"s2"));
638
639 plan.mark_status("s1", TaskStatus::Completed);
641 plan.mark_status("s2", TaskStatus::Completed);
642
643 let ready = plan.get_ready_steps();
645 assert_eq!(ready.len(), 1);
646 assert_eq!(ready[0].id, "s3");
647 }
648
649 #[test]
654 fn test_agent_goal() {
655 let mut goal = AgentGoal::new("Complete task")
656 .with_criteria(vec!["Criterion 1".to_string(), "Criterion 2".to_string()]);
657
658 assert_eq!(goal.description, "Complete task");
659 assert_eq!(goal.success_criteria.len(), 2);
660 assert_eq!(goal.progress, 0.0);
661 assert!(!goal.achieved);
662
663 goal.update_progress(0.5);
664 assert_eq!(goal.progress, 0.5);
665
666 goal.mark_achieved();
667 assert!(goal.achieved);
668 assert_eq!(goal.progress, 1.0);
669 assert!(goal.achieved_at.is_some());
670 }
671
672 #[test]
673 fn test_complexity_levels() {
674 assert_eq!(
675 serde_json::to_string(&Complexity::Simple).unwrap(),
676 "\"Simple\""
677 );
678 assert_eq!(
679 serde_json::to_string(&Complexity::Complex).unwrap(),
680 "\"Complex\""
681 );
682 }
683}