1use crate::config::constants::execution;
7use crate::tools::lru_cache::LruCache;
8use crate::tools::pattern_detection::{PatternDetector, ToolEvent};
9use crate::tools::tool_middleware::{MiddlewareChain, MiddlewareResult, ToolRequest, ToolResponse};
10use crate::tools::{UnifiedErrorKind, UnifiedToolError};
11use serde_json::Value;
12use std::collections::hash_map::DefaultHasher;
13use std::hash::Hasher;
14use std::sync::Arc;
15use std::sync::RwLock;
16use std::sync::atomic::{AtomicU64, Ordering};
17use std::time::Duration;
18use tokio::time::timeout;
19use tracing::warn;
20
21static REQUEST_ID: AtomicU64 = AtomicU64::new(1);
22
23fn next_request_id() -> String {
24 let id = REQUEST_ID.fetch_add(1, Ordering::Relaxed);
25 format!("req-{id}")
26}
27
28#[repr(align(64))]
33struct PaddedAtomicU64(AtomicU64);
34
35impl PaddedAtomicU64 {
36 fn new(value: u64) -> Self {
37 Self(AtomicU64::new(value))
38 }
39
40 #[inline]
41 fn fetch_add(&self, value: u64, ordering: Ordering) -> u64 {
42 self.0.fetch_add(value, ordering)
43 }
44
45 #[inline]
46 fn load(&self, ordering: Ordering) -> u64 {
47 self.0.load(ordering)
48 }
49}
50
51#[derive(Clone, Debug)]
53pub struct ExecutorStats {
54 pub total_calls: u64,
55 pub successful_calls: u64,
56 pub failed_calls: u64,
57 pub cache_hits: u64,
58 pub cache_misses: u64,
59 pub avg_duration_ms: u64,
60 pub patterns_detected: usize,
61}
62
63struct AtomicStats {
68 total_calls: PaddedAtomicU64,
69 successful_calls: PaddedAtomicU64,
70 failed_calls: PaddedAtomicU64,
71 cache_hits: PaddedAtomicU64,
72 cache_misses: PaddedAtomicU64,
73 total_duration_ms: PaddedAtomicU64,
74 duration_count: PaddedAtomicU64,
75}
76
77impl AtomicStats {
78 fn new() -> Self {
79 Self {
80 total_calls: PaddedAtomicU64::new(0),
81 successful_calls: PaddedAtomicU64::new(0),
82 failed_calls: PaddedAtomicU64::new(0),
83 cache_hits: PaddedAtomicU64::new(0),
84 cache_misses: PaddedAtomicU64::new(0),
85 total_duration_ms: PaddedAtomicU64::new(0),
86 duration_count: PaddedAtomicU64::new(0),
87 }
88 }
89
90 #[inline]
91 fn record_success(&self, duration_ms: u64) {
92 self.successful_calls.fetch_add(1, Ordering::Relaxed);
93 self.total_duration_ms.fetch_add(duration_ms, Ordering::Relaxed);
94 self.duration_count.fetch_add(1, Ordering::Relaxed);
95 }
96
97 #[inline]
98 fn record_failure(&self, duration_ms: u64) {
99 self.failed_calls.fetch_add(1, Ordering::Relaxed);
100 self.total_duration_ms.fetch_add(duration_ms, Ordering::Relaxed);
101 self.duration_count.fetch_add(1, Ordering::Relaxed);
102 }
103
104 fn snapshot(&self) -> ExecutorStats {
105 let count = self.duration_count.load(Ordering::Relaxed);
106 let total = self.total_duration_ms.load(Ordering::Relaxed);
107 let avg = total.checked_div(count).unwrap_or(0);
108 ExecutorStats {
109 total_calls: self.total_calls.load(Ordering::Relaxed),
110 successful_calls: self.successful_calls.load(Ordering::Relaxed),
111 failed_calls: self.failed_calls.load(Ordering::Relaxed),
112 cache_hits: self.cache_hits.load(Ordering::Relaxed),
113 cache_misses: self.cache_misses.load(Ordering::Relaxed),
114 avg_duration_ms: avg,
115 patterns_detected: 0, }
117 }
118}
119
120pub struct CachedToolExecutor {
122 cache: Arc<LruCache<Value>>,
124 middleware: MiddlewareChain,
126 patterns: Arc<RwLock<PatternDetector>>,
137 stats: Arc<AtomicStats>,
139}
140
141impl CachedToolExecutor {
142 pub fn new() -> Self {
148 Self::with_config(1000, Duration::from_secs(3600), 3)
149 }
150
151 pub fn with_config(cache_capacity: usize, cache_ttl: Duration, pattern_window: usize) -> Self {
153 let cache = Arc::new(LruCache::<Value>::new(cache_capacity, cache_ttl));
154 let middleware = MiddlewareChain::new();
155 let patterns = Arc::new(RwLock::new(PatternDetector::new(pattern_window)));
156 let stats = Arc::new(AtomicStats::new());
157
158 Self { cache, middleware, patterns, stats }
159 }
160
161 pub fn with_middleware(mut self, mw: Arc<dyn crate::tools::tool_middleware::Middleware>) -> Self {
163 self.middleware = self.middleware.push(mw);
164 self
165 }
166
167 pub async fn execute(&self, tool_name: &str, args: Value) -> MiddlewareResult<Value> {
169 let r = self.execute_shared_owned(tool_name, args).await?;
171 Ok((*r).clone())
172 }
173
174 pub async fn execute_shared(&self, tool_name: &str, args: Arc<Value>) -> MiddlewareResult<Arc<Value>> {
178 let start = std::time::Instant::now();
179 let cache_key = make_cache_key(tool_name, &args);
180
181 self.stats.total_calls.fetch_add(1, Ordering::Relaxed);
183
184 let owned_args = Arc::clone(&args);
186 let req = ToolRequest {
187 id: next_request_id(),
188 tool_name: tool_name.into(),
189 args: (*owned_args).clone(),
190 metadata: Some(Default::default()),
191 };
192
193 if let Err(err) = self.middleware.before_execute(&req).await {
195 self.record_error(tool_name, start.elapsed(), &req, &err).await;
196 return Err(err);
197 }
198
199 if let Some(result) = self.cache.get(&cache_key).await {
201 let duration_ms = start.elapsed().as_millis() as u64;
202 self.stats.record_success(duration_ms);
203 self.stats.cache_hits.fetch_add(1, Ordering::Relaxed);
204
205 let res = ToolResponse {
206 id: req.id.clone(),
207 success: true,
208 result: Some((*result).clone()),
209 error: None,
210 duration_ms: Some(duration_ms),
211 cache_hit: Some(true),
212 };
213 if let Err(err) = self.middleware.after_execute(&req, &res).await {
214 self.record_error(tool_name, start.elapsed(), &req, &err).await;
215 return Err(err);
216 }
217
218 self.record_pattern(tool_name, true, duration_ms).await;
220
221 return Ok(Arc::clone(&result));
222 }
223
224 self.stats.cache_misses.fetch_add(1, Ordering::Relaxed);
226
227 let timeout_secs = execution::DEFAULT_TIMEOUT_SECS;
229 let result = match timeout(
230 Duration::from_secs(timeout_secs),
231 self.execute_tool_internal(tool_name, &owned_args),
232 )
233 .await
234 {
235 Ok(result) => match result {
236 Ok(result) => result,
237 Err(err) => {
238 self.record_error(tool_name, start.elapsed(), &req, &err).await;
239 return Err(err);
240 }
241 },
242 Err(_) => {
243 let err = UnifiedToolError::new(
244 UnifiedErrorKind::Timeout,
245 format!("Tool '{tool_name}' timed out after {timeout_secs} seconds"),
246 )
247 .with_tool_name(tool_name);
248 self.record_error(tool_name, start.elapsed(), &req, &err).await;
249 return Err(err);
250 }
251 };
252
253 let duration_ms = start.elapsed().as_millis() as u64;
254
255 let arc_res = Arc::new(result);
257
258 self.cache.insert_arc(cache_key, Arc::clone(&arc_res)).await;
260
261 self.stats.record_success(duration_ms);
263
264 let res = ToolResponse {
265 id: req.id.clone(),
266 success: true,
267 result: Some((*arc_res).clone()),
268 error: None,
269 duration_ms: Some(duration_ms),
270 cache_hit: Some(false),
271 };
272
273 if let Err(err) = self.middleware.after_execute(&req, &res).await {
275 self.record_error(tool_name, start.elapsed(), &req, &err).await;
276 return Err(err);
277 }
278
279 self.record_pattern(tool_name, true, duration_ms).await;
281
282 Ok(arc_res)
283 }
284
285 pub async fn execute_shared_owned(&self, tool_name: &str, args: Value) -> MiddlewareResult<Arc<Value>> {
287 let arg = Arc::new(args);
288 self.execute_shared(tool_name, arg).await
289 }
290
291 async fn execute_tool_internal(&self, _tool_name: &str, _args: &Value) -> MiddlewareResult<Value> {
293 Ok(serde_json::json!({"status": "ok"}))
296 }
297
298 async fn record_error(&self, tool_name: &str, elapsed: Duration, req: &ToolRequest, err: &UnifiedToolError) {
299 self.stats.record_failure(elapsed.as_millis() as u64);
300
301 let _ = self.middleware.on_error(req, err).await;
302 self.record_pattern(tool_name, false, elapsed.as_millis() as u64).await;
303 }
304
305 async fn record_pattern(&self, tool_name: &str, success: bool, duration_ms: u64) {
307 let mut patterns = self.patterns.write().unwrap_or_else(|poisoned| {
308 warn!("pattern detector lock poisoned; recovering");
309 poisoned.into_inner()
310 });
311 patterns.record_event(ToolEvent {
312 tool_name: tool_name.to_string(),
313 success,
314 duration_ms,
315 timestamp: std::time::Instant::now(),
316 });
317 }
318
319 pub async fn stats(&self) -> ExecutorStats {
321 let mut stats = self.stats.snapshot();
322 let patterns = self.patterns.read().unwrap_or_else(|poisoned| {
323 warn!("pattern detector lock poisoned; recovering");
324 poisoned.into_inner()
325 });
326 stats.patterns_detected = patterns.patterns().len();
327 stats
328 }
329
330 pub async fn cache_stats(&self) -> crate::tools::lru_cache::CacheStats {
332 self.cache.stats().await
333 }
334
335 pub async fn patterns(&self) -> Vec<crate::tools::pattern_detection::DetectedPattern> {
337 let patterns = self.patterns.read().unwrap_or_else(|poisoned| {
338 warn!("pattern detector lock poisoned; recovering");
339 poisoned.into_inner()
340 });
341 patterns.patterns().to_vec()
342 }
343
344 pub async fn feature_vector(&self) -> Vec<f64> {
346 let patterns = self.patterns.read().unwrap_or_else(|poisoned| {
347 warn!("pattern detector lock poisoned; recovering");
348 poisoned.into_inner()
349 });
350 patterns.feature_vector()
351 }
352
353 pub async fn clear_cache(&self) {
355 self.cache.clear().await;
356 }
357
358 pub async fn clear_patterns(&self) {
360 let mut patterns = self.patterns.write().unwrap_or_else(|poisoned| {
361 warn!("pattern detector lock poisoned; recovering");
362 poisoned.into_inner()
363 });
364 patterns.reset();
365 }
366
367 pub async fn report(&self) {
369 let stats = self.stats().await;
370 let cache_stats = self.cache_stats().await;
371 let patterns = self.patterns().await;
372
373 println!("\n=== ToolExecutor Report ===\n");
374
375 println!("Execution Statistics:");
376 println!(" Total calls: {}", stats.total_calls);
377 println!(" Successful: {}", stats.successful_calls);
378 println!(" Failed: {}", stats.failed_calls);
379 println!(" Avg duration: {}ms", stats.avg_duration_ms);
380
381 println!("\nCache Performance:");
382 println!(" Hits: {}", cache_stats.hits);
383 println!(" Misses: {}", cache_stats.misses);
384 println!(" Hit rate: {:.1}%", cache_stats.hit_rate());
385 println!(" Evictions: {}", cache_stats.evictions);
386 println!(" Expirations: {}", cache_stats.expirations);
387
388 println!("\nWorkflow Patterns ({} detected):", patterns.len());
389 for (i, pattern) in patterns.iter().take(5).enumerate() {
390 println!(" {}. {:?}", i + 1, pattern.sequence);
391 println!(" Frequency: {}, Confidence: {:.1}%", pattern.frequency, pattern.confidence * 100.0);
392 }
393
394 println!("\n");
395 }
396}
397
398#[inline]
402fn make_cache_key(tool_name: &str, args: &Value) -> String {
403 use std::hash::Hash;
404 let mut hasher = DefaultHasher::new();
405 tool_name.hash(&mut hasher);
407 if let Ok(bytes) = serde_json::to_vec(args) {
409 hasher.write(&bytes);
410 }
411 let h = hasher.finish();
412 format!("{tool_name}:{h:x}")
413}
414
415impl Default for CachedToolExecutor {
418 fn default() -> Self {
419 Self::new()
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426 use crate::tools::tool_middleware::{LoggingMiddleware, MetricsMiddleware, MiddlewareResult};
427 use crate::tools::{UnifiedErrorKind, UnifiedToolError};
428 use async_trait::async_trait;
429 use std::sync::Arc;
430
431 struct FailingMiddleware;
432
433 #[async_trait]
434 impl crate::tools::tool_middleware::Middleware for FailingMiddleware {
435 async fn before_execute(&self, _req: &ToolRequest) -> MiddlewareResult<()> {
436 Err(UnifiedToolError::new(UnifiedErrorKind::ExecutionFailed, "middleware rejected request"))
437 }
438 }
439
440 #[tokio::test]
441 async fn test_executor_basic() -> anyhow::Result<()> {
442 let executor = CachedToolExecutor::new();
443
444 let result = executor.execute("test_tool", serde_json::json!({"arg": 1})).await?;
446 assert_eq!(result, serde_json::json!({"status": "ok"}));
447
448 let stats = executor.stats().await;
449 assert_eq!(stats.total_calls, 1);
450 assert_eq!(stats.cache_misses, 1);
451
452 Ok(())
453 }
454
455 #[tokio::test]
456 async fn test_executor_cache_hit() -> anyhow::Result<()> {
457 let executor = CachedToolExecutor::new();
458
459 executor.execute("test_tool", serde_json::json!({"arg": 1})).await?;
461 executor.execute("test_tool", serde_json::json!({"arg": 1})).await?;
462
463 let stats = executor.stats().await;
464 assert_eq!(stats.total_calls, 2);
465 assert_eq!(stats.successful_calls, 2);
466 assert_eq!(stats.cache_hits, 1);
467 assert_eq!(stats.cache_misses, 1);
468
469 Ok(())
470 }
471
472 #[tokio::test]
473 async fn cache_hit_after_repeat_call() {
474 let exec = CachedToolExecutor::with_config(10, Duration::from_secs(60), 3);
475 let args = serde_json::json!({"x": 1});
476
477 let _first = exec.execute("test_tool", args.clone()).await.unwrap();
479
480 let _second = exec.execute("test_tool", args.clone()).await.unwrap();
482
483 let stats = exec.stats().await;
485 assert!(stats.cache_hits >= 1);
486 assert!(stats.cache_misses >= 1);
487 }
488
489 #[tokio::test]
490 async fn test_executor_with_middleware() -> anyhow::Result<()> {
491 let executor = CachedToolExecutor::new().with_middleware(LoggingMiddleware::new("test"));
492
493 executor.execute("test_tool", serde_json::json!({})).await?;
494
495 let stats = executor.stats().await;
496 assert_eq!(stats.total_calls, 1);
497
498 Ok(())
499 }
500
501 #[tokio::test]
502 async fn test_executor_patterns() -> anyhow::Result<()> {
503 let executor = CachedToolExecutor::new();
504
505 for _ in 0..6 {
508 executor.execute("tool_a", serde_json::json!({})).await?;
509 executor.execute("tool_b", serde_json::json!({})).await?;
510 }
511
512 let patterns = executor.patterns().await;
513 assert!(!patterns.is_empty());
514
515 Ok(())
516 }
517
518 #[tokio::test]
519 async fn test_executor_clear() -> anyhow::Result<()> {
520 let executor = CachedToolExecutor::new();
521
522 executor.execute("test", serde_json::json!({})).await?;
523
524 let stats_before = executor.stats().await;
525 assert_eq!(stats_before.total_calls, 1);
526
527 executor.clear_cache().await;
528 executor.clear_patterns().await;
529
530 let cache_stats = executor.cache_stats().await;
531 assert_eq!(cache_stats.hits + cache_stats.misses, 0);
532
533 Ok(())
534 }
535
536 #[tokio::test]
537 async fn test_executor_reports_typed_error_to_middleware_metrics() {
538 let metrics = MetricsMiddleware::new();
539 let executor = CachedToolExecutor::new()
540 .with_middleware(metrics.clone())
541 .with_middleware(Arc::new(FailingMiddleware));
542
543 let err = executor.execute("test", serde_json::json!({})).await;
544 assert!(err.is_err());
545
546 let stats = executor.stats().await;
547 assert_eq!(stats.failed_calls, 1);
548
549 let snapshot = metrics.snapshot().await;
550 assert_eq!(snapshot.total_calls, 1);
551 assert_eq!(snapshot.failed_calls, 1);
552 }
553}