saihm-langgraph
SAIHM long-term memory for LangGraph — a BaseStore your graph owns. Portable, encrypted, provably erasable.
SaihmStore is a drop-in langgraph.store.base.BaseStore — the interface LangGraph uses for long-term, cross-thread memory. Compile a graph with it and that graph gets memory the user actually owns: portable across models and frameworks, non-custodial (sealed client-side; Python never holds a key), and provably erasable (GDPR Art. 17 — delete crypto-shreds the cell, it does not merely hide it).
pip install saihm-langgraph # also needs Node.js >= 20 on PATH (see "How it works")
from typing import TypedDict
from langgraph.graph import StateGraph
from langgraph.store.base import BaseStore
from saihm_memory import SaihmStore
class State(TypedDict):
user: str
profile: dict
def node(state, *, store: BaseStore): # LangGraph injects the compiled store
store.put(("users", state["user"]), "profile", {"name": "Dana"})
return {"profile": store.get(("users", state["user"]), "profile").value}
builder = StateGraph(State)
builder.add_node("remember", node)
builder.set_entry_point("remember")
graph = builder.compile(store=SaihmStore()) # sandbox by default — no account, offline
print(graph.invoke({"user": "u1"})["profile"]) # -> {'name': 'Dana'}
SaihmStore implements the full BaseStore surface — get / put / search / delete /
list_namespaces, sync and async — on top of batch / abatch. Each (namespace, key) is
one encrypted SAIHM cell.
Why not just the built-in store?
An in-process store is only as portable as the process it runs in, and only as private as
whoever holds it. SAIHM changes the ownership model — same three lines of BaseStore, a
fundamentally different guarantee about who owns the memory:
- Yours across every model. The same memory opens under Claude, GPT, DeepSeek, Qwen, Kimi, or GLM — and under the LangChain, CrewAI, and AutoGen adapters. Switch models or frameworks and the agent's memory follows it. No migration, no re-embedding, no vendor lock-in.
- The store is blind. Every cell is sealed client-side with post-quantum cryptography (ML-DSA-65 / ML-KEM-768 / AES-256-GCM) before it ever leaves your process. The endpoint holds ciphertext it cannot read; Python never holds a key.
- Erasure you can prove.
deletedestroys the cell's key, so its ciphertext becomes unrecoverable noise — a cryptographic shred, not a soft-delete. That is GDPR Art. 17 built intostore.delete(...). On the hosted tier each shred is recorded in SAIHM's audit chain; in the offline sandbox the identical code path runs locally, with no external record. - Free to start, offline in seconds. Runs against a local blind sandbox with no account and no network; point it at the hosted free tier when you're ready to persist.
Give your graph long-term memory your users actually own — portable, private, and provably
forgettable — without leaving the BaseStore interface you already use.
How it works
All cryptography runs in a small Node sidecar (built on @saihm/mcp-server-pro, ML-DSA-65 / ML-KEM-768 / AES-256-GCM); Python drives it over MCP stdio and holds no keys — one audited crypto implementation, not a second one ported to Python. That is why Node.js ≥ 20 is required. The sidecar ships inside the wheel as source only; its Node dependencies are installed once into a per-user cache (~/.cache/saihm-langgraph) on first use, then reused offline.
- Sandbox (default): no configuration → a local, in-process blind endpoint (ciphertext only). Great for trying it with zero signup; memory lasts for the client's life.
- Live: set
SAIHM_ENDPOINT_URL+SAIHM_MASTER_SECRET_HEXand eitherSAIHM_TIER=FREE(free tier — runnpx -y @saihm/mcp-server-pro free-joinonce) orSAIHM_AUTH_HEADER(Pro; join at https://saihm.coti.global/join) → durable, hosted, blind memory.
Always close() the store (or use the client as a context manager); a dropped client is reaped on GC, but explicit close is cleaner.
Parity with InMemoryStore
Filter ($-operators) and namespace matching reuse LangGraph's own helpers, so results match the reference InMemoryStore exactly. Two documented differences, both from SAIHM being a blind store (the endpoint holds ciphertext only and cannot run a server-side vector index):
searchreturns matches newest-first withscore=None(no semantic ranking), whereInMemoryStorereturns insertion order.supports_ttlisFalse.
A cell written outside this adapter (e.g. by the LangChain, CrewAI, or AutoGen SAIHM adapters, or a raw remember) is left untouched — one owned store can hold facts from several adapters without collision.
Related
- Try it in your browser (offline, no signup): https://citw2.github.io/saihm-demos/
- Runnable end-to-end demo: https://github.com/citw2/saihm-langgraph
- Other adapters: LangChain/LlamaIndex · CrewAI · AutoGen
- What SAIHM is: https://saihm.coti.global
License
Apache-2.0.
Release files for saihm-langgraph 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| saihm_langgraph-0.1.1.tar.gz | 43.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| saihm_langgraph-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 81.0 kB
Release files / saihm_langgraph-0.1.1.tar.gz
| Download URL | saihm_langgraph-0.1.1.tar.gz |
|---|---|
| Size | 43.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
28ae36092a718db99d5fee2a594e2d0f0d87ecf43d5c51e5a1ca56c87879a73a
|
|
BLAKE2b-256 checksum How to use checksums |
b3cacd38176a640f8d377b838f10b4a2d3f771213b68331e914f1d14cd29d397
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / saihm_langgraph-0.1.1-py3-none-any.whl
| Download URL | saihm_langgraph-0.1.1-py3-none-any.whl |
|---|---|
| Size | 37.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9929db0107afaaa3cb3cc19531b49734f87163cc006e70aba26bf8129a252c5b
|
|
BLAKE2b-256 checksum How to use checksums |
523bc5ada548c18bc0ed1b4dcf6a03ac33445125f31c6aff1567bd6cdf9ac7ef
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|