use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct AutoFeedbackConfig {
pub min_samples_for_update: usize,
pub error_threshold: f64,
pub update_cooldown_ms: u64,
pub max_feedback_history: usize,
pub enabled: bool,
}
impl AutoFeedbackConfig {
pub fn new() -> Self {
Self {
min_samples_for_update: 10,
error_threshold: 0.5,
update_cooldown_ms: 60000, max_feedback_history: 100,
enabled: true,
}
}
pub fn with_params(
min_samples: usize,
error_threshold: f64,
cooldown_ms: u64,
max_history: usize,
) -> Self {
Self {
min_samples_for_update: min_samples,
error_threshold: error_threshold.clamp(0.1, 1.0),
update_cooldown_ms: cooldown_ms,
max_feedback_history: max_history,
enabled: true,
}
}
pub fn should_trigger_update(
&self,
feedback_count: usize,
last_update_ms: u64,
current_error: f64,
) -> bool {
if !self.enabled {
return false;
}
if current_error > self.error_threshold {
return true;
}
if feedback_count < self.min_samples_for_update {
return false;
}
let current_time = Instant::now().elapsed().as_millis() as u64;
if current_time.saturating_sub(last_update_ms) < self.update_cooldown_ms {
return false;
}
true
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn disable(&mut self) {
self.enabled = false;
}
}
impl Default for AutoFeedbackConfig {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct AutoFeedbackTrigger {
config: AutoFeedbackConfig,
last_update_time_ms: AtomicU64,
feedback_count: AtomicU64,
}
impl AutoFeedbackTrigger {
pub fn new(config: AutoFeedbackConfig) -> Self {
Self {
config,
last_update_time_ms: AtomicU64::new(0),
feedback_count: AtomicU64::new(0),
}
}
pub fn record_feedback(&self) {
self.feedback_count.fetch_add(1, Ordering::Relaxed);
}
pub fn should_trigger(&self, current_error: f64) -> bool {
let count = self.feedback_count.load(Ordering::Relaxed) as usize;
let last_update = self.last_update_time_ms.load(Ordering::Relaxed);
self.config
.should_trigger_update(count, last_update, current_error)
}
pub fn mark_updated(&self) {
let current_time = Instant::now().elapsed().as_millis() as u64;
self.last_update_time_ms
.store(current_time, Ordering::Relaxed);
self.feedback_count.store(0, Ordering::Relaxed);
}
pub fn get_feedback_count(&self) -> u64 {
self.feedback_count.load(Ordering::Relaxed)
}
pub fn update_config(&mut self, config: AutoFeedbackConfig) {
self.config = config;
}
pub fn config(&self) -> &AutoFeedbackConfig {
&self.config
}
}
impl Default for AutoFeedbackTrigger {
fn default() -> Self {
Self::new(AutoFeedbackConfig::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auto_feedback_config() {
let config = AutoFeedbackConfig::new();
assert!(config.enabled);
assert_eq!(config.min_samples_for_update, 10);
assert!(!config.should_trigger_update(5, 0, 0.1)); assert!(config.should_trigger_update(5, 0, 0.6));
let current_time = Instant::now().elapsed().as_millis() as u64;
if current_time > config.update_cooldown_ms {
assert!(config.should_trigger_update(15, 0, 0.1)); }
}
#[test]
fn test_auto_feedback_trigger() {
let config = AutoFeedbackConfig::with_params(5, 0.5, 1000, 50);
let trigger = AutoFeedbackTrigger::new(config);
assert!(!trigger.should_trigger(0.1));
for _ in 0..5 {
trigger.record_feedback();
}
assert!(trigger.should_trigger(0.6));
trigger.mark_updated();
assert_eq!(trigger.get_feedback_count(), 0);
}
#[test]
fn test_config_enable_disable() {
let mut config = AutoFeedbackConfig::new();
assert!(config.enabled);
config.disable();
assert!(!config.enabled);
assert!(!config.should_trigger_update(100, 0, 1.0));
config.enable();
assert!(config.enabled);
}
}