Memory that behaves like a system, not a log.
Project description
langmemory
Memory that behaves like a system, not a log.
AGE Memory turns recall into a tiered runtime built from queues, indexes, graphs, and promotion logic. Fast. Inspectable. Durable.
Most LLM memory is a list. You append to it. You search it. It grows until it breaks.
LangMemory is different. Every memory is a node in a living system — assigned an importance, placed in a tier, indexed by time and concept, chained cryptographically to its history, and promoted or demoted automatically as the conversation evolves.
Zero dependencies. Pure Python.
Install
pip install langmemory
No required dependencies. Works out of the box with Python 3.10+.
Quick start
from langmemory import LangMemory
memory = LangMemory()
memory.insert("User is building a Python API with FastAPI")
memory.insert("They prefer minimal dependencies")
memory.insert("The project deploys to AWS Lambda")
results = memory.retrieve("What should I know about the user's stack?")
for r in results:
print(r.node.content)
print(f" score={r.score:.3f} importance={r.node.current_importance:.3f}")
Inject into any LLM prompt:
context = memory.get_context_window(
query="What does the user care about?",
token_budget=1000
)
# Pass context directly into your system prompt
response = llm.chat(system=context, user=user_message)
How it works
LangMemory is not a vector database wrapper. It is a memory runtime built from six data structures, each solving a specific problem that flat storage cannot.
The six primitives
| Structure | Complexity | Role |
|---|---|---|
| Bloom Filter | O(1) | Deduplication — never store the same memory twice |
| Skip List | O(log n) | Temporal index — range queries over time |
| Merkle Tree | O(log n) | Integrity chain — every memory is cryptographically linked |
| LSM Tiers | O(1) amortized | Hot / Warm / Cold storage — memory cost scales with access patterns |
| Min-Heap | O(log n) | Importance-weighted retrieval — surfaces what matters, not just what's recent |
| Concept Trie | O(key length) | Hierarchical concept indexing — Python/types/generics is a path, not a tag |
The write path
insert(content)
│
├─ Bloom Filter → duplicate? return existing node
├─ Embed → encode to vector (pluggable backend)
├─ MemoryNode → assign importance, decay rate, concepts
├─ Merkle Tree → compute hash, link to chain
├─ Skip List → register timestamp
├─ Concept Trie → index under concept paths
├─ Importance Heap → push with priority
└─ HOT tier → store in memory, compact if over limit
The read path
retrieve(query)
│
├─ Path A: Semantic → importance heap × embedding similarity
├─ Path B: Temporal → skip list range or recent tail
├─ Path C: Concept → trie prefix lookup
│
├─ Score fusion → (0.5 × semantic) + (0.3 × importance) + (0.2 × recency)
└─ Top-K → sorted, access metadata updated
Tiers
Memory moves automatically between tiers as importance decays:
HOT → RAM Fast read/write. Recent and frequently accessed nodes.
WARM → Disk Persistent. Moderate access. Promoted to HOT on read.
COLD → Archive Cheap storage. Rarely accessed. Promoted on demand.
The decay worker runs in the background. You never manage tiers manually.
Memory types
from langmemory import LangMemory, MemoryType
memory = LangMemory()
# Episodic — specific events (decays at normal rate)
memory.insert("User asked about deploying to Lambda", memory_type=MemoryType.EPISODIC)
# Semantic — general knowledge (decays slowly)
memory.insert("User prefers minimal dependencies", memory_type=MemoryType.SEMANTIC)
# Procedural — how-to knowledge
memory.insert("To deploy: run make deploy then push to main", memory_type=MemoryType.PROCEDURAL)
# Working — transient context (decays fast)
memory.insert("User is currently looking at the auth module", memory_type=MemoryType.WORKING)
Integrity verification
Every memory is Merkle-chained. Any modification to any node breaks its hash.
report = memory.verify_integrity()
print(report.is_valid) # True
print(report.total_nodes) # 42
print(report.corrupted_nodes) # []
print(report.root_hash.hex()) # deterministic fingerprint of entire memory state
Store the root hash externally to detect tampering across sessions. This is the property no other LLM memory system provides.
Concept-scoped retrieval
# Index memories under concept paths
memory.insert(
"Use async generators for streaming responses",
concepts=["Python/async", "API/streaming"]
)
# Retrieve everything under a concept subtree
results = memory.retrieve("streaming", concept_filter="Python/async")
Embedder backends
The default embedder is a deterministic stub — no API key, no network, full functionality.
Switch backends without changing anything else:
from langmemory import LangMemory, ChainConfig
# OpenAI (pip install openai)
memory = LangMemory(ChainConfig(embedder_backend="openai"))
# Local / offline (pip install sentence-transformers)
memory = LangMemory(ChainConfig(embedder_backend="sentence-transformers"))
LangChain integration
from langmemory import LangMemory
from langmemory.adapters.langchain import LangChainMemory
from langchain.chains import ConversationChain
memory = LangChainMemory(chain=LangMemory())
chain = ConversationChain(llm=llm, memory=memory)
Configuration
from langmemory import LangMemory, ChainConfig
memory = LangMemory(ChainConfig(
hot_tier_limit=500, # nodes kept in RAM
semantic_weight=0.6, # retrieval score weights
importance_weight=0.3,
recency_weight=0.1,
default_decay_factor=0.01, # importance decay per idle day
bloom_capacity=50_000,
))
Stats
memory.stats()
# {
# "hot_count": 38,
# "warm_count": 0,
# "cold_count": 0,
# "total_indexed": 38,
# "bloom_fp_rate": 0.0002,
# "root_hash": "a3f9...",
# "skip_list_size": 38
# }
Why not RAG?
RAG is retrieval over documents. LangMemory is a memory runtime for agents.
| RAG | LangMemory | |
|---|---|---|
| Structure | Flat vector index | Tiered graph with integrity chain |
| Deduplication | None | Bloom filter at insert time |
| Temporal queries | Not supported | Skip list range queries |
| Importance decay | Not supported | Background decay worker |
| Integrity | None | Merkle chain, verifiable root hash |
| Dependencies | Heavy (vector DB, embedder) | Zero required |
| Complexity guarantees | Undocumented | Every operation documented |
License
MIT — Puneet Singh
Project details
Release history Release notifications | RSS feed
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 langmemory-0.1.1.tar.gz.
File metadata
- Download URL: langmemory-0.1.1.tar.gz
- Upload date:
- Size: 31.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31d0e1c5ec5721b9da848279b4f8f5fd6a118d37ad729bbb0d721b0c0d6250dc
|
|
| MD5 |
54ee677213febbce4d5ea7af53d78bf1
|
|
| BLAKE2b-256 |
595f91533e593bb501a8b950c34956e557247cbcbd9d647712dff964de85406a
|
File details
Details for the file langmemory-0.1.1-py3-none-any.whl.
File metadata
- Download URL: langmemory-0.1.1-py3-none-any.whl
- Upload date:
- Size: 34.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87e13f9b5d7af4611708ece74dab10295156154b3afccb4cc89f2c3e8852cfb3
|
|
| MD5 |
44e881da097303d399b0ad8233a5c0ba
|
|
| BLAKE2b-256 |
5038da72fde102ac5d7705157f020dc5d2fb1aa2cf36f6629ab86a0163223a83
|