1use std::time::Duration;
6
7#[derive(Debug, Clone)]
9pub struct TimeoutConfig {
10 pub skill_timeout: Duration,
12
13 pub step_timeout: Duration,
15
16 pub tool_timeout: Duration,
18
19 pub timeout_action: TimeoutAction,
21}
22
23#[derive(Debug, Clone, Default, PartialEq, Eq)]
25pub enum TimeoutAction {
26 #[default]
28 Fail,
29
30 Skip,
32
33 Partial,
35}
36
37impl Default for TimeoutConfig {
38 fn default() -> Self {
39 Self {
40 skill_timeout: Duration::from_secs(300), step_timeout: Duration::from_secs(60), tool_timeout: Duration::from_secs(30), timeout_action: TimeoutAction::Fail,
44 }
45 }
46}
47
48impl TimeoutConfig {
49 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn with_skill_timeout(mut self, timeout: Duration) -> Self {
56 self.skill_timeout = timeout;
57 self
58 }
59
60 pub fn with_step_timeout(mut self, timeout: Duration) -> Self {
62 self.step_timeout = timeout;
63 self
64 }
65
66 pub fn with_tool_timeout(mut self, timeout: Duration) -> Self {
68 self.tool_timeout = timeout;
69 self
70 }
71
72 pub fn with_timeout_action(mut self, action: TimeoutAction) -> Self {
74 self.timeout_action = action;
75 self
76 }
77}
78
79#[derive(Debug, Clone)]
81pub struct RetryConfig {
82 pub max_retries: usize,
84
85 pub initial_delay: Duration,
87
88 pub max_delay: Duration,
90
91 pub backoff: BackoffStrategy,
93
94 pub retryable_errors: Vec<RetryableError>,
96}
97
98#[derive(Debug, Clone, Default, PartialEq, Eq)]
100pub enum BackoffStrategy {
101 Fixed,
103
104 Exponential,
106
107 #[default]
109 ExponentialJitter,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114pub enum RetryableError {
115 Network,
117
118 RateLimit,
120
121 Timeout,
123
124 ServerError,
126
127 All,
129}
130
131impl Default for RetryConfig {
132 fn default() -> Self {
133 Self {
134 max_retries: 3,
135 initial_delay: Duration::from_millis(100),
136 max_delay: Duration::from_secs(10),
137 backoff: BackoffStrategy::ExponentialJitter,
138 retryable_errors: vec![
139 RetryableError::Network,
140 RetryableError::RateLimit,
141 RetryableError::Timeout,
142 ],
143 }
144 }
145}
146
147impl RetryConfig {
148 pub fn new() -> Self {
150 Self::default()
151 }
152
153 pub fn no_retries() -> Self {
155 Self {
156 max_retries: 0,
157 ..Default::default()
158 }
159 }
160
161 pub fn with_max_retries(mut self, max: usize) -> Self {
163 self.max_retries = max;
164 self
165 }
166
167 pub fn with_initial_delay(mut self, delay: Duration) -> Self {
169 self.initial_delay = delay;
170 self
171 }
172
173 pub fn with_max_delay(mut self, delay: Duration) -> Self {
175 self.max_delay = delay;
176 self
177 }
178
179 pub fn with_backoff(mut self, strategy: BackoffStrategy) -> Self {
181 self.backoff = strategy;
182 self
183 }
184
185 pub fn with_retryable_errors(mut self, errors: Vec<RetryableError>) -> Self {
187 self.retryable_errors = errors;
188 self
189 }
190
191 pub fn retry_all_errors(mut self) -> Self {
193 self.retryable_errors = vec![RetryableError::All];
194 self
195 }
196}
197
198#[derive(Debug, Clone, Default)]
200pub struct ExecutionConfig {
201 pub timeout: TimeoutConfig,
203
204 pub retry: RetryConfig,
206}
207
208impl ExecutionConfig {
209 pub fn new() -> Self {
211 Self::default()
212 }
213
214 pub fn with_timeout(mut self, config: TimeoutConfig) -> Self {
216 self.timeout = config;
217 self
218 }
219
220 pub fn with_retry(mut self, config: RetryConfig) -> Self {
222 self.retry = config;
223 self
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
232 fn test_timeout_config_defaults() {
233 let config = TimeoutConfig::default();
234 assert_eq!(config.skill_timeout, Duration::from_secs(300));
235 assert_eq!(config.step_timeout, Duration::from_secs(60));
236 assert_eq!(config.tool_timeout, Duration::from_secs(30));
237 assert_eq!(config.timeout_action, TimeoutAction::Fail);
238 }
239
240 #[test]
241 fn test_timeout_config_builder() {
242 let config = TimeoutConfig::new()
243 .with_skill_timeout(Duration::from_secs(120))
244 .with_step_timeout(Duration::from_secs(30))
245 .with_timeout_action(TimeoutAction::Skip);
246
247 assert_eq!(config.skill_timeout, Duration::from_secs(120));
248 assert_eq!(config.step_timeout, Duration::from_secs(30));
249 assert_eq!(config.timeout_action, TimeoutAction::Skip);
250 }
251
252 #[test]
253 fn test_retry_config_defaults() {
254 let config = RetryConfig::default();
255 assert_eq!(config.max_retries, 3);
256 assert_eq!(config.initial_delay, Duration::from_millis(100));
257 assert_eq!(config.backoff, BackoffStrategy::ExponentialJitter);
258 assert!(config.retryable_errors.contains(&RetryableError::Network));
259 }
260
261 #[test]
262 fn test_retry_config_no_retries() {
263 let config = RetryConfig::no_retries();
264 assert_eq!(config.max_retries, 0);
265 }
266
267 #[test]
268 fn test_retry_config_builder() {
269 let config = RetryConfig::new()
270 .with_max_retries(5)
271 .with_initial_delay(Duration::from_millis(200))
272 .with_backoff(BackoffStrategy::Fixed)
273 .retry_all_errors();
274
275 assert_eq!(config.max_retries, 5);
276 assert_eq!(config.initial_delay, Duration::from_millis(200));
277 assert_eq!(config.backoff, BackoffStrategy::Fixed);
278 assert!(config.retryable_errors.contains(&RetryableError::All));
279 }
280
281 #[test]
282 fn test_execution_config() {
283 let config = ExecutionConfig::new()
284 .with_timeout(TimeoutConfig::new().with_skill_timeout(Duration::from_secs(60)))
285 .with_retry(RetryConfig::no_retries());
286
287 assert_eq!(config.timeout.skill_timeout, Duration::from_secs(60));
288 assert_eq!(config.retry.max_retries, 0);
289 }
290}