Archived
f33d789471
- vector.rs: replace flat O(n) scan with instant-distance HNSW for stores >= 100 nodes; flat scan retained as fallback for small graphs; dirty-flag persistence in sled triggers index rebuild only when nodes are added - consolidation.rs: Episodic → Semantic promotion based on activation_count and salience_floor thresholds; global decay pass after each cycle; ConsolidationConfig + ConsolidationReport types; 8 tests - migration.rs: reads Neuron SQLite (memory_nodes, knowledge_entries, graph_edges) and writes to Engram sled; placeholder unit-vector embeddings with TODO for ONNX; 5 tests including full in-memory DB roundtrip - crates/engram-migrate: CLI binary (engram-migrate --sqlite / --output) - crates/engram-jni: JNI cdylib exposing open/close/put_node/get_node/ activate/search_embedding/touch/decay/node_count/edge_count via Java_ai_neuron_engram_EngramDb_* entry points; 6 tests - bindings/kotlin: EngramDb.kt (AutoCloseable JNI wrapper), EngramNode, EngramEdge, ActivatedNode, EngramTypes; build.gradle.kts; settings.gradle.kts - bindings/typescript: engram-wasm crate (wasm-bindgen, serde-wasm-bindgen); WasmEngramDb with in-memory backend (sled not available in WASM); TypeScript wrapper (index.ts, types.ts, package.json, tsconfig.json) - bindings/go: engram.go (CGo wrapper), engram.h (C header), engram_test.go (4 tests covering open/close/put_node/get_node/node_count/decay); go.mod - engram-core: wasm feature gate for in-memory backend; mem_storage.rs; activation.activate_mem for WASM path; Node::with_id helper; salience.rs doctest fixed (text block) - examples/basic.rs: consolidation section added - examples/migrate.rs: migration API demonstration Build: cargo build --workspace -- zero warnings, zero errors Tests: 38 pass (25 engram-core + 7 engram-ffi + 6 engram-jni)
100 lines
3.3 KiB
Rust
100 lines
3.3 KiB
Rust
/// In-memory storage backend for environments without a filesystem.
|
|
///
|
|
/// Used when the `wasm` feature is enabled (e.g. browser via wasm-bindgen).
|
|
/// Implements the same logical interface as the sled-backed `storage` module
|
|
/// so that `EngramDb` can work identically in both environments.
|
|
///
|
|
/// All state lives in a `MemStore` that is held by `EngramDb` under a `RwLock`
|
|
/// so concurrent reads are fine and writes are serialised.
|
|
use crate::error::{EngramError, EngramResult};
|
|
use crate::types::{Edge, Node};
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Default)]
|
|
pub struct MemStore {
|
|
pub nodes: HashMap<Uuid, Node>,
|
|
/// from_id → list of edges
|
|
pub edges_from: HashMap<Uuid, Vec<Edge>>,
|
|
/// to_id → list of edges
|
|
pub edges_to: HashMap<Uuid, Vec<Edge>>,
|
|
}
|
|
|
|
impl MemStore {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
// ── Node operations ───────────────────────────────────────────────────────
|
|
|
|
pub fn write_node(&mut self, node: &Node) -> EngramResult<()> {
|
|
self.nodes.insert(node.id, node.clone());
|
|
Ok(())
|
|
}
|
|
|
|
pub fn read_node(&self, id: Uuid) -> EngramResult<Option<Node>> {
|
|
Ok(self.nodes.get(&id).cloned())
|
|
}
|
|
|
|
pub fn scan_nodes(&self) -> EngramResult<Vec<Node>> {
|
|
Ok(self.nodes.values().cloned().collect())
|
|
}
|
|
|
|
pub fn node_count(&self) -> usize {
|
|
self.nodes.len()
|
|
}
|
|
|
|
// ── Edge operations ───────────────────────────────────────────────────────
|
|
|
|
pub fn write_edge(&mut self, edge: &Edge) -> EngramResult<()> {
|
|
self.edges_from
|
|
.entry(edge.from_id)
|
|
.or_default()
|
|
.push(edge.clone());
|
|
self.edges_to
|
|
.entry(edge.to_id)
|
|
.or_default()
|
|
.push(edge.clone());
|
|
Ok(())
|
|
}
|
|
|
|
pub fn read_edges_from(&self, from_id: Uuid) -> EngramResult<Vec<Edge>> {
|
|
Ok(self
|
|
.edges_from
|
|
.get(&from_id)
|
|
.cloned()
|
|
.unwrap_or_default())
|
|
}
|
|
|
|
pub fn read_edges_to(&self, to_id: Uuid) -> EngramResult<Vec<Edge>> {
|
|
Ok(self
|
|
.edges_to
|
|
.get(&to_id)
|
|
.cloned()
|
|
.unwrap_or_default())
|
|
}
|
|
|
|
pub fn edge_count(&self) -> usize {
|
|
self.edges_from.values().map(|v| v.len()).sum()
|
|
}
|
|
|
|
// ── Vector operations ─────────────────────────────────────────────────────
|
|
|
|
pub fn scan_vectors(&self) -> EngramResult<Vec<(Uuid, Vec<f32>)>> {
|
|
Ok(self
|
|
.nodes
|
|
.iter()
|
|
.map(|(id, n)| (*id, n.embedding.clone()))
|
|
.collect())
|
|
}
|
|
|
|
// ── Salience ──────────────────────────────────────────────────────────────
|
|
|
|
pub fn write_salience(&mut self, id: Uuid, salience: f32) -> EngramResult<()> {
|
|
if let Some(node) = self.nodes.get_mut(&id) {
|
|
node.salience = salience;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|