Skip to main content

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}