Skip to main content

rig_memvid/
memory_graph.rs

1//! Backend-agnostic structured-memory abstraction.
2//!
3//! [`MemoryGraph`] is the read-side trait that
4//! [`crate::MemoryCardContext`] (and any future graph-aware adapters) is
5//! generic over. It models a memory store as a typed entity-relationship
6//! graph: nodes are *entities* (subjects), edges are *slots* (predicates)
7//! pointing at *values* (objects), each carrying polarity, timestamps,
8//! and provenance.
9//!
10//! # Why a trait
11//!
12//! Memvid is the most complete implementation today (it ships frames +
13//! Logic-Mesh + memory cards in a single `.mv2` file), but the same
14//! abstraction fits Neo4j (graph-native), Postgres / SQLite (cards
15//! table), and the in-memory test fake here. Keeping the trait local
16//! to `rig-memvid` for now is deliberate — the next backend that
17//! implements it will tell us where the upstream home should be (most
18//! likely `rig-core::memory` next to
19//! [`rig::vector_store::VectorStoreIndex`]).
20//!
21//! # Semantics
22//!
23//! - All methods are **read-only** and **synchronous**: implementations
24//!   are expected to be backed by an in-memory snapshot, a memory-mapped
25//!   file, or a thread-pool-bound database connection. Async backends
26//!   should `block_in_place` or expose a separate async trait above
27//!   this one.
28//! - The error type is implementation-defined but must convert into
29//!   [`rig::vector_store::VectorStoreError`] so adapters can surface
30//!   failures uniformly.
31//! - `entity_memories` returning an empty `Vec` is **not** an error;
32//!   unknown entities are valid.
33//!
34//! # Implementing for a new backend
35//!
36//! ```rust,no_run
37//! use memvid_core::MemoryCard;
38//! use rig::vector_store::VectorStoreError;
39//! use rig_memvid::MemoryGraph;
40//!
41//! struct PgGraph; // imagine a postgres-backed cards table
42//!
43//! impl MemoryGraph for PgGraph {
44//!     type Error = VectorStoreError;
45//!     fn memory_card_count(&self) -> Result<usize, Self::Error> { Ok(0) }
46//!     fn all_memory_cards(&self) -> Result<Vec<MemoryCard>, Self::Error> { Ok(vec![]) }
47//!     fn entity_memories(&self, _: &str) -> Result<Vec<MemoryCard>, Self::Error> { Ok(vec![]) }
48//!     fn current_memory(&self, _: &str, _: &str) -> Result<Option<MemoryCard>, Self::Error> { Ok(None) }
49//!     fn entity_preferences(&self, _: &str) -> Result<Vec<MemoryCard>, Self::Error> { Ok(vec![]) }
50//!     fn memory_timeline(&self, _: &str) -> Result<Vec<MemoryCard>, Self::Error> { Ok(vec![]) }
51//! }
52//! ```
53
54use memvid_core::MemoryCard;
55
56/// Read-side abstraction over a structured-memory store.
57///
58/// Implementations expose entity / slot / value cards with versioning,
59/// polarity, and provenance. The default
60/// [`crate::MemvidStore`] implementation backs all six methods with the
61/// memvid `.mv2` memories track.
62pub trait MemoryGraph {
63    /// Error returned by graph queries. Must be convertible to
64    /// [`rig::vector_store::VectorStoreError`] when used with an
65    /// adapter that exposes a [`rig::vector_store::VectorStoreIndex`]
66    /// (such as [`crate::MemoryCardContext`]).
67    type Error: Into<rig::vector_store::VectorStoreError>;
68
69    /// Total number of cards currently stored.
70    fn memory_card_count(&self) -> Result<usize, Self::Error>;
71
72    /// Snapshot of every card. Used by selection strategies that need
73    /// to filter / sort across the whole set.
74    fn all_memory_cards(&self) -> Result<Vec<MemoryCard>, Self::Error>;
75
76    /// All cards for `entity`. Empty `Vec` for unknown entities.
77    fn entity_memories(&self, entity: &str) -> Result<Vec<MemoryCard>, Self::Error>;
78
79    /// Most recent non-retracted value for `entity`/`slot`, if any.
80    fn current_memory(&self, entity: &str, slot: &str) -> Result<Option<MemoryCard>, Self::Error>;
81
82    /// Preference-kind cards for `entity`.
83    fn entity_preferences(&self, entity: &str) -> Result<Vec<MemoryCard>, Self::Error>;
84
85    /// Event-kind cards for `entity` in chronological order.
86    fn memory_timeline(&self, entity: &str) -> Result<Vec<MemoryCard>, Self::Error>;
87
88    /// Cards whose `entity` mentions appear in `query` (case-insensitive
89    /// whole-word match). The default implementation snapshots the entire
90    /// archive via [`MemoryGraph::all_memory_cards`] and filters in pure
91    /// Rust, which is correct but clones every card; backends backed by a
92    /// graph store should override this to filter behind their own
93    /// locking / indexing and avoid the intermediate full-archive
94    /// allocation.
95    fn cards_for_query(&self, query: &str) -> Result<Vec<MemoryCard>, Self::Error> {
96        let needle = query.to_lowercase();
97        let all = self.all_memory_cards()?;
98        Ok(all
99            .into_iter()
100            .filter(|card| {
101                let entity = card.entity.to_lowercase();
102                !entity.is_empty() && crate::cards_context::contains_word(&needle, &entity)
103            })
104            .collect())
105    }
106}
107
108impl MemoryGraph for crate::MemvidStore {
109    type Error = crate::MemvidError;
110
111    fn memory_card_count(&self) -> Result<usize, Self::Error> {
112        Self::memory_card_count(self)
113    }
114
115    fn all_memory_cards(&self) -> Result<Vec<MemoryCard>, Self::Error> {
116        Self::all_memory_cards(self)
117    }
118
119    fn entity_memories(&self, entity: &str) -> Result<Vec<MemoryCard>, Self::Error> {
120        Self::entity_memories(self, entity)
121    }
122
123    fn current_memory(&self, entity: &str, slot: &str) -> Result<Option<MemoryCard>, Self::Error> {
124        Self::current_memory(self, entity, slot)
125    }
126
127    fn entity_preferences(&self, entity: &str) -> Result<Vec<MemoryCard>, Self::Error> {
128        Self::entity_preferences(self, entity)
129    }
130
131    fn memory_timeline(&self, entity: &str) -> Result<Vec<MemoryCard>, Self::Error> {
132        Self::memory_timeline(self, entity)
133    }
134
135    fn cards_for_query(&self, query: &str) -> Result<Vec<MemoryCard>, Self::Error> {
136        Self::cards_for_query(self, query)
137    }
138}