use hippmem_core::model::enums::ContentType;
use hippmem_core::model::unit::WriteContext;
use hippmem_engine::{
Engine, EngineConfig, FeedbackInput, RetrieveContext, RetrieveInput, UsageSignal,
WriteMemoryInput,
};
use hippmem_store::activation_log::ActivationLogger;
use tempfile::tempdir;
fn ctx() -> WriteContext {
WriteContext {
conversation_id: Some(1),
session_id: Some(1),
project_id: None,
task_id: None,
user_id: None,
local_time: hippmem_core::time::Timestamp(1_700_000_000_000),
preceding_memory_ids: vec![],
source_refs: vec![],
}
}
fn retrieve(engine: &Engine, query: &str, top_k: usize) -> hippmem_engine::RetrieveOutput {
engine
.retrieve(RetrieveInput {
query: query.into(),
context: RetrieveContext::default(),
top_k,
max_hops: None,
retrieval_mode: hippmem_core::model::links::RetrievalMode::Balanced,
})
.unwrap()
}
fn write(engine: &Engine, text: &str) -> hippmem_core::ids::MemoryId {
engine
.write(WriteMemoryInput {
content: text.into(),
content_type: Some(ContentType::UserStatement),
context: ctx(),
importance_hint: None,
source_refs: vec![],
})
.unwrap()
.memory_id
}
#[test]
fn activation_log_preserves_generated_memory_ids() {
let dir = tempdir().unwrap();
let engine = Engine::open(EngineConfig {
store_dir: dir.path().join("hippmem.redb"),
..Default::default()
})
.unwrap();
let mid = write(&engine, "小明和李华是高中同学。");
let out = retrieve(&engine, "小明和李华之间有什么关系?", 5);
std::thread::sleep(std::time::Duration::from_millis(2)); engine
.feedback(FeedbackInput {
retrieval_id: out.retrieval_id,
used_memory_ids: vec![mid],
signal: UsageSignal::UserConfirmedCorrect,
})
.unwrap();
engine.close().unwrap();
let db = redb::Database::create(dir.path().join("hippmem.redb")).unwrap();
let records = ActivationLogger::new(std::sync::Arc::new(db))
.read_all()
.unwrap();
assert!(
!records.is_empty(),
"retrieve + feedback 应产生 activation_log 记录"
);
for rec in &records {
for used in &rec.used_memory_ids {
assert_eq!(
*used, mid.0,
"activation_log 中的 memory id 必须等于写入的 ULID(禁止 u64 截断),\
record: retrieval_id={} signal={}",
rec.retrieval_id, rec.signal
);
}
}
}
#[test]
fn feedback_confirm_changes_subsequent_retrieval() {
let dir = tempdir().unwrap();
let engine = Engine::open(EngineConfig {
store_dir: dir.path().join("hippmem.redb"),
..Default::default()
})
.unwrap();
write(&engine, "小明和李华是高中同学。");
write(&engine, "李华和王芳在同一家公司工作。");
write(&engine, "王芳和张伟是一对夫妻。");
let confirmed = write(&engine, "张伟是北京大学的教授。");
let out1 = retrieve(&engine, "张伟在哪里工作?", 5);
assert!(out1.results.len() >= 2, "测试前提:至少两条结果");
let target = out1
.results
.iter()
.find(|r| r.memory.id.0 == confirmed.0)
.expect("被确认记忆应在结果中");
assert!(
target.final_score < out1.results[0].final_score,
"测试前提:被确认记忆不是 top-1"
);
let before: Vec<(u128, f32)> = out1
.results
.iter()
.map(|r| (r.memory.id.0, r.final_score))
.collect();
std::thread::sleep(std::time::Duration::from_millis(2));
engine
.feedback(FeedbackInput {
retrieval_id: out1.retrieval_id,
used_memory_ids: vec![confirmed],
signal: UsageSignal::UserConfirmedCorrect,
})
.unwrap();
let out2 = retrieve(&engine, "张伟在哪里工作?", 5);
let after: Vec<(u128, f32)> = out2
.results
.iter()
.map(|r| (r.memory.id.0, r.final_score))
.collect();
assert_ne!(
before, after,
"feedback 确认后检索结果必须变化:被确认记忆经 RecentActivation 通道获得强化"
);
let after_target = out2
.results
.iter()
.find(|r| r.memory.id.0 == confirmed.0)
.expect("被确认记忆仍应在结果中");
assert!(
after_target.final_score > target.final_score,
"被确认记忆的分数必须提升({} → {})",
target.final_score,
after_target.final_score
);
engine.close().unwrap();
}