Skip to main content

vtcode_core/metrics/
discovery_metrics.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::VecDeque;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct DiscoveryMetrics {
7    pub total_queries: u64,
8    pub successful_queries: u64,
9    pub failed_queries: u64,
10    pub total_time_ms: u64,
11    pub cache_hits: u64,
12    pub recent_queries: VecDeque<QueryRecord>,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct QueryRecord {
17    pub keyword: String,
18    pub result_count: u64,
19    pub response_time_ms: u64,
20    pub timestamp: DateTime<Utc>,
21    pub success: bool,
22}
23
24impl DiscoveryMetrics {
25    pub fn new() -> Self {
26        Self {
27            total_queries: 0,
28            successful_queries: 0,
29            failed_queries: 0,
30            total_time_ms: 0,
31            cache_hits: 0,
32            recent_queries: VecDeque::with_capacity(100),
33        }
34    }
35
36    pub fn record_query(&mut self, keyword: String, result_count: u64, response_time_ms: u64) {
37        self.total_queries += 1;
38        self.successful_queries += 1;
39        self.total_time_ms += response_time_ms;
40
41        let record = QueryRecord {
42            keyword,
43            result_count,
44            response_time_ms,
45            timestamp: Utc::now(),
46            success: true,
47        };
48
49        if self.recent_queries.len() >= 100 {
50            self.recent_queries.pop_front();
51        }
52        self.recent_queries.push_back(record);
53    }
54
55    pub fn record_failure(&mut self, keyword: String) {
56        self.total_queries += 1;
57        self.failed_queries += 1;
58
59        let record = QueryRecord {
60            keyword,
61            result_count: 0,
62            response_time_ms: 0,
63            timestamp: Utc::now(),
64            success: false,
65        };
66
67        if self.recent_queries.len() >= 100 {
68            self.recent_queries.pop_front();
69        }
70        self.recent_queries.push_back(record);
71    }
72
73    pub fn record_cache_hit(&mut self) {
74        self.cache_hits += 1;
75    }
76
77    pub fn avg_response_time_ms(&self) -> u64 {
78        if self.successful_queries > 0 {
79            self.total_time_ms / self.successful_queries
80        } else {
81            0
82        }
83    }
84
85    pub fn hit_rate(&self) -> f64 {
86        if self.total_queries > 0 {
87            self.successful_queries as f64 / self.total_queries as f64
88        } else {
89            0.0
90        }
91    }
92
93    pub fn cache_hit_rate(&self) -> f64 {
94        if self.total_queries > 0 {
95            self.cache_hits as f64 / self.total_queries as f64
96        } else {
97            0.0
98        }
99    }
100}
101
102impl Default for DiscoveryMetrics {
103    fn default() -> Self {
104        Self::new()
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_record_query() {
114        let mut metrics = DiscoveryMetrics::new();
115        metrics.record_query("file".to_owned(), 5, 50);
116
117        assert_eq!(metrics.total_queries, 1);
118        assert_eq!(metrics.successful_queries, 1);
119        assert_eq!(metrics.total_time_ms, 50);
120        assert_eq!(metrics.avg_response_time_ms(), 50);
121    }
122
123    #[test]
124    fn test_record_failure() {
125        let mut metrics = DiscoveryMetrics::new();
126        metrics.record_failure("invalid".to_owned());
127
128        assert_eq!(metrics.total_queries, 1);
129        assert_eq!(metrics.failed_queries, 1);
130        assert_eq!(metrics.hit_rate(), 0.0);
131    }
132
133    #[test]
134    fn test_hit_rate() {
135        let mut metrics = DiscoveryMetrics::new();
136        metrics.record_query("test".to_owned(), 3, 30);
137        metrics.record_query("test2".to_owned(), 2, 25);
138        metrics.record_failure("test3".to_owned());
139
140        assert_eq!(metrics.total_queries, 3);
141        assert_eq!(metrics.hit_rate(), 2.0 / 3.0);
142    }
143
144    #[test]
145    fn test_cache_hit_rate() {
146        let mut metrics = DiscoveryMetrics::new();
147        metrics.record_query("test".to_owned(), 3, 30);
148        metrics.record_cache_hit();
149        metrics.record_cache_hit();
150
151        assert_eq!(metrics.cache_hits, 2);
152        assert!(metrics.cache_hit_rate() > 0.0);
153    }
154
155    #[test]
156    fn test_recent_queries_limit() {
157        let mut metrics = DiscoveryMetrics::new();
158        for i in 0..150 {
159            metrics.record_query(format!("query_{}", i), 1, 10);
160        }
161
162        assert_eq!(metrics.total_queries, 150);
163        assert_eq!(metrics.recent_queries.len(), 100);
164    }
165}