Backend-agnostic memory-policy primitives shared across Rig
memory-store adapters (e.g. rig-memvid
and future SQLite / LanceDB / Qdrant / filesystem backends).
This crate has no dependency on memvid-core or any specific storage
engine. Adapters wrap these primitives in their own backend-specific code.
dedup— in-process content-hash dedup for hooks/compactors that must satisfyrig::memory::{DemotionHook, Compactor}'s idempotency contract on(conversation_id, messages).metadata— typed envelope (FrameMetadata+FrameKind) written into a backend's per-entry metadata map so downstream tools (evals, memory inspectors, RAG pipelines) can reason about the lifecycle that produced each entry.store— small capability traits (TextWriter+Committable) for hooks, compactors, and adapters that need to write text and explicitly flush it without depending on a concrete storage backend.inmem— deterministic no-disk lexical reference store (Episode,InMemoryStore,InMemoryHit) for tests, examples, offline modes, and backend-neutral fixtures.scope— normalized exact and hierarchical scope matching helpers for backend isolation, tenant boundaries, and provenance projection.retention— deterministic keep/drop/defer decisions over decoded frame metadata plus backend-provided sequence numbers, timestamps, and retention labels.error::PolicyError— neutral error type shared by the helpers above.
Backend crates keep ownership of their storage-specific APIs while leaning on
these shared primitives for policy-level behaviour. For example,
rig-memvid wraps Memvid's .mv2
archive format, but re-exports this crate's dedup, metadata, in-memory, and
text-write capability surfaces to preserve its historic public paths.
New backend-neutral code should prefer importing directly from
rig-memory-policy:
use rig_memory_policy::{Episode, InMemoryStore};
#[derive(Clone)]
struct Finding {
summary: String,
}
impl Episode for Finding {
fn summary(&self) -> &str {
&self.summary
}
}
# async fn run() -> Result<(), rig_memory_policy::PolicyError> {
let store = InMemoryStore::<Finding>::new();
store
.append(Finding {
summary: "scheduled maintenance".into(),
})
.await?;
let hits = store.retrieve_similar("maintenance", 5).await?;
assert_eq!(hits.len(), 1);
# Ok(()) }Existing rig-memvid callers can continue using rig_memvid::inmem::* and
the top-level rig_memvid::{Episode, InMemoryStore, InMemoryHit} re-exports;
those are compatibility shims over this crate.
Backends can evaluate retention decisions without adopting a shared storage
trait. Decode a frame's FrameMetadata, attach any backend-local facts (such
as sequence numbers or write timestamps), and run it through a pure
RetentionPolicy before compaction, deletion, or archival.
use rig_memory_policy::{
FrameKind, FrameMetadata, RetentionCandidate, RetentionDecision,
RetentionPolicy, Scope,
};
let metadata = FrameMetadata {
schema_version: 1,
kind: FrameKind::CompactionSummary,
conversation_id: "conv-1".into(),
chat_role: "assistant".into(),
dedup_key: "abc".into(),
scope: Some("tenant-a/project-1".into()),
};
let policy = RetentionPolicy::new()
.keep_summaries()
.drop_outside_scope(Some(Scope::new("tenant-a/project-1")))
.drop_written_before(1_700_000_000_000)
.default_decision(RetentionDecision::Drop);
let decision = policy.evaluate(
RetentionCandidate::new(&metadata).with_written_at_unix_ms(1_600_000_000_000),
);
assert_eq!(decision, RetentionDecision::Keep);The policy helpers intentionally use primitive caller-supplied timestamps and string-backed labels. That keeps the crate runtime-agnostic and avoids baking one backend's storage model into the shared contract.
- Crate version:
0.2.1. - Rust edition: 2024.
- MSRV: 1.89.
- Runtime stance: runtime-agnostic library. The only runtime dependencies
are pure-Rust utility crates (
blake3,serde,serde_json,thiserror,tracing);tokiois not in[dependencies]. - Scope stance: backend-neutral. The crate must not depend on
memvid-core,rig-compose,rig-resources,rig-mcp,rig-memvid, orrig-retrieval-evalsso every memory-store backend can consume it. - Stability: the Phase 1 public surface listed above is considered stable for
0.2.x. New backend-neutral capabilities land as additive modules; breaking changes go through a minor-version bump and are flagged in CHANGELOG.md.
See ROADMAP.md for the published Phase 1 surface and Phase 2 candidates.
MIT