Skip to main content

vtcode_core/code/code_completion/learning/
feedback.rs

1/// Feedback processor for analyzing user interactions
2pub struct FeedbackProcessor {
3    feedback_history: Vec<FeedbackEntry>,
4}
5
6#[derive(Debug, Clone)]
7struct FeedbackEntry {
8    suggestion: String,
9    accepted: bool,
10    context: String,
11    timestamp: std::time::SystemTime,
12}
13
14impl Default for FeedbackProcessor {
15    fn default() -> Self {
16        Self::new()
17    }
18}
19
20impl FeedbackProcessor {
21    pub fn new() -> Self {
22        Self {
23            feedback_history: Vec::new(),
24        }
25    }
26
27    /// Process user feedback
28    pub fn process(&mut self, suggestion: &str, accepted: bool, context: &str) {
29        let entry = FeedbackEntry {
30            suggestion: suggestion.into(),
31            accepted,
32            context: context.into(),
33            timestamp: std::time::SystemTime::now(),
34        };
35
36        self.feedback_history.push(entry);
37
38        // Keep only recent feedback (last 1000 entries)
39        if self.feedback_history.len() > 1000 {
40            self.feedback_history.remove(0);
41        }
42    }
43
44    /// Get acceptance rate for a specific pattern
45    pub fn get_acceptance_rate(&self, pattern: &str) -> f64 {
46        use std::time::{Duration, SystemTime};
47
48        let now = SystemTime::now();
49        let mut weighted_sum = 0.0;
50        let mut total_weight = 0.0;
51
52        for entry in &self.feedback_history {
53            if !entry.suggestion.contains(pattern) {
54                continue;
55            }
56
57            let age_weight = now
58                .duration_since(entry.timestamp)
59                .map(|duration| {
60                    // Recent feedback counts more. Anything older than 30 days is heavily down-weighted.
61                    let thirty_days = Duration::from_secs(60 * 60 * 24 * 30);
62                    1.0 - (duration.as_secs_f64() / thirty_days.as_secs_f64()).min(0.9)
63                })
64                .unwrap_or(1.0);
65
66            let context_weight = if entry.context.is_empty() {
67                0.8
68            } else {
69                // Prefer matches where the usage context was recorded.
70                1.0
71            };
72
73            let weight = age_weight * context_weight;
74            total_weight += weight;
75            if entry.accepted {
76                weighted_sum += weight;
77            }
78        }
79
80        if total_weight == 0.0 {
81            return 0.5;
82        }
83
84        weighted_sum / total_weight
85    }
86}