VoltMem
Current-truth memory for LLM agents.
Most memory layers treat every fact the same — your hometown and today's mood get equal weight. That forces a bad tradeoff: go stale on fast-changing facts, or get corrupted when a confident-but-wrong update overwrites something durable.
VoltMem scales protection and retrieval freshness by how fast each kind of fact actually changes. Volatile facts update; stable facts resist corruption; stale volatile memories rank lower at search time.
Mem0 remembers relevant facts. VoltMem remembers current truth.
Research & benchmarks: docs/RESEARCH.md · Known limits & roadmap: docs/OPEN_PROBLEMS.md · Sleeptime roadmap: docs/SCHEDULE.md
What’s new in 0.3.1
- Real consolidate — mismatch evidence log + heuristic/LLM summarizer; scheduled
in the sidecar (
VOLTMEM_CONSOLIDATE/VOLTMEM_CONSOLIDATE_INTERVAL); skips when no evidence - Vector index
event_id— stored on upsert; optional search filter; legacy DB migration - Evals — multi-facet linked retrieve + independent stale@k/audit
(
tests/test_multi_facet_eval.py); TTL haystack 0% past expiry (tests/test_ttl_haystack_eval.py)
What’s new in 0.3.0
- Multi-facet events —
event_id/modalityon items;add_event()+retrieve_by_event()(Python, sidecar, TypeScript client) - Optional TTL —
expires_at/ttl_seconds; retrieval skips expired rows;expire_cleanuppurges them (ledgered, index-synced) - Maintenance substrate —
MaintenanceWindow, flag tasks (pattern_audit,reclassify_ambiguous), run ledger +rollback_maintenance(run_id) - Sidecar sleeptime hooks —
POST .../maintenance/trigger+.../rollback; background daemon runs due expire/audit/consolidate tasks (VOLTMEM_MAINTENANCE=1); consolidate merges mismatch evidence into updated tips (skip if no evidence; disable withVOLTMEM_CONSOLIDATE=0) - Classification eval corpus — 230 labeled utterances / 14 domains;
heuristic baseline ≈84%; CI floors in
tests/test_classifiers.py - SQLite WAL — file-backed DBs open in WAL mode for HTTP + maintenance concurrency
What’s new in 0.2.2
- Adaptive freshness mix — when top candidates have nearly equal similarity (under-specified queries), VoltMem dampens the volatility penalty so freshness cannot dominate near-ties; clear similarity gaps keep full freshness behavior
domain_stats()— always-on prior calibration telemetry (insert / confirm / mismatch / audit counts and rates per domain); does not requireauto_discover- Calibration histogram —
experiments/prior_calibration_hist.py(ASCII + SVG)
Install
pip install voltmem[embeddings]
# from source:
# pip install -e ".[embeddings]"
Core library has zero required dependencies. Embeddings extras pull in
sentence-transformers (recommended). LangChain: pip install -e ".[langchain]".
HTTP sidecar (FastAPI): pip install -e ".[sidecar]" — see sidecar/README.md.
Quickstart
from voltmem import create_memory
mem = create_memory("app.db", user_id="alice")
mem.add("I live in Berlin")
mem.add("I prefer concise, direct answers")
mem.add("Actually I moved to Paris last month") # updates location, not prefs
hits = mem.search("where does the user live?", limit=3)
print(hits[0]["memory"]) # Actually I moved to Paris last month
Message pairs (auto fact extraction)
mem.add([
{"role": "user", "content": "I moved to Paris. I'm working on a DB migration."},
], extract=True) # default for message lists — splits into atomic facts
Optional: create_memory(..., llm_extract=True) for Ollama-powered extraction.
Inject into a prompt
memories = mem.search(user_message, limit=5)
context = "\n".join(f"- {m['memory']}" for m in memories)
system = f"What you know about this user:\n{context}"
API
| Method | Description |
|---|---|
create_memory(db, user_id) |
Factory with auto-detected embeddings + vector index |
Memory.add(text | messages) |
Store a fact; slot-aware linking updates related memories |
Memory.search(query, limit=5) |
ANN candidates + volatility re-rank (relevance + freshness; adaptive mix on similarity plateaus) |
Memory.domain_stats() |
Per-domain prior calibration telemetry (audit / mismatch / confirm rates) |
Memory.get_all() |
All active memories for this user |
Memory.delete(id) |
Remove one memory |
Memory.clear() |
Wipe user namespace |
Advanced: mem.layer exposes MemoryLayer for low-level observe() / write().
create_memory(..., vector_index="auto") enables a SQLite embedding index when an
embedder is present ("off" restores full-scan retrieval). VoltMem always applies
volatility re-ranking on top of vector candidates — not raw ANN results.
stats = mem.domain_stats()
# {
# "location": {"prior": 0.6, "audited": 4, "logged_mismatch": 2,
# "confirmed": 10, "audit_rate": 0.25, ...},
# }
flowchart LR
Q[search query] --> E[embed query]
E --> V[vector index: top candidates]
V --> S[SQLite: load memory records]
S --> P{sim spread flat?}
P -->|no| R[full freshness re-rank]
P -->|yes| D[dampen freshness mix]
R --> T[current truth]
D --> T
Why VoltMem
| Problem | ADD-only memory | VoltMem |
|---|---|---|
| User moves cities | Berlin and Paris both stored | Updates to current city |
| Old project name in haystack | Ranks by similarity | Down-ranks stale volatile facts |
| Confident wrong blip on stable pref | Often accepted | Resists corruption |
| Career / role change (medium-stable) | Often blocked or duplicated | Updates on strong explicit contradiction |
Example results (reproducible)
Run locally with pip install -e ".[embeddings]". Embeddings:
sentence-transformers (all-MiniLM-L6-v2).
examples/contradiction_demo.py — 5-turn script vs naive always-add:
| After scenario | always-add | VoltMem |
|---|---|---|
| User moves Berlin → Paris | 2 location facts (stale + current) | 1 current fact |
| Paraphrase blip on stable pref | adopts blip ("really like short replies") | keeps original ("concise, direct answers") |
experiments/mem0_comparison.py — 3 scenarios, top-1 search (always-add baseline):
| Scenario | always-add | VoltMem |
|---|---|---|
location_update |
WIN (2 facts stored) | WIN (1 fact) |
stable_pref_blip |
LOSE | WIN |
volatile_mood |
LOSE (stale "great") | WIN (current "stressed") |
VoltMem clearer wins: 2/3 (always-add also finds Paris on location, but keeps stale facts).
experiments/mem0_side_by_side.py — same 3 scenarios vs real Mem0
(open-source, gpt-4o-mini + text-embedding-3-small):
| Scenario | Mem0 | VoltMem |
|---|---|---|
location_update |
LOSE (stale "Berlin", 2 facts) | WIN ("Paris", 1 fact) |
stable_pref_blip |
PARTIAL (adopts blip) | WIN (keeps "concise") |
volatile_mood |
LOSE (stale "great", 2 facts) | WIN ("stressed", 1 fact) |
VoltMem clearer wins: 3/3. Mem0 keeps contradictory facts; VoltMem updates volatile slots and protects stable prefs via domain volatility + slot-aware linking.
experiments/voltmem_eval.py — end-to-end escalation + retrieval (real vs flat vs swap):
| Battery | real profile | flat (equal V) | swap (inverted V) |
|---|---|---|---|
| A — selective updating | 20/20 | 15/20 | 7/20 |
| B — retrieval separation | +0.589 | +0.202 | −0.267 |
Includes the professional_context career-change probe (strong explicit evidence → update).
experiments/memory_demo.py — 3 final Q&A checks vs ground truth:
| Policy | Score |
|---|---|
| VoltMem | 3/3 |
| never-overwrite | 2/3 |
| always-overwrite | 1/3 |
| reliability-threshold | 1/3 |
VoltMem is the only policy that both rejects confident false blips on stable
facts and tracks weak-but-true updates on volatile ones. Full distributions:
docs/RESEARCH.md (llm_memory_bench.py).
python examples/contradiction_demo.py
python experiments/mem0_comparison.py
python experiments/mem0_side_by_side.py # pip install mem0ai; OPENAI_API_KEY or MEM0_BACKEND=ollama
python experiments/voltmem_eval.py # 20/20 escalation probes + retrieval separation
python experiments/memory_demo.py
Integrations
LangChain
pip install -e ".[langchain]"
python examples/langchain_agent.py
from voltmem.integrations.langchain import VoltMemMemory
memory = VoltMemMemory(session_id="user-42", db_path="app.db")
memory.load_memory_variables({"input": "Where do I live?"})
memory.save_context({"input": "I moved to Paris"}, {"output": "Noted."})
Multi-tenant
One SQLite file, many users — user_id maps to an isolated namespace:
alice = create_memory("app.db", user_id="alice")
bob = create_memory("app.db", user_id="bob")
HTTP sidecar / TypeScript
For Cloudflare Workers and other TypeScript apps, run VoltMem as an HTTP sidecar
instead of porting the engine. The Worker stays thin and calls REST
(add / search / domain_stats).
Consumer deploy guide: docs/SIDECAR.md — Docker pull/build, production checklist, Fly.io, connecting @voltmem/client.
# Anyone can build the public Dockerfile
git clone https://github.com/Rouche01/voltmem.git && cd voltmem
docker build -t voltmem-sidecar .
docker run -p 8080:8080 -e VOLTMEM_API_KEY=secret -v voltmem-data:/data voltmem-sidecar
# Or run without Docker
pip install -e ".[sidecar,embeddings]"
VOLTMEM_API_KEY=secret VOLTMEM_DB_PATH=./voltmem_sidecar.db python -m sidecar
API reference: sidecar/README.md · TypeScript SDK: clients/typescript.
Examples
| Script | What it shows |
|---|---|
examples/contradiction_demo.py |
VoltMem vs always-add on contradictions |
experiments/mem0_comparison.py |
3-scenario head-to-head vs always-add |
experiments/mem0_side_by_side.py |
3-scenario head-to-head vs real Mem0 (3/3 wedge) |
experiments/voltmem_eval.py |
End-to-end escalation + retrieval (20/20 probes, real > flat > swap) + domain_stats footprint |
experiments/prior_calibration_hist.py |
ASCII + SVG histogram of audit_rate by domain (Battery A replay) |
experiments/retrieval_plateau_probe.py |
Synthetic Problem 3 plateau / clear-gap check |
experiments/calibrate_escalation.py |
Print E_t vs θ table for tuning explicit-override constants |
examples/quickstart_batteries.py |
remember() / recall() low-level API |
examples/multi_tenant.py |
One DB, many users |
examples/langchain_agent.py |
LangChain adapter |
examples/chat_app/ |
Memory-aware CLI chat (extendable to web UI) |
examples/custom_classifier.py |
Pluggable KeywordClassifier + DomainRegistry |
Chat app (CLI)
pip install -e ".[embeddings]"
python -m examples.chat_app # REPL; uses Ollama if running, else echo mode
python -m examples.chat_app --demo # scripted smoke test
python -m examples.chat_app --show-recall
Slash commands: /memories, /search <query>, /clear, /reset, /help.
Domain volatility priors
| Domain | Volatility | Behavior |
|---|---|---|
personality_trait |
0.05 | Very protected |
core_preference |
0.08 | Very protected |
biographical |
0.10 | High protection |
professional_context |
0.30 | Medium — job/role (career changes) |
location |
0.60 | Updates readily (Berlin → Paris) |
current_project |
0.55 | Updates readily |
emotional_context |
0.80 | Fast-moving |
current_task |
0.90 | Minimal protection |
Custom domains: register via DomainRegistry and pass to create_memory(domains=...).
Pluggable classifiers: create_memory(classifier=...) — "heuristic", "llm", KeywordClassifier, or a callable dict.
mem.domain_stats() always records insert / confirm / mismatch / audit rates per
domain (prior calibration). Optional: create_memory(..., auto_discover=True) also
blends empirical volatility into scoring from those patterns (cold-start applies).
from voltmem import create_memory, DomainRegistry, KeywordClassifier, ChainedClassifier, HeuristicClassifier
domains = DomainRegistry()
domains.register("style_preference", 0.08)
domains.register("style_constraint", 0.25)
mem = create_memory(
"app.db",
user_id="alice",
domains=domains,
classifier=ChainedClassifier([
KeywordClassifier({
"style_preference": ["prefer", "darker colors", "minimal"],
"style_constraint": ["no wool", "tight budget"],
}),
HeuristicClassifier(),
]),
)
mem.add("I prefer darker colors and minimal fits")
hits = mem.search("what colors does the user like?")
Development
pip install -e ".[all]"
python tests/test_voltmem.py
python tests/test_multi_facet_eval.py
python tests/test_ttl_haystack_eval.py
python tests/test_client.py
Experiments and benchmarks live in experiments/ — see docs/RESEARCH.md.
Open problems and roadmap: docs/OPEN_PROBLEMS.md.
python experiments/prior_calibration_hist.py # ASCII + experiments/out/*.svg
python experiments/retrieval_plateau_probe.py
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file voltmem-0.3.1.tar.gz.
File metadata
- Download URL: voltmem-0.3.1.tar.gz
- Upload date:
- Size: 88.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43ce955501f68a07d5656dd9b62bddc9ccc569d64a233dc37b9c23254843f9c0
|
|
| MD5 |
8772fc324a4064be7132087fe18fbe9d
|
|
| BLAKE2b-256 |
9fc9f61255685ee73431f1180714bd28f2ad94caa3fc6a797f2a094a7e93931b
|
File details
Details for the file voltmem-0.3.1-py3-none-any.whl.
File metadata
- Download URL: voltmem-0.3.1-py3-none-any.whl
- Upload date:
- Size: 67.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6cf5d491e92f8ec8b1d2247657d79de6a8958ef1f42357341ef67289a6e32296
|
|
| MD5 |
579a3d20a11302cc9f7f28908e031849
|
|
| BLAKE2b-256 |
a64478dce18be1064bdb7aeac4c5605e73eab061acbe5da34b2e317dc9a11800
|