Skip to main content

GraphMemory

Trait GraphMemory 

Source
pub trait GraphMemory<N, E>:
    Send
    + Sync
    + 'static
where N: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static,
{
Show 14 methods // Required methods fn add_node<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, node: N, ) -> Pin<Box<dyn Future<Output = Result<NodeId>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn add_edge<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, from: &'life3 NodeId, to: &'life4 NodeId, edge: E, timestamp: DateTime<Utc>, ) -> Pin<Box<dyn Future<Output = Result<EdgeId>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait; fn get_node<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, id: &'life3 NodeId, ) -> Pin<Box<dyn Future<Output = Result<Option<N>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn neighbors<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, node: &'life3 NodeId, direction: Direction, ) -> Pin<Box<dyn Future<Output = Result<Vec<(EdgeId, NodeId, E)>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn traverse<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, start: &'life3 NodeId, direction: Direction, max_depth: usize, ) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn find_path<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, from: &'life3 NodeId, to: &'life4 NodeId, direction: Direction, max_depth: usize, ) -> Pin<Box<dyn Future<Output = Result<Option<Vec<GraphHop<E>>>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait; fn temporal_filter<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, from: DateTime<Utc>, to: DateTime<Utc>, ) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn delete_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, edge_id: &'life3 EdgeId, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn delete_node<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, node_id: &'life3 NodeId, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; // Provided methods fn add_edges_batch<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, edges: Vec<(NodeId, NodeId, E, DateTime<Utc>)>, ) -> Pin<Box<dyn Future<Output = Result<Vec<EdgeId>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait { ... } fn get_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, _ns: &'life2 Namespace, _edge_id: &'life3 EdgeId, ) -> Pin<Box<dyn Future<Output = Result<Option<GraphHop<E>>>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait { ... } fn node_count<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, _ns: &'life2 Namespace, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait { ... } fn edge_count<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, _ns: &'life2 Namespace, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait { ... } fn prune_older_than<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, _ns: &'life2 Namespace, _ttl: Duration, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait { ... }
}
Expand description

Generic graph-of-knowledge memory. Trait so backends (Neo4j, ArangoDB, Postgres-with-recursive-CTE) can plug in without touching the consumer code; reference in-process impl is InMemoryGraphMemory.

Required Methods§

Source

fn add_node<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, node: N, ) -> Pin<Box<dyn Future<Output = Result<NodeId>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Insert node and return its assigned id.

Source

fn add_edge<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, from: &'life3 NodeId, to: &'life4 NodeId, edge: E, timestamp: DateTime<Utc>, ) -> Pin<Box<dyn Future<Output = Result<EdgeId>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Insert an edge from from to to carrying edge. timestamp is supplied by the caller so re-inserting after a replay produces deterministic edges.

Source

fn get_node<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, id: &'life3 NodeId, ) -> Pin<Box<dyn Future<Output = Result<Option<N>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Look up a node by id (verb-family get per .claude/rules/naming.md — single-item primary-key lookup).

Source

fn neighbors<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, node: &'life3 NodeId, direction: Direction, ) -> Pin<Box<dyn Future<Output = Result<Vec<(EdgeId, NodeId, E)>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Edges incident to node in the requested direction. Each triple is (EdgeId, neighbour NodeId, edge payload).

Source

fn traverse<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, start: &'life3 NodeId, direction: Direction, max_depth: usize, ) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Breadth-first traversal starting at start, expanding up to max_depth hops along edges in the requested direction. Returns the visited hops in BFS order (excluding the seed node, which has no inbound edge in this traversal). Use Direction::Both for relationship-graph queries that don’t care about edge polarity (knowledge graphs typically want this).

Source

fn find_path<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, from: &'life3 NodeId, to: &'life4 NodeId, direction: Direction, max_depth: usize, ) -> Pin<Box<dyn Future<Output = Result<Option<Vec<GraphHop<E>>>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Shortest unweighted path from from to to (BFS) along edges in the requested direction. Returns the sequence of hops; Some(vec![]) means from == to (already at destination — no edges traversed); None means no path exists within max_depth hops.

