ai_agents_reasoning/
lib.rs1mod config;
4mod evaluation;
5mod metadata;
6mod mode;
7mod plan;
8mod planning;
9
10pub use config::{ReasoningConfig, ReflectionConfig};
11pub use evaluation::{CriterionResult, EvaluationResult, ReflectionAttempt};
12pub use metadata::{ReasoningMetadata, ReflectionMetadata};
13pub use mode::{ReasoningMode, ReasoningOutput, ReflectionMode};
14pub use plan::{Plan, PlanAction, PlanStatus, PlanStep, StepStatus};
15pub use planning::{
16 PlanAvailableActions, PlanReflectionConfig, PlanningConfig, StepFailureAction, StringOrList,
17};
18
19#[cfg(test)]
20mod tests {
21 use super::*;
22
23 #[test]
24 fn test_full_reasoning_config() {
25 let yaml = r#"
26mode: plan_and_execute
27judge_llm: router
28output: tagged
29max_iterations: 10
30planning:
31 planner_llm: router
32 max_steps: 15
33 available:
34 tools: all
35 skills:
36 - analyze
37 - summarize
38 reflection:
39 enabled: true
40 on_step_failure: replan
41 max_replans: 3
42"#;
43 let config: ReasoningConfig = serde_yaml::from_str(yaml).unwrap();
44 assert_eq!(config.mode, ReasoningMode::PlanAndExecute);
45 assert!(config.is_enabled());
46 assert!(config.needs_planning());
47
48 let planning = config.get_planning().unwrap();
49 assert_eq!(planning.max_steps, 15);
50 assert!(planning.available.tools.is_all());
51 assert!(!planning.available.skills.is_all());
52 assert!(planning.available.skills.allows("analyze"));
53 assert!(planning.reflection.enabled);
54 }
55
56 #[test]
57 fn test_full_reflection_config() {
58 let yaml = r#"
59enabled: auto
60evaluator_llm: router
61max_retries: 3
62criteria:
63 - "Response addresses the question"
64 - "Response is helpful"
65 - "Response is accurate"
66pass_threshold: 0.75
67"#;
68 let config: ReflectionConfig = serde_yaml::from_str(yaml).unwrap();
69 assert!(config.is_auto());
70 assert!(config.requires_evaluation());
71 assert_eq!(config.max_retries, 3);
72 assert_eq!(config.criteria.len(), 3);
73 assert_eq!(config.pass_threshold, 0.75);
74 }
75
76 #[test]
77 fn test_plan_workflow() {
78 let mut plan = Plan::new("Analyze weather and make recommendation");
79
80 let step1 = PlanStep::new(
81 "Get weather for Seoul",
82 PlanAction::tool("weather", serde_json::json!({"city": "Seoul"})),
83 )
84 .with_id("get_seoul");
85
86 let step2 = PlanStep::new(
87 "Get weather for Tokyo",
88 PlanAction::tool("weather", serde_json::json!({"city": "Tokyo"})),
89 )
90 .with_id("get_tokyo");
91
92 let step3 = PlanStep::new(
93 "Compare weather data",
94 PlanAction::think("Compare the weather"),
95 )
96 .with_id("compare")
97 .with_dependencies(vec!["get_seoul".to_string(), "get_tokyo".to_string()]);
98
99 let step4 = PlanStep::new(
100 "Make recommendation",
101 PlanAction::respond("Based on {{ compare }}..."),
102 )
103 .with_id("respond")
104 .with_dependencies(vec!["compare".to_string()]);
105
106 plan.add_step(step1);
107 plan.add_step(step2);
108 plan.add_step(step3);
109 plan.add_step(step4);
110
111 assert_eq!(plan.steps.len(), 4);
112
113 let executable: Vec<_> = plan
115 .steps
116 .iter()
117 .filter(|s| {
118 s.status.is_pending()
119 && s.dependencies
120 .iter()
121 .all(|dep| plan.is_step_completed_pub(dep))
122 })
123 .collect();
124 assert_eq!(executable.len(), 2);
125 }
126
127 #[test]
128 fn test_evaluation_workflow() {
129 let criteria = vec![
130 CriterionResult::pass("Addresses question"),
131 CriterionResult::fail("Complete response", "Missing conclusion"),
132 ];
133
134 let evaluation = EvaluationResult::new(false, 0.6).with_criteria(criteria);
135
136 assert!(!evaluation.passed);
137 assert!(!evaluation.passed_all());
138 assert_eq!(evaluation.failed_criteria().count(), 1);
139 assert!((evaluation.pass_rate() - 0.5).abs() < 0.01);
140
141 let attempt = ReflectionAttempt::new("Initial response", evaluation.clone())
142 .with_feedback("Add a conclusion to complete the response");
143
144 assert!(!attempt.passed());
145 assert!(attempt.feedback.is_some());
146
147 let mut metadata = ReflectionMetadata::new(evaluation);
148 metadata.add_attempt(attempt);
149 assert_eq!(metadata.attempts, 1);
150 }
151
152 #[test]
153 fn test_reasoning_metadata() {
154 let metadata = ReasoningMetadata::new(ReasoningMode::CoT)
155 .with_thinking("Step 1: Understand\nStep 2: Analyze\nStep 3: Conclude")
156 .with_iterations(1)
157 .with_auto_detected(false);
158
159 assert_eq!(metadata.mode_used, ReasoningMode::CoT);
160 assert!(metadata.has_thinking());
161 assert!(!metadata.auto_detected);
162 }
163
164 impl Plan {
165 fn is_step_completed_pub(&self, step_id: &str) -> bool {
166 self.steps
167 .iter()
168 .find(|s| s.id == step_id)
169 .map(|s| s.status.is_completed())
170 .unwrap_or(false)
171 }
172 }
173}