1use std::collections::{HashMap, VecDeque};
7use std::sync::Arc;
8use tokio::sync::Mutex;
9
10use crate::error::Error;
11
12pub struct PerformanceTracker {
14 real_time_metrics: Arc<Mutex<RealTimeMetrics>>,
16 performance_history: Arc<Mutex<VecDeque<PerformanceRecord>>>,
18 resource_tracker: Arc<Mutex<ResourceUtilizationTracker>>,
20 optimizer: Arc<Mutex<PerformanceOptimizer>>,
22 config: PerformanceTrackerConfig,
24}
25
26impl Default for PerformanceTracker {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl PerformanceTracker {
33 pub fn new() -> Self {
34 Self {
35 real_time_metrics: Arc::new(Mutex::new(RealTimeMetrics::new())),
36 performance_history: Arc::new(Mutex::new(VecDeque::new())),
37 resource_tracker: Arc::new(Mutex::new(ResourceUtilizationTracker::new())),
38 optimizer: Arc::new(Mutex::new(PerformanceOptimizer::new())),
39 config: PerformanceTrackerConfig::default(),
40 }
41 }
42
43 pub async fn record_tool_call(
45 &self,
46 tool_call_id: u32,
47 duration_ms: u64,
48 cost: f64,
49 ) -> Result<(), Error> {
50 let timestamp = chrono::Utc::now();
51
52 {
54 let mut metrics = self.real_time_metrics.lock().await;
55 metrics.record_tool_call(tool_call_id, duration_ms, cost, timestamp);
56 }
57
58 {
60 let mut tracker = self.resource_tracker.lock().await;
61 tracker.record_tool_call(duration_ms).await;
62 }
63
64 {
66 let mut optimizer = self.optimizer.lock().await;
67 optimizer
68 .analyze_tool_call(tool_call_id, duration_ms, cost)
69 .await;
70 }
71
72 tracing::debug!(
73 "Recorded tool call {}: {}ms, ${:.4}",
74 tool_call_id,
75 duration_ms,
76 cost
77 );
78
79 Ok(())
80 }
81
82 pub async fn record_operation(
84 &self,
85 operation_name: &str,
86 tool_calls_used: u32,
87 duration_ms: u64,
88 success: bool,
89 metadata: serde_json::Value,
90 ) -> Result<(), Error> {
91 let timestamp = chrono::Utc::now();
92 let record = PerformanceRecord {
93 id: format!("op_{}_{}", operation_name, timestamp.timestamp()),
94 operation_name: operation_name.to_string(),
95 timestamp,
96 tool_calls_used,
97 duration_ms,
98 success,
99 metadata,
100 };
101
102 {
104 let mut history = self.performance_history.lock().await;
105 history.push_back(record.clone());
106
107 if history.len() > self.config.max_history_records {
109 history.pop_front();
110 }
111 }
112
113 {
115 let mut metrics = self.real_time_metrics.lock().await;
116 metrics.record_operation(&record);
117 }
118
119 {
121 let mut optimizer = self.optimizer.lock().await;
122 optimizer.analyze_operation(&record).await;
123 }
124
125 tracing::info!(
126 "Recorded operation '{}': {} tool calls, {}ms, success: {}",
127 operation_name,
128 tool_calls_used,
129 duration_ms,
130 success
131 );
132
133 Ok(())
134 }
135
136 pub async fn get_real_time_metrics(&self) -> Result<RealTimeMetrics, Error> {
138 let metrics = self.real_time_metrics.lock().await;
139 Ok(metrics.clone())
140 }
141
142 pub async fn get_performance_summary(
144 &self,
145 window_duration: chrono::Duration,
146 ) -> Result<PerformanceSummary, Error> {
147 let now = chrono::Utc::now();
148 let cutoff = now - window_duration;
149
150 let history = self.performance_history.lock().await;
151 let recent_records: Vec<_> = history
152 .iter()
153 .filter(|record| record.timestamp >= cutoff)
154 .cloned()
155 .collect();
156
157 if recent_records.is_empty() {
158 return Ok(PerformanceSummary::empty());
159 }
160
161 let total_tool_calls: u32 = recent_records.iter().map(|r| r.tool_calls_used).sum();
162 let total_duration_ms: u64 = recent_records.iter().map(|r| r.duration_ms).sum();
163 let success_count = recent_records.iter().filter(|r| r.success).count();
164 let total_records = recent_records.len();
165
166 let avg_tool_calls_per_operation = total_tool_calls as f64 / total_records as f64;
167 let avg_duration_per_operation = total_duration_ms as f64 / total_records as f64;
168 let success_rate = success_count as f64 / total_records as f64;
169
170 let minutes = window_duration.num_minutes() as f64;
172 let throughput_per_minute = if minutes > 0.0 {
173 total_records as f64 / minutes
174 } else {
175 0.0
176 };
177
178 let efficiency_score = self.calculate_efficiency_score(&recent_records);
180
181 let bottlenecks = self.identify_bottlenecks(&recent_records).await;
183
184 Ok(PerformanceSummary {
185 time_window: window_duration,
186 total_operations: total_records,
187 total_tool_calls,
188 total_duration_ms,
189 avg_tool_calls_per_operation,
190 avg_duration_per_operation,
191 success_rate,
192 throughput_per_minute,
193 efficiency_score,
194 bottlenecks,
195 top_performing_operations: self.get_top_performing_operations(&recent_records),
196 recommendations: self
197 .generate_optimization_recommendations(&recent_records)
198 .await,
199 })
200 }
201
202 pub async fn get_resource_utilization(&self) -> Result<ResourceUtilization, Error> {
204 let tracker = self.resource_tracker.lock().await;
205 Ok(tracker.get_utilization_stats())
206 }
207
208 pub async fn get_optimization_recommendations(
210 &self,
211 ) -> Result<Vec<OptimizationRecommendation>, Error> {
212 let optimizer = self.optimizer.lock().await;
213 Ok(optimizer.get_recommendations().await)
214 }
215
216 pub async fn reset(&self) -> Result<(), Error> {
218 {
219 let mut metrics = self.real_time_metrics.lock().await;
220 metrics.reset();
221 }
222
223 {
224 let mut history = self.performance_history.lock().await;
225 history.clear();
226 }
227
228 {
229 let mut tracker = self.resource_tracker.lock().await;
230 tracker.reset();
231 }
232
233 {
234 let mut optimizer = self.optimizer.lock().await;
235 optimizer.reset();
236 }
237
238 tracing::info!("Performance tracker reset");
239 Ok(())
240 }
241
242 fn calculate_efficiency_score(&self, records: &[PerformanceRecord]) -> f64 {
244 if records.is_empty() {
245 return 1.0;
246 }
247
248 let success_rate =
249 records.iter().filter(|r| r.success).count() as f64 / records.len() as f64;
250
251 let avg_tool_calls =
253 records.iter().map(|r| r.tool_calls_used).sum::<u32>() as f64 / records.len() as f64;
254 let tool_call_efficiency = (avg_tool_calls / 100.0).min(1.0); let avg_duration =
258 records.iter().map(|r| r.duration_ms).sum::<u64>() as f64 / records.len() as f64;
259 let time_efficiency = (60000.0 / avg_duration).min(1.0); (success_rate * 0.4 + tool_call_efficiency * 0.3 + time_efficiency * 0.3).clamp(0.0, 1.0)
263 }
264
265 async fn identify_bottlenecks(
267 &self,
268 records: &[PerformanceRecord],
269 ) -> Vec<PerformanceBottleneck> {
270 let mut bottlenecks = Vec::new();
271
272 let avg_duration =
274 records.iter().map(|r| r.duration_ms).sum::<u64>() as f64 / records.len() as f64;
275 let slow_operations: Vec<_> = records
276 .iter()
277 .filter(|r| r.duration_ms as f64 > avg_duration * 2.0)
278 .collect();
279
280 if !slow_operations.is_empty() {
281 bottlenecks.push(PerformanceBottleneck {
282 type_: BottleneckType::SlowOperations,
283 severity: if slow_operations.len() as f64 / records.len() as f64 > 0.3 {
284 BottleneckSeverity::High
285 } else {
286 BottleneckSeverity::Medium
287 },
288 description: format!(
289 "{} operations are taking longer than average ({}ms avg)",
290 slow_operations.len(),
291 avg_duration as u64
292 ),
293 affected_operations: slow_operations
294 .iter()
295 .map(|r| r.operation_name.clone())
296 .collect(),
297 });
298 }
299
300 let avg_tool_calls =
302 records.iter().map(|r| r.tool_calls_used).sum::<u32>() as f64 / records.len() as f64;
303 let high_tool_call_ops: Vec<_> = records
304 .iter()
305 .filter(|r| r.tool_calls_used as f64 > avg_tool_calls * 2.0)
306 .collect();
307
308 if !high_tool_call_ops.is_empty() {
309 bottlenecks.push(PerformanceBottleneck {
310 type_: BottleneckType::ExcessiveToolCalls,
311 severity: if high_tool_call_ops.len() as f64 / records.len() as f64 > 0.2 {
312 BottleneckSeverity::High
313 } else {
314 BottleneckSeverity::Low
315 },
316 description: format!(
317 "{} operations use excessive tool calls ({} avg)",
318 high_tool_call_ops.len(),
319 avg_tool_calls
320 ),
321 affected_operations: high_tool_call_ops
322 .iter()
323 .map(|r| r.operation_name.clone())
324 .collect(),
325 });
326 }
327
328 let failure_rate =
330 records.iter().filter(|r| !r.success).count() as f64 / records.len() as f64;
331 if failure_rate > 0.1 {
332 bottlenecks.push(PerformanceBottleneck {
333 type_: BottleneckType::HighFailureRate,
334 severity: if failure_rate > 0.3 {
335 BottleneckSeverity::Critical
336 } else {
337 BottleneckSeverity::High
338 },
339 description: format!("High failure rate: {:.1}%", failure_rate * 100.0),
340 affected_operations: records
341 .iter()
342 .filter(|r| !r.success)
343 .map(|r| r.operation_name.clone())
344 .collect(),
345 });
346 }
347
348 bottlenecks
349 }
350
351 fn get_top_performing_operations(
353 &self,
354 records: &[PerformanceRecord],
355 ) -> Vec<OperationPerformance> {
356 let mut operations: HashMap<String, Vec<&PerformanceRecord>> = HashMap::new();
357
358 for record in records {
360 operations
361 .entry(record.operation_name.clone())
362 .or_default()
363 .push(record);
364 }
365
366 let mut performance_data = Vec::new();
368 for (op_name, op_records) in operations {
369 let total_calls: u32 = op_records.iter().map(|r| r.tool_calls_used).sum();
370 let total_duration: u64 = op_records.iter().map(|r| r.duration_ms).sum();
371 let success_count = op_records.iter().filter(|r| r.success).count();
372 let total_ops = op_records.len();
373
374 performance_data.push(OperationPerformance {
375 operation_name: op_name,
376 total_executions: total_ops,
377 avg_tool_calls: total_calls as f64 / total_ops as f64,
378 avg_duration_ms: total_duration as f64 / total_ops as f64,
379 success_rate: success_count as f64 / total_ops as f64,
380 efficiency_score: self.calculate_operation_efficiency(&op_records),
381 });
382 }
383
384 performance_data
386 .sort_by(|a, b| b.efficiency_score.partial_cmp(&a.efficiency_score).unwrap());
387 performance_data.into_iter().take(5).collect()
388 }
389
390 fn calculate_operation_efficiency(&self, records: &[&PerformanceRecord]) -> f64 {
392 if records.is_empty() {
393 return 0.0;
394 }
395
396 let success_rate =
397 records.iter().filter(|r| r.success).count() as f64 / records.len() as f64;
398 let avg_duration =
399 records.iter().map(|r| r.duration_ms).sum::<u64>() as f64 / records.len() as f64;
400 let avg_tool_calls =
401 records.iter().map(|r| r.tool_calls_used).sum::<u32>() as f64 / records.len() as f64;
402
403 let duration_score = (10000.0 / avg_duration).min(1.0);
405 let tool_call_score = (50.0 / avg_tool_calls).min(1.0);
406
407 success_rate * 0.5 + duration_score * 0.3 + tool_call_score * 0.2
408 }
409
410 async fn generate_optimization_recommendations(
412 &self,
413 records: &[PerformanceRecord],
414 ) -> Vec<OptimizationRecommendation> {
415 let mut recommendations = Vec::new();
416
417 let avg_duration =
419 records.iter().map(|r| r.duration_ms).sum::<u64>() as f64 / records.len() as f64;
420 let avg_tool_calls =
421 records.iter().map(|r| r.tool_calls_used).sum::<u32>() as f64 / records.len() as f64;
422
423 if avg_duration > 30000.0 {
424 recommendations.push(OptimizationRecommendation {
425 priority: RecommendationPriority::High,
426 category: RecommendationCategory::Performance,
427 title: "Optimize Operation Duration".to_string(),
428 description: format!(
429 "Average operation duration is {:.1} seconds. Consider optimizing algorithms or parallelizing operations.",
430 avg_duration / 1000.0
431 ),
432 estimated_impact: ImpactLevel::High,
433 implementation_effort: ImplementationEffort::Medium,
434 });
435 }
436
437 if avg_tool_calls > 50.0 {
438 recommendations.push(OptimizationRecommendation {
439 priority: RecommendationPriority::Medium,
440 category: RecommendationCategory::ToolEfficiency,
441 title: "Reduce Tool Call Count".to_string(),
442 description: format!(
443 "Average tool calls per operation is {:.1}. Consider batching operations or caching results.",
444 avg_tool_calls
445 ),
446 estimated_impact: ImpactLevel::Medium,
447 implementation_effort: ImplementationEffort::Low,
448 });
449 }
450
451 let operation_counts: HashMap<String, usize> = records
453 .iter()
454 .map(|r| (r.operation_name.clone(), 1))
455 .fold(HashMap::new(), |mut acc, (name, count)| {
456 *acc.entry(name).or_insert(0) += count;
457 acc
458 });
459
460 let frequently_used_ops: Vec<_> = operation_counts
461 .iter()
462 .filter(|(_, count)| **count >= 5)
463 .map(|(name, _)| name)
464 .collect();
465
466 if !frequently_used_ops.is_empty() {
467 recommendations.push(OptimizationRecommendation {
468 priority: RecommendationPriority::Medium,
469 category: RecommendationCategory::Caching,
470 title: "Implement Result Caching".to_string(),
471 description: format!(
472 "Operations {} are executed frequently. Consider implementing result caching to improve performance.",
473 frequently_used_ops.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(", ")
474 ),
475 estimated_impact: ImpactLevel::High,
476 implementation_effort: ImplementationEffort::Low,
477 });
478 }
479
480 recommendations
481 }
482}
483
484#[derive(Debug, Clone)]
486pub struct RealTimeMetrics {
487 pub current_tool_call_id: u32,
488 pub total_tool_calls_today: u32,
489 pub avg_duration_ms: f64,
490 pub avg_cost_per_call: f64,
491 pub current_throughput_per_minute: f64,
492 pub error_rate: f64,
493 pub last_update: chrono::DateTime<chrono::Utc>,
494 pub rolling_window: VecDeque<PerformanceSample>,
495}
496
497impl Default for RealTimeMetrics {
498 fn default() -> Self {
499 Self::new()
500 }
501}
502
503impl RealTimeMetrics {
504 pub fn new() -> Self {
505 Self {
506 current_tool_call_id: 0,
507 total_tool_calls_today: 0,
508 avg_duration_ms: 0.0,
509 avg_cost_per_call: 0.0,
510 current_throughput_per_minute: 0.0,
511 error_rate: 0.0,
512 last_update: chrono::Utc::now(),
513 rolling_window: VecDeque::new(),
514 }
515 }
516
517 pub fn record_tool_call(
518 &mut self,
519 tool_call_id: u32,
520 duration_ms: u64,
521 cost: f64,
522 timestamp: chrono::DateTime<chrono::Utc>,
523 ) {
524 self.current_tool_call_id = tool_call_id;
525 self.total_tool_calls_today += 1;
526 self.last_update = timestamp;
527
528 let sample = PerformanceSample {
530 timestamp,
531 duration_ms,
532 cost,
533 success: true, };
535
536 self.rolling_window.push_back(sample);
537 if self.rolling_window.len() > 100 {
538 self.rolling_window.pop_front();
539 }
540
541 let total_samples = self.rolling_window.len() as f64;
543 self.avg_duration_ms = self
544 .rolling_window
545 .iter()
546 .map(|s| s.duration_ms as f64)
547 .sum::<f64>()
548 / total_samples;
549
550 self.avg_cost_per_call =
551 self.rolling_window.iter().map(|s| s.cost).sum::<f64>() / total_samples;
552
553 if let (Some(first), Some(last)) = (self.rolling_window.front(), self.rolling_window.back())
555 {
556 let time_span = (last.timestamp - first.timestamp).num_seconds() as f64;
557 if time_span > 0.0 {
558 self.current_throughput_per_minute = (total_samples * 60.0) / time_span;
559 }
560 }
561 }
562
563 pub fn record_operation(&mut self, record: &PerformanceRecord) {
564 let total_ops = self.rolling_window.len() as f64 + 1.0;
566 let successful_ops = self.rolling_window.iter().filter(|s| s.success).count() as f64
567 + if record.success { 1.0 } else { 0.0 };
568 self.error_rate = 1.0 - (successful_ops / total_ops);
569 }
570
571 pub fn reset(&mut self) {
572 self.current_tool_call_id = 0;
573 self.total_tool_calls_today = 0;
574 self.avg_duration_ms = 0.0;
575 self.avg_cost_per_call = 0.0;
576 self.current_throughput_per_minute = 0.0;
577 self.error_rate = 0.0;
578 self.rolling_window.clear();
579 }
580}
581
582#[derive(Debug, Clone)]
584pub struct PerformanceSample {
585 timestamp: chrono::DateTime<chrono::Utc>,
586 duration_ms: u64,
587 cost: f64,
588 success: bool,
589}
590
591#[derive(Debug)]
593struct ResourceUtilizationTracker {
594 memory_usage_mb: VecDeque<f64>,
595 cpu_usage_percent: VecDeque<f64>,
596 network_io_mb: VecDeque<f64>,
597 disk_io_mb: VecDeque<f64>,
598 peak_memory_mb: f64,
599 peak_cpu_percent: f64,
600}
601
602impl ResourceUtilizationTracker {
603 fn new() -> Self {
604 Self {
605 memory_usage_mb: VecDeque::new(),
606 cpu_usage_percent: VecDeque::new(),
607 network_io_mb: VecDeque::new(),
608 disk_io_mb: VecDeque::new(),
609 peak_memory_mb: 0.0,
610 peak_cpu_percent: 0.0,
611 }
612 }
613
614 async fn record_tool_call(&mut self, duration_ms: u64) {
615 let simulated_memory = 100.0 + (duration_ms as f64 / 1000.0) * 10.0;
617 let simulated_cpu = 10.0 + (duration_ms as f64 / 1000.0) * 5.0;
618 let simulated_network = duration_ms as f64 / 1000.0 * 2.0;
619 let simulated_disk = duration_ms as f64 / 1000.0 * 1.0;
620
621 self.memory_usage_mb.push_back(simulated_memory);
622 self.cpu_usage_percent.push_back(simulated_cpu);
623 self.network_io_mb.push_back(simulated_network);
624 self.disk_io_mb.push_back(simulated_disk);
625
626 let max_samples = 50;
628 if self.memory_usage_mb.len() > max_samples {
629 self.memory_usage_mb.pop_front();
630 self.cpu_usage_percent.pop_front();
631 self.network_io_mb.pop_front();
632 self.disk_io_mb.pop_front();
633 }
634
635 self.peak_memory_mb = self.peak_memory_mb.max(simulated_memory);
637 self.peak_cpu_percent = self.peak_cpu_percent.max(simulated_cpu);
638 }
639
640 fn get_utilization_stats(&self) -> ResourceUtilization {
641 let avg_memory = if !self.memory_usage_mb.is_empty() {
642 self.memory_usage_mb.iter().sum::<f64>() / self.memory_usage_mb.len() as f64
643 } else {
644 0.0
645 };
646
647 let avg_cpu = if !self.cpu_usage_percent.is_empty() {
648 self.cpu_usage_percent.iter().sum::<f64>() / self.cpu_usage_percent.len() as f64
649 } else {
650 0.0
651 };
652
653 let avg_network = if !self.network_io_mb.is_empty() {
654 self.network_io_mb.iter().sum::<f64>() / self.network_io_mb.len() as f64
655 } else {
656 0.0
657 };
658
659 let avg_disk = if !self.disk_io_mb.is_empty() {
660 self.disk_io_mb.iter().sum::<f64>() / self.disk_io_mb.len() as f64
661 } else {
662 0.0
663 };
664
665 ResourceUtilization {
666 avg_memory_usage_mb: avg_memory,
667 peak_memory_usage_mb: self.peak_memory_mb,
668 avg_cpu_usage_percent: avg_cpu,
669 peak_cpu_usage_percent: self.peak_cpu_percent,
670 avg_network_io_mb_per_call: avg_network,
671 avg_disk_io_mb_per_call: avg_disk,
672 memory_efficiency: if self.peak_memory_mb > 0.0 {
673 avg_memory / self.peak_memory_mb
674 } else {
675 1.0
676 },
677 }
678 }
679
680 fn reset(&mut self) {
681 self.memory_usage_mb.clear();
682 self.cpu_usage_percent.clear();
683 self.network_io_mb.clear();
684 self.disk_io_mb.clear();
685 self.peak_memory_mb = 0.0;
686 self.peak_cpu_percent = 0.0;
687 }
688}
689
690#[derive(Debug)]
692struct PerformanceOptimizer {
693 analysis_cache: HashMap<String, PerformanceAnalysis>,
694 optimization_history: VecDeque<OptimizationAction>,
695}
696
697impl PerformanceOptimizer {
698 fn new() -> Self {
699 Self {
700 analysis_cache: HashMap::new(),
701 optimization_history: VecDeque::new(),
702 }
703 }
704
705 async fn analyze_tool_call(&mut self, _tool_call_id: u32, duration_ms: u64, cost: f64) {
706 if duration_ms > 10_000 {
708 tracing::warn!("Slow tool call detected: {}ms", duration_ms);
710 }
711
712 if cost > 0.01 {
713 tracing::info!("High-cost tool call: ${:.4}", cost);
715 }
716 }
717
718 async fn analyze_operation(&mut self, record: &PerformanceRecord) {
719 let analysis_key = format!(
721 "{}_{}",
722 record.operation_name,
723 record.timestamp.date_naive()
724 );
725
726 let analysis = PerformanceAnalysis {
727 operation_name: record.operation_name.clone(),
728 avg_duration_ms: record.duration_ms as f64,
729 avg_tool_calls: record.tool_calls_used as f64,
730 success_rate: if record.success { 1.0 } else { 0.0 },
731 last_analysis: chrono::Utc::now(),
732 };
733
734 self.analysis_cache.insert(analysis_key, analysis);
735 }
736
737 async fn get_recommendations(&self) -> Vec<OptimizationRecommendation> {
738 let mut recommendations = Vec::new();
740
741 for analysis in self.analysis_cache.values() {
742 if analysis.avg_duration_ms > 30000.0 {
743 recommendations.push(OptimizationRecommendation {
744 priority: RecommendationPriority::High,
745 category: RecommendationCategory::Performance,
746 title: format!("Optimize {} duration", analysis.operation_name),
747 description: format!(
748 "Operation '{}' averages {:.1} seconds. Consider optimization.",
749 analysis.operation_name,
750 analysis.avg_duration_ms / 1000.0
751 ),
752 estimated_impact: ImpactLevel::High,
753 implementation_effort: ImplementationEffort::Medium,
754 });
755 }
756
757 if analysis.avg_tool_calls > 30.0 {
758 recommendations.push(OptimizationRecommendation {
759 priority: RecommendationPriority::Medium,
760 category: RecommendationCategory::ToolEfficiency,
761 title: format!("Reduce {} tool calls", analysis.operation_name),
762 description: format!(
763 "Operation '{}' uses {:.1} tool calls on average.",
764 analysis.operation_name, analysis.avg_tool_calls
765 ),
766 estimated_impact: ImpactLevel::Medium,
767 implementation_effort: ImplementationEffort::Low,
768 });
769 }
770 }
771
772 recommendations
773 }
774
775 fn reset(&mut self) {
776 self.analysis_cache.clear();
777 self.optimization_history.clear();
778 }
779}
780
781#[derive(Debug, Clone)]
783pub struct PerformanceRecord {
784 pub id: String,
785 pub operation_name: String,
786 pub timestamp: chrono::DateTime<chrono::Utc>,
787 pub tool_calls_used: u32,
788 pub duration_ms: u64,
789 pub success: bool,
790 pub metadata: serde_json::Value,
791}
792
793#[derive(Debug, Clone)]
795#[allow(dead_code)]
796struct PerformanceAnalysis {
797 operation_name: String,
798 avg_duration_ms: f64,
799 avg_tool_calls: f64,
800 success_rate: f64,
801 last_analysis: chrono::DateTime<chrono::Utc>,
802}
803
804#[derive(Debug, Clone)]
806pub struct PerformanceSummary {
807 pub time_window: chrono::Duration,
808 pub total_operations: usize,
809 pub total_tool_calls: u32,
810 pub total_duration_ms: u64,
811 pub avg_tool_calls_per_operation: f64,
812 pub avg_duration_per_operation: f64,
813 pub success_rate: f64,
814 pub throughput_per_minute: f64,
815 pub efficiency_score: f64,
816 pub bottlenecks: Vec<PerformanceBottleneck>,
817 pub top_performing_operations: Vec<OperationPerformance>,
818 pub recommendations: Vec<OptimizationRecommendation>,
819}
820
821impl PerformanceSummary {
822 pub fn empty() -> Self {
823 Self {
824 time_window: chrono::Duration::minutes(0),
825 total_operations: 0,
826 total_tool_calls: 0,
827 total_duration_ms: 0,
828 avg_tool_calls_per_operation: 0.0,
829 avg_duration_per_operation: 0.0,
830 success_rate: 1.0,
831 throughput_per_minute: 0.0,
832 efficiency_score: 1.0,
833 bottlenecks: Vec::new(),
834 top_performing_operations: Vec::new(),
835 recommendations: Vec::new(),
836 }
837 }
838}
839
840#[derive(Debug, Clone)]
842pub struct PerformanceBottleneck {
843 pub type_: BottleneckType,
844 pub severity: BottleneckSeverity,
845 pub description: String,
846 pub affected_operations: Vec<String>,
847}
848
849#[derive(Debug, Clone, PartialEq)]
850pub enum BottleneckType {
851 SlowOperations,
852 ExcessiveToolCalls,
853 HighFailureRate,
854 ResourceContention,
855 NetworkLatency,
856}
857
858#[derive(Debug, Clone, PartialEq)]
859pub enum BottleneckSeverity {
860 Low,
861 Medium,
862 High,
863 Critical,
864}
865
866#[derive(Debug, Clone)]
868pub struct OperationPerformance {
869 pub operation_name: String,
870 pub total_executions: usize,
871 pub avg_tool_calls: f64,
872 pub avg_duration_ms: f64,
873 pub success_rate: f64,
874 pub efficiency_score: f64,
875}
876
877#[derive(Debug, Clone)]
879pub struct OptimizationRecommendation {
880 pub priority: RecommendationPriority,
881 pub category: RecommendationCategory,
882 pub title: String,
883 pub description: String,
884 pub estimated_impact: ImpactLevel,
885 pub implementation_effort: ImplementationEffort,
886}
887
888#[derive(Debug, Clone, PartialEq)]
889pub enum RecommendationPriority {
890 Low,
891 Medium,
892 High,
893 Critical,
894}
895
896#[derive(Debug, Clone, PartialEq)]
897pub enum RecommendationCategory {
898 Performance,
899 ToolEfficiency,
900 Caching,
901 ResourceManagement,
902 ErrorHandling,
903}
904
905#[derive(Debug, Clone, PartialEq)]
906pub enum ImpactLevel {
907 Low,
908 Medium,
909 High,
910 VeryHigh,
911}
912
913#[derive(Debug, Clone, PartialEq)]
914pub enum ImplementationEffort {
915 Low,
916 Medium,
917 High,
918}
919
920#[derive(Debug, Clone)]
922pub struct ResourceUtilization {
923 pub avg_memory_usage_mb: f64,
924 pub peak_memory_usage_mb: f64,
925 pub avg_cpu_usage_percent: f64,
926 pub peak_cpu_usage_percent: f64,
927 pub avg_network_io_mb_per_call: f64,
928 pub avg_disk_io_mb_per_call: f64,
929 pub memory_efficiency: f64,
930}
931
932#[derive(Debug, Clone)]
934#[allow(dead_code)]
935struct OptimizationAction {
936 timestamp: chrono::DateTime<chrono::Utc>,
937 action_type: String,
938 target: String,
939 expected_improvement: f64,
940 actual_improvement: Option<f64>,
941}
942
943#[derive(Debug, Clone)]
945pub struct PerformanceTrackerConfig {
946 pub max_history_records: usize,
947 pub rolling_window_size: usize,
948 pub enable_real_time_monitoring: bool,
949 pub enable_resource_tracking: bool,
950 pub enable_optimization: bool,
951 pub analysis_interval_minutes: u32,
952}
953
954impl Default for PerformanceTrackerConfig {
955 fn default() -> Self {
956 Self {
957 max_history_records: 10000,
958 rolling_window_size: 100,
959 enable_real_time_monitoring: true,
960 enable_resource_tracking: true,
961 enable_optimization: true,
962 analysis_interval_minutes: 5,
963 }
964 }
965}
966
967#[cfg(test)]
968mod tests {
969 use super::*;
970
971 #[tokio::test]
972 async fn test_performance_tracker_creation() {
973 let tracker = PerformanceTracker::new();
974 assert!(tracker.record_tool_call(1, 1000, 0.001).await.is_ok());
975 }
976
977 #[tokio::test]
978 async fn test_real_time_metrics() {
979 let mut metrics = RealTimeMetrics::new();
980 metrics.record_tool_call(1, 2000, 0.002, chrono::Utc::now());
981
982 assert_eq!(metrics.total_tool_calls_today, 1);
983 assert_eq!(metrics.avg_duration_ms, 2000.0);
984 }
985
986 #[tokio::test]
987 async fn test_performance_summary() {
988 let tracker = PerformanceTracker::new();
989
990 let _record = PerformanceRecord {
991 id: "test1".to_string(),
992 operation_name: "test_op".to_string(),
993 timestamp: chrono::Utc::now(),
994 tool_calls_used: 10,
995 duration_ms: 5000,
996 success: true,
997 metadata: serde_json::json!({}),
998 };
999
1000 assert!(tracker
1001 .record_operation("test_op", 10, 5000, true, serde_json::json!({}))
1002 .await
1003 .is_ok());
1004 }
1005}