Source

fn temporal_filter<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, from: DateTime<Utc>, to: DateTime<Utc>, ) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Edges whose timestamp falls in [from, to). Useful for audit-log style queries (“what relationships did the agent learn last week”).

Source

fn delete_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, edge_id: &'life3 EdgeId, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Drop one edge by id. Idempotent — deleting an absent edge succeeds. Required — backends that don’t support edge deletion are degenerate; closed the CRUD-completeness gap.

Source

fn delete_node<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, node_id: &'life3 NodeId, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Drop one node by id and every edge incident to it. Cascades — operators that don’t want cascading delete every incident edge first via Self::delete_edge and then call this. Returns the count of removed edges so callers can log or expose cleanup metrics; 0 when the node had no edges (or was absent — the operation is idempotent).

Cascade is the right default because the alternative (leaving dangling edges that point at a deleted node) would break the invariant “every edge endpoint is a resolvable node id” that traversal relies on. Refusing when edges exist (the SQL RESTRICT shape) would force every operator into a manual edge-delete loop.

Provided Methods§

Source

fn add_edges_batch<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, ctx: &'life1 ExecutionContext, ns: &'life2 Namespace, edges: Vec<(NodeId, NodeId, E, DateTime<Utc>)>, ) -> Pin<Box<dyn Future<Output = Result<Vec<EdgeId>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Insert a batch of edges atomically. Each tuple is (from, to, edge, timestamp); endpoints must already exist (same contract as Self::add_edge). Returns the assigned EdgeIds in input order.

Backends with native bulk-insert support (e.g. PgGraphMemory’s INSERT … SELECT FROM UNNEST(…)) override this to fold N round-trips into one. The default impl loops over Self::add_edge — correct for every backend, fast for none. Knowledge-graph batch ingest is the operator hot path that motivates the override.

Source

fn get_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, _ns: &'life2 Namespace, _edge_id: &'life3 EdgeId, ) -> Pin<Box<dyn Future<Output = Result<Option<GraphHop<E>>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Look up an edge by id and return the full structural body (GraphHop<E>from, to, edge, timestamp). Operators rarely want the payload alone for edges; the endpoints and timestamp are usually load-bearing for any follow-up decision (audit context, freshness check, neighbour navigation). Returning the full hop saves a second lookup.

Asymmetric with Self::get_node (which returns Option<N> because nodes have no separate structural body) — the shape difference is intentional, not an oversight.

Default impl returns None.

Source

fn node_count<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, _ns: &'life2 Namespace, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Count nodes in ns. Cheap operator metric for size-based decisions (paginate vs stream, fast-fail empty-namespace check, audit / dashboard surface). Default impl returns 0.

Source

fn edge_count<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, _ns: &'life2 Namespace, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Count edges in ns. Cheap operator metric — same rationale as Self::node_count. Default impl returns 0.

Source

fn prune_older_than<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _ctx: &'life1 ExecutionContext, _ns: &'life2 Namespace, _ttl: Duration, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Drop every edge in ns whose timestamp is older than ttl ago. Returns the count of removed edges so callers can log or expose pruning metrics.

Edge-only by design — nodes have no timestamp on the trait surface, so a TTL sweep cannot reason about them directly. Nodes left orphaned by edge removal stay in place until the operator drops them explicitly via a future operation. This mirrors crate::EntityMemory::prune_older_than and crate::EpisodicMemory::prune_older_than (single timestamp axis, no cascading semantics).

Default impl returns Ok(0) — only backends that own a timestamp index implement this. Operators schedule it on a timer (or trigger from a periodic graph) to bound edge-table growth in long-running deployments.

Implementors§

Source§

impl<N, E> GraphMemory<N, E> for InMemoryGraphMemory<N, E>
where N: Clone + Send + Sync + 'static, E: Clone + Send + Sync + 'static,