ryo_executor/decider/mod.rs
1//! Decider: Agent decision-making layer for ryo
2//!
3//! This module provides the abstraction for agents to make decisions
4//! about what action to take next, based on context and world state.
5//!
6//! # Architecture
7//!
8//! ```text
9//! ┌─────────────────────────────────────────────────────────────┐
10//! │ Agent Mode │
11//! ├─────────────────────────────────────────────────────────────┤
12//! │ Plain Mode │ Just execute the given operation │
13//! │ Agentic Mode │ Autonomous decision-making via Decider │
14//! └─────────────────────────────────────────────────────────────┘
15//!
16//! ┌─────────────────────────────────────────────────────────────┐
17//! │ Decider Implementations │
18//! ├─────────────────────────────────────────────────────────────┤
19//! │ PlainDecider │ μs │ Pass-through (no decision) │
20//! │ KeywordDecider │ μs │ Keyword matching │
21//! │ ParameterizedDecider│ μs │ WorldState-based decisions │
22//! │ MurmurationDecider │ μs │ Swarm-like collision avoid │
23//! └─────────────────────────────────────────────────────────────┘
24//! ```
25//!
26//! # Usage
27//!
28//! ```rust,ignore
29//! use ryo_executor::decider::{AgentMode, DeciderConfig, KeywordDecider};
30//!
31//! // Plain mode - just execute operations
32//! let config = DeciderConfig::plain();
33//!
34//! // Agentic mode - autonomous decision-making
35//! let config = DeciderConfig::agentic(Box::new(KeywordDecider::default()));
36//! ```
37
38mod action;
39mod context;
40mod deciders;
41mod state;
42
43pub use action::{Action, ActionKind, ActionResult};
44pub use context::DecisionContext;
45pub use deciders::{
46 ComposableDecider,
47 Decider,
48 // Composable
49 DecisionModifier,
50 ErrorAwareModifier,
51 MurmurationDecider,
52 ParameterizedDecider,
53 PlainDecider,
54 RetryModifier,
55 StallDetectionModifier,
56 SuccessRateModifier,
57};
58pub use state::{AgentState, ErrorInfo, FileState};
59
60use std::sync::Arc;
61
62// ============================================================================
63// Agent Mode
64// ============================================================================
65
66/// Agent execution mode
67#[derive(Debug, Clone, Default)]
68pub enum AgentMode {
69 /// Plain mode: Execute the given operation directly without autonomous decision-making
70 #[default]
71 Plain,
72
73 /// Agentic mode: Use a Decider to make autonomous decisions
74 Agentic(Arc<dyn Decider>),
75}
76
77impl AgentMode {
78 /// Create plain mode
79 pub fn plain() -> Self {
80 Self::Plain
81 }
82
83 /// Create agentic mode with a decider
84 pub fn agentic(decider: impl Decider + 'static) -> Self {
85 Self::Agentic(Arc::new(decider))
86 }
87
88 /// Check if this is plain mode
89 pub fn is_plain(&self) -> bool {
90 matches!(self, Self::Plain)
91 }
92
93 /// Check if this is agentic mode
94 pub fn is_agentic(&self) -> bool {
95 matches!(self, Self::Agentic(_))
96 }
97
98 /// Get the decider if in agentic mode
99 pub fn decider(&self) -> Option<&dyn Decider> {
100 match self {
101 Self::Agentic(d) => Some(d.as_ref()),
102 Self::Plain => None,
103 }
104 }
105}
106
107// ============================================================================
108// Decider Configuration
109// ============================================================================
110
111/// Configuration for agent decision-making
112#[derive(Debug, Clone)]
113pub struct DeciderConfig {
114 /// The execution mode
115 pub mode: AgentMode,
116
117 /// Maximum retries for failed actions
118 pub max_retries: u32,
119
120 /// Enable escalation on repeated failures
121 pub enable_escalation: bool,
122
123 /// Escalation threshold (failure rate that triggers escalation)
124 pub escalation_threshold: f64,
125}
126
127impl Default for DeciderConfig {
128 fn default() -> Self {
129 Self {
130 mode: AgentMode::Plain,
131 max_retries: 3,
132 enable_escalation: true,
133 escalation_threshold: 0.4,
134 }
135 }
136}
137
138impl DeciderConfig {
139 /// Create plain mode configuration
140 pub fn plain() -> Self {
141 Self::default()
142 }
143
144 /// Create agentic mode with parameterized decisions
145 pub fn parameterized() -> Self {
146 Self {
147 mode: AgentMode::agentic(ParameterizedDecider::default()),
148 ..Default::default()
149 }
150 }
151
152 /// Create agentic mode with murmuration (swarm) behavior
153 pub fn murmuration() -> Self {
154 Self {
155 mode: AgentMode::agentic(MurmurationDecider::default()),
156 ..Default::default()
157 }
158 }
159
160 /// Create agentic mode with a custom decider
161 pub fn with_decider(decider: impl Decider + 'static) -> Self {
162 Self {
163 mode: AgentMode::agentic(decider),
164 ..Default::default()
165 }
166 }
167
168 /// Set maximum retries
169 pub fn max_retries(mut self, n: u32) -> Self {
170 self.max_retries = n;
171 self
172 }
173
174 /// Enable or disable escalation
175 pub fn escalation(mut self, enable: bool) -> Self {
176 self.enable_escalation = enable;
177 self
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_agent_mode_plain() {
187 let mode = AgentMode::plain();
188 assert!(mode.is_plain());
189 assert!(!mode.is_agentic());
190 assert!(mode.decider().is_none());
191 }
192
193 #[test]
194 fn test_agent_mode_agentic() {
195 let mode = AgentMode::agentic(ParameterizedDecider::default());
196 assert!(!mode.is_plain());
197 assert!(mode.is_agentic());
198 assert!(mode.decider().is_some());
199 assert_eq!(mode.decider().unwrap().name(), "ParameterizedDecider");
200 }
201
202 #[test]
203 fn test_decider_config() {
204 let config = DeciderConfig::parameterized();
205 assert!(config.mode.is_agentic());
206 assert_eq!(config.max_retries, 3);
207
208 let config = DeciderConfig::plain().max_retries(5).escalation(false);
209 assert!(config.mode.is_plain());
210 assert_eq!(config.max_retries, 5);
211 assert!(!config.enable_escalation);
212 }
213}