use crate::signals::usage_delta;
use crate::{Engine, EngineError, EngineResult, FeedbackInput};
use hippmem_core::model::unit::MemoryUnit;
use hippmem_core::score::UnitScore;
use hippmem_core::time::{Clock, SystemClock};
use hippmem_store::activation_log::{ActivationLogger, ActivationRecord};
use hippmem_store::kv::KvStore;
impl Engine {
pub fn feedback(&self, input: FeedbackInput) -> EngineResult<()> {
let clock = SystemClock;
let now = clock.now();
let logger = ActivationLogger::new(self.store.db_arc());
let signal = signal_to_string(&input.signal);
let rec = ActivationRecord {
retrieval_id: input.retrieval_id,
used_memory_ids: input.used_memory_ids.iter().map(|id| id.0).collect(),
signal: signal.clone(),
recorded_at_ms: now.as_i64(),
};
logger
.record(&rec)
.map_err(|e| EngineError::Internal(format!("activation_log: {}", e)))?;
let delta = usage_delta(&signal);
if delta != 0.0 && !input.used_memory_ids.is_empty() {
let kv = KvStore::new(self.store.db_arc());
for id in &input.used_memory_ids {
if let Some(raw) = kv
.get(&id.0)
.map_err(|e| EngineError::Store(e.to_string()))?
{
let (mut unit, _): (MemoryUnit, _) =
bincode::serde::decode_from_slice(&raw, bincode::config::standard())
.map_err(|e| EngineError::Internal(e.to_string()))?;
let new_usage = (unit.activation.usage_score.value() + delta).clamp(0.0, 1.0);
unit.activation.usage_score = UnitScore::new(new_usage);
let encoded = bincode::serde::encode_to_vec(&unit, bincode::config::standard())
.map_err(|e| EngineError::Internal(e.to_string()))?;
kv.put(id.0, &encoded)
.map_err(|e| EngineError::Store(e.to_string()))?;
}
}
}
Ok(())
}
}
fn signal_to_string(s: &crate::UsageSignal) -> String {
match s {
crate::UsageSignal::Referenced => "Referenced".into(),
crate::UsageSignal::UserConfirmedCorrect => "UserConfirmedCorrect".into(),
crate::UsageSignal::TaskSucceeded => "TaskSucceeded".into(),
crate::UsageSignal::UserRejected => "UserRejected".into(),
}
}