hippmem_engine/
feedback_api.rs1use crate::signals::usage_delta;
9use crate::{Engine, EngineError, EngineResult, FeedbackInput};
10use hippmem_core::model::unit::MemoryUnit;
11use hippmem_core::score::UnitScore;
12use hippmem_core::time::{Clock, SystemClock};
13use hippmem_store::activation_log::{ActivationLogger, ActivationRecord};
14use hippmem_store::kv::KvStore;
15
16impl Engine {
17 pub fn feedback(&self, input: FeedbackInput) -> EngineResult<()> {
23 let clock = SystemClock;
24 let now = clock.now();
25
26 let logger = ActivationLogger::new(self.store.db_arc());
27 let signal = signal_to_string(&input.signal);
28 let rec = ActivationRecord {
29 retrieval_id: input.retrieval_id,
30 used_memory_ids: input.used_memory_ids.iter().map(|id| id.0).collect(),
32 signal: signal.clone(),
33 recorded_at_ms: now.as_i64(),
34 };
35 logger
36 .record(&rec)
37 .map_err(|e| EngineError::Internal(format!("activation_log: {}", e)))?;
38
39 let delta = usage_delta(&signal);
41 let apply_delta = |ids: &[u128], amount: f32| -> EngineResult<()> {
42 let kv = KvStore::new(self.store.db_arc());
43 for id in ids {
44 if let Some(raw) = kv.get(id).map_err(|e| EngineError::Store(e.to_string()))? {
45 let (mut unit, _): (MemoryUnit, _) =
46 bincode::serde::decode_from_slice(&raw, bincode::config::standard())
47 .map_err(|e| EngineError::Internal(e.to_string()))?;
48 let new_usage = (unit.activation.usage_score.value() + amount).clamp(0.0, 1.0);
49 unit.activation.usage_score = UnitScore::new(new_usage);
50 let encoded = bincode::serde::encode_to_vec(&unit, bincode::config::standard())
51 .map_err(|e| EngineError::Internal(e.to_string()))?;
52 kv.put(*id, &encoded)
53 .map_err(|e| EngineError::Store(e.to_string()))?;
54 }
55 }
56 Ok(())
57 };
58 if delta != 0.0 && !input.used_memory_ids.is_empty() {
59 let ids: Vec<u128> = input.used_memory_ids.iter().map(|id| id.0).collect();
60 apply_delta(&ids, delta)?;
61 }
62 if matches!(input.signal, crate::UsageSignal::UserRejected)
69 && input.used_memory_ids.is_empty()
70 {
71 if let Ok(records) = logger.read_all() {
72 if let Some(rec) = records
73 .iter()
74 .find(|r| r.retrieval_id == input.retrieval_id && r.signal == "retrieve")
75 {
76 apply_delta(&rec.used_memory_ids, RESULT_SET_REJECT_DELTA)?;
77 }
78 }
79 }
80
81 Ok(())
82 }
83}
84
85const RESULT_SET_REJECT_DELTA: f32 = -0.05;
89
90fn signal_to_string(s: &crate::UsageSignal) -> String {
91 match s {
92 crate::UsageSignal::Referenced => "Referenced".into(),
93 crate::UsageSignal::UserConfirmedCorrect => "UserConfirmedCorrect".into(),
94 crate::UsageSignal::TaskSucceeded => "TaskSucceeded".into(),
95 crate::UsageSignal::UserRejected => "UserRejected".into(),
96 }
97}