pub mod consolidate_api;
pub mod dump_api;
pub mod explain_api;
pub mod feedback_api;
pub mod inspect_api;
pub mod list_api;
pub mod retrieve_api;
pub mod runtime;
pub mod traverse_api;
pub mod write_api;
use hippmem_core::config::{AlgoParams, EmbedderConfig};
use hippmem_core::ids::MemoryId;
use hippmem_core::model::links::AssociationLink;
use hippmem_core::model::understanding::MemoryUnderstanding;
use hippmem_core::model::unit::{MemoryStage, WriteContext};
use hippmem_model::registry::{build_embedder, BackendSelection};
use hippmem_model::traits::Embedder;
use hippmem_store::fulltext::FulltextIndex;
use hippmem_store::semantic::binary::BinaryCodeIndex;
use hippmem_store::semantic::hnsw::FlatVectorIndex;
use hippmem_store::store::{RedbStore, Store};
use parking_lot::RwLock;
use serde::Serialize;
use std::path::PathBuf;
use std::sync::Arc;
#[derive(Debug, thiserror::Error)]
pub enum EngineError {
#[error("store: {0}")]
Store(String),
#[error("not found: {0:?}")]
NotFound(MemoryId),
#[error("invalid input: {0}")]
InvalidInput(String),
#[error("schema too new: {0}")]
SchemaTooNew(u16),
#[error("model: {0}")]
Model(String),
#[error("backend unavailable: {0}")]
BackendUnavailable(String),
#[error("internal: {0}")]
Internal(String),
}
pub type EngineResult<T> = Result<T, EngineError>;
pub struct WriteMemoryInput {
pub content: String,
pub content_type: Option<hippmem_core::model::enums::ContentType>,
pub context: WriteContext,
pub importance_hint: Option<f32>,
pub source_refs: Vec<hippmem_core::model::unit::SourceRef>,
}
pub struct WriteMemoryOutput {
pub memory_id: MemoryId,
pub stage_reached: MemoryStage,
pub created_links: Vec<AssociationLink>,
pub understanding: MemoryUnderstanding,
pub warnings: Vec<WriteWarning>,
}
use hippmem_core::model::links::{RecallChannel, RetrievalResult};
#[derive(Debug, Clone, Default)]
pub struct RetrieveContext {
pub conversation_id: Option<u64>,
pub session_id: Option<u64>,
pub project_id: Option<u64>,
pub task_id: Option<u64>,
pub user_id: Option<u64>,
pub recent_memory_ids: Vec<MemoryId>,
}
pub struct RetrieveInput {
pub query: String,
pub context: RetrieveContext,
pub top_k: usize,
pub max_hops: Option<usize>,
pub retrieval_mode: hippmem_core::model::links::RetrievalMode,
}
pub struct RetrieveOutput {
pub results: Vec<RetrievalResult>,
pub trace: RetrievalTrace,
pub diagnostics: RetrievalDiagnostics,
}
pub struct RetrievalTrace {
pub seeds: Vec<SeedRecord>,
pub steps: Vec<hippmem_core::model::links::ActivationStep>,
pub hops_used: u8,
pub merged_count: usize,
}
pub struct SeedRecord {
pub id: MemoryId,
pub channel: RecallChannel,
pub initial_energy: f32,
pub rank_in_channel: Option<usize>,
}
pub struct RetrievalDiagnostics {
pub channel_contributions: Vec<(RecallChannel, u32)>,
pub reranked: bool,
pub pruned_branches: u32,
pub backend_used: BackendUsage,
pub latency_ms: u32,
}
pub struct BackendUsage {
pub embedder: String,
pub reranker: Option<String>,
}
pub struct FeedbackInput {
pub retrieval_id: u64,
pub used_memory_ids: Vec<MemoryId>,
pub signal: UsageSignal,
}
#[derive(Debug, Clone)]
pub enum ConsolidationScope {
Full,
Incremental,
ByMemoryType(hippmem_core::model::enums::ContentType),
ByTimeRange {
from: hippmem_core::time::Timestamp,
to: hippmem_core::time::Timestamp,
},
Reindex,
EdgesOnly,
}
use hippmem_core::model::links::LinkType;
use hippmem_core::model::unit::{MemoryLifecycle, MemoryUnit};
pub struct Explanation {
pub memory_id: MemoryId,
pub content_summary: String,
pub current_importance: f32,
pub linked: Vec<LinkSummary>,
pub corrections: Vec<MemoryId>,
pub contradictions: Vec<MemoryId>,
pub recent_activations: u32,
}
pub struct LinkSummary {
pub target: MemoryId,
pub link_type: LinkType,
pub strength: f32,
}
pub enum InspectQuery {
Memory(MemoryId),
Edges(MemoryId),
Channel(RecallChannel),
StoreStats,
QueueStatus,
StrongestEdges { limit: usize },
Contradictions { limit: usize },
}
pub enum InspectReport {
Memory(Box<MemoryInspect>),
StoreStats(StoreStats),
QueueStatus(QueueStatus),
}
pub struct MemoryInspect {
pub unit: MemoryUnit,
pub out_edges: Vec<EdgeView>,
pub in_edges: Vec<EdgeView>,
pub stage: MemoryStage,
pub lifecycle: MemoryLifecycle,
}
#[derive(Debug, Clone)]
pub struct EdgeView {
pub from: MemoryId,
pub to: MemoryId,
pub link_type: LinkType,
pub strength: f32,
pub confidence: f32,
pub activation_count: u32,
pub evidence: String,
}
pub struct StoreStats {
pub memory_count: u64,
pub edge_count: u64,
pub observing_edge_count: u64,
pub per_index_size: Vec<(RecallChannel, u64)>,
pub queue_backlog: u64,
pub store_bytes: u64,
}
pub struct QueueStatus {
pub pending_enrich: u64,
pub pending_consolidate: u64,
pub in_flight: u64,
pub oldest_pending_age_ms: u64,
}
pub struct ConsolidationReport {
pub memories_processed: u64,
pub edges_decayed: u64,
pub edges_archived: u64,
pub edges_merged: u64,
pub observation_promoted: u64,
pub summaries_created: u64,
pub contradictions_found: u64,
pub reindexed: bool,
pub elapsed_ms: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UsageSignal {
Referenced,
UserConfirmedCorrect,
TaskSucceeded,
UserRejected,
}
#[derive(Debug, Clone, PartialEq)]
pub enum WriteWarning {
ExtractorDegraded,
EmbeddingDeferred,
StrongDimsDeferred,
ModelError { detail: String },
}
#[derive(Debug, Clone)]
pub struct ListInput {
pub limit: usize,
pub cursor: Option<u128>,
pub content_type: Option<hippmem_core::model::enums::ContentType>,
}
impl Default for ListInput {
fn default() -> Self {
Self {
limit: 20,
cursor: None,
content_type: None,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct ListOutput {
pub items: Vec<ListItem>,
pub next_cursor: Option<u128>,
pub total: u64,
}
#[derive(Debug, Clone, Serialize)]
pub struct ListItem {
pub id: hippmem_core::ids::MemoryId,
pub content_preview: String,
pub content_type: hippmem_core::model::enums::ContentType,
pub created_at: hippmem_core::time::Timestamp,
pub importance: f32,
pub stage: hippmem_core::model::unit::MemoryStage,
pub lifecycle: hippmem_core::model::unit::MemoryLifecycle,
pub edge_count: usize,
}
#[derive(Debug, Clone, Default)]
pub struct DumpInput {
pub output_path: Option<std::path::PathBuf>,
}
#[derive(Debug, Clone, Serialize)]
pub struct DumpOutput {
pub count: u64,
pub written_to: Option<std::path::PathBuf>,
pub json: Option<String>,
}
#[derive(Debug, Clone)]
pub struct TraverseInput {
pub start_id: hippmem_core::ids::MemoryId,
pub max_depth: u8,
pub direction: TraverseDirection,
pub link_types: Option<Vec<hippmem_core::model::links::LinkType>>,
}
impl TraverseInput {
pub fn new(start_id: hippmem_core::ids::MemoryId) -> Self {
Self {
start_id,
max_depth: 2,
direction: TraverseDirection::Outgoing,
link_types: None,
}
}
}
impl Default for TraverseInput {
fn default() -> Self {
Self {
start_id: hippmem_core::ids::MemoryId(0),
max_depth: 2,
direction: TraverseDirection::Outgoing,
link_types: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraverseDirection {
Outgoing,
Incoming,
Both,
}
#[derive(Debug, Clone)]
pub struct TraverseOutput {
pub nodes: Vec<TraverseNode>,
pub edges: Vec<EdgeView>,
}
#[derive(Debug, Clone)]
pub struct TraverseNode {
pub id: hippmem_core::ids::MemoryId,
pub depth: u8,
pub content_preview: String,
pub content_type: hippmem_core::model::enums::ContentType,
pub importance: f32,
}
impl From<hippmem_store::store::StoreError> for EngineError {
fn from(e: hippmem_store::store::StoreError) -> Self {
EngineError::Store(e.to_string())
}
}
#[derive(Debug, Clone)]
pub struct BackgroundConfig {
pub enrich_workers: usize,
pub consolidate_workers: usize,
pub queue_capacity: usize,
pub consolidate_interval_ms: u64,
pub enrich_enabled: bool,
}
impl Default for BackgroundConfig {
fn default() -> Self {
Self {
enrich_workers: 2,
consolidate_workers: 1,
queue_capacity: 4096,
consolidate_interval_ms: 3_600_000,
enrich_enabled: true,
}
}
}
#[derive(Debug, Clone)]
pub struct EngineConfig {
pub store_dir: PathBuf,
pub algo: AlgoParams,
pub embedder: EmbedderConfig,
pub backend: BackendSelection,
pub background: BackgroundConfig,
}
impl Default for EngineConfig {
fn default() -> Self {
Self {
store_dir: PathBuf::from("./hippmem_data"),
algo: AlgoParams::default(),
embedder: EmbedderConfig::default(),
backend: BackendSelection::default(),
background: BackgroundConfig::default(),
}
}
}
pub struct Engine {
store: Arc<RedbStore>,
#[allow(dead_code)]
params: Arc<RwLock<AlgoParams>>,
embedder: Arc<dyn Embedder>,
#[allow(dead_code)]
backend: BackendSelection,
fulltext_index: parking_lot::Mutex<FulltextIndex>,
fulltext_dir: PathBuf,
binary_code_index: parking_lot::Mutex<BinaryCodeIndex>,
dense_vector_index: parking_lot::Mutex<FlatVectorIndex>,
}
impl Engine {
pub fn open(config: EngineConfig) -> EngineResult<Self> {
if let Some(parent) = config.store_dir.parent() {
std::fs::create_dir_all(parent).map_err(|e| {
EngineError::Store(format!("cannot create storage directory: {}", e))
})?;
}
let embedder =
build_embedder(&config.embedder).map_err(|e| EngineError::Model(e.to_string()))?;
let store = RedbStore::open(&config.store_dir)?;
let fulltext_dir = config
.store_dir
.parent()
.map(|p| p.join("fulltext"))
.unwrap_or_else(|| PathBuf::from("hippmem_data").join("fulltext"));
let fulltext_index = FulltextIndex::open(&fulltext_dir)
.or_else(|_| FulltextIndex::create(&fulltext_dir))
.map_err(|e| {
EngineError::Store(format!("Tantivy index initialization failed: {}", e))
})?;
Ok(Self {
store: Arc::new(store),
params: Arc::new(RwLock::new(config.algo)),
embedder,
backend: config.backend,
fulltext_index: parking_lot::Mutex::new(fulltext_index),
fulltext_dir,
binary_code_index: parking_lot::Mutex::new(BinaryCodeIndex::new()),
dense_vector_index: parking_lot::Mutex::new(FlatVectorIndex::new()),
})
}
pub fn close(self) -> EngineResult<()> {
if let Err(e) = self.fulltext_index.lock().flush() {
eprintln!("Tantivy flush failed: {}", e);
}
drop(self.store);
Ok(())
}
pub fn set_fulltext_commit_every(&self, n: usize) {
self.fulltext_index.lock().set_commit_every(n);
}
pub fn flush_fulltext(&self) {
let _ = self.fulltext_index.lock().flush();
}
}