use hippmem_core::model::enums::ContentType;
use hippmem_core::model::links::RetrievalMode;
use hippmem_core::model::unit::{GeneratedBy, MemoryLifecycle, MemoryUnit, WriteContext};
use hippmem_engine::{
ConsolidationScope, Engine, EngineConfig, RetrieveContext, RetrieveInput, WriteMemoryInput,
};
use redb::ReadableDatabase;
use redb::ReadableTable;
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_ctx() -> RetrieveContext {
RetrieveContext {
conversation_id: Some(1),
session_id: Some(1),
project_id: None,
task_id: None,
user_id: None,
recent_memory_ids: vec![],
}
}
fn compressed_ids(db_path: &std::path::Path) -> Vec<u128> {
let db = redb::Database::create(db_path).unwrap();
let read_txn = db.begin_read().unwrap();
let table = read_txn
.open_table(hippmem_store::store::MEMORY_KV)
.unwrap();
let mut out = Vec::new();
for entry in table.iter().unwrap().flatten() {
if let Ok((unit, _)) = bincode::serde::decode_from_slice::<MemoryUnit, _>(
entry.1.value(),
bincode::config::standard(),
) {
if matches!(unit.lifecycle, MemoryLifecycle::Compressed { .. }) {
out.push(unit.id.0);
}
}
}
out
}
fn build_cluster_store() -> (tempfile::TempDir, Engine) {
let dir = tempdir().unwrap();
let db_path = dir.path().join("hippmem.redb");
let engine = Engine::open(EngineConfig {
store_dir: db_path.clone(),
..Default::default()
})
.unwrap();
for i in 0..13u32 {
engine
.write(WriteMemoryInput {
content: format!("项目{i}采用了 Rust 编写核心引擎,重点优化内存检索性能。"),
content_type: Some(ContentType::ProjectKnowledge),
context: ctx(),
importance_hint: None,
source_refs: vec![],
})
.unwrap();
}
(dir, engine)
}
#[test]
fn compressed_sources_are_not_seeds_after_consolidation() {
let (dir, engine) = build_cluster_store();
let db_path = dir.path().join("hippmem.redb");
let report = engine.consolidate(ConsolidationScope::Incremental).unwrap();
assert!(
report.summaries_created >= 1,
"cluster of 13 similar low-importance memories must trigger a summary"
);
engine.close().unwrap();
let sources = compressed_ids(&db_path);
assert!(
!sources.is_empty(),
"summary sources must be marked Compressed"
);
let engine = Engine::open(EngineConfig {
store_dir: db_path.clone(),
..Default::default()
})
.unwrap();
let out = engine
.retrieve(RetrieveInput {
query: "项目采用 Rust 编写核心引擎,重点优化内存检索性能".to_string(),
context: retrieve_ctx(),
top_k: 10,
max_hops: Some(2),
retrieval_mode: RetrievalMode::Balanced,
})
.unwrap();
let seed_ids: Vec<u128> = out.trace.seeds.iter().map(|s| s.id.0).collect();
for sid in &sources {
assert!(
!seed_ids.contains(sid),
"compressed source {sid} must not act as a retrieval seed"
);
}
let result_ids: Vec<u128> = out.results.iter().map(|r| r.memory.id.0).collect();
for sid in &sources {
assert!(
!result_ids.contains(sid),
"compressed source {sid} must not appear in retrieval results"
);
}
engine.close().unwrap();
let summary_ids = summary_ids(&db_path);
assert!(!summary_ids.is_empty(), "summary units must exist");
for sid in &summary_ids {
assert!(
!seed_ids.contains(sid),
"summary {sid} must not act as a retrieval seed (B1)"
);
}
}
fn summary_ids(db_path: &std::path::Path) -> Vec<u128> {
let db = redb::Database::create(db_path).unwrap();
let read_txn = db.begin_read().unwrap();
let table = read_txn
.open_table(hippmem_store::store::MEMORY_KV)
.unwrap();
let mut out = Vec::new();
for entry in table.iter().unwrap().flatten() {
if let Ok((unit, _)) = bincode::serde::decode_from_slice::<MemoryUnit, _>(
entry.1.value(),
bincode::config::standard(),
) {
if unit.provenance.generated_by == GeneratedBy::Consolidation {
out.push(unit.id.0);
}
}
}
out
}