LangGraph checkpointer and store
langgraph-checkpoint-hotkv gives LangGraph
agents two kinds of persistence in HotKV:
- Checkpointers (
HotKVSaver,AsyncHotKVSaver): a graph keeps its state between runs, can be interrupted and resumed, and can go back to any earlier step and branch from it. - A store (
HotKVStore,AsyncHotKVStore): long-term memory shared across threads and runs. Items are found by namespace, by filter or, with an embedding model, by meaning, and they can expire.
It works on a HotKV server with or without an enterprise license.
It is open source (Apache-2.0): github.com/hotkv-ltd/hotkv-langgraph.
Install
Section titled “Install”pip install langgraph-checkpoint-hotkvIt needs Python 3.10 or newer and langgraph-checkpoint 4.2 or newer.
Checkpointer
Section titled “Checkpointer”from langgraph.checkpoint.hotkv import HotKVSaver
with HotKVSaver.from_conn_string("hotkv://localhost:6379") as checkpointer: graph = builder.compile(checkpointer=checkpointer) graph.invoke({"messages": [("user", "hi")]}, {"configurable": {"thread_id": "1"}})For ainvoke and astream, use the async saver:
from langgraph.checkpoint.hotkv import AsyncHotKVSaver
async with AsyncHotKVSaver.from_conn_string("hotkv://localhost:6379") as checkpointer: graph = builder.compile(checkpointer=checkpointer) await graph.ainvoke(...)from_conn_string accepts hotkv://, hotkvs:// (TLS), redis:// and
rediss:// URLs. Extra keyword arguments go to the HotKV client. To share a
connection pool, build the saver yourself: HotKVSaver(hotkv.from_url(url)).
Everything is stored under a key prefix (langgraph by default; set
prefix=), so several applications or environments can share one server.
Housekeeping
Section titled “Housekeeping”checkpointer.delete_thread("1") # forget a conversationcheckpointer.prune(["1"], strategy="keep_latest") # keep only the newest checkpoint of a threadcheckpointer.copy_thread("1", "2") # branch a whole threadprune understands LangGraph’s DeltaChannel: when a thread uses one, it keeps
the older checkpoints the channel needs to rebuild its state, so the state is
never silently emptied. Checkpoints do not expire by themselves. Prune or
delete old threads on a schedule.
from langgraph.store.hotkv import HotKVStore
with HotKVStore.from_conn_string("hotkv://localhost:6379") as store: store.put(("users", "ada"), "prefs", {"theme": "dark"}) store.get(("users", "ada"), "prefs") store.search(("users",), filter={"theme": "dark"}) store.list_namespaces(prefix=("users",)) graph = builder.compile(checkpointer=checkpointer, store=store)The filter operators are $eq, $ne, $gt, $gte, $lt and $lte. Results
come newest first.
Semantic search
Section titled “Semantic search”Give the store an embedding model and it indexes what you put, using HotKV’s vector search:
from langchain.embeddings import init_embeddings
with HotKVStore.from_conn_string( "hotkv://localhost:6379", index={"dims": 1536, "embed": init_embeddings("openai:text-embedding-3-small")},) as s: s.put(("docs",), "a", {"text": "cats purr and chase mice"}) s.search(("docs",), query="a pet that hunts", limit=3) # results carry .scorefields (default the whole item) and a per-put index=[...] choose what is
embedded. index=False keeps an item out of the index. A filter is applied to
the nearest neighbours the index returns, so a very selective filter on a large
namespace can return fewer results than limit.
Expiry
Section titled “Expiry”HotKVStore.from_conn_string(url, ttl={"default_ttl": 60}) # minutes, for every new itemstore.put(ns, key, value, ttl=5) # or per itemA read or a search refreshes the clock unless you pass refresh_ttl=False.
Expiry is kept in whole seconds on the server, so a ttl of 0.02 minutes lasts
2 seconds.
Things to know
Section titled “Things to know”- Use a standalone or replicated HotKV server. The savers and the store touch several keys in one transaction or script, which a cluster rejects unless every key falls in one slot. Cluster mode is not supported.
- The async classes also answer the synchronous methods when they are called
from another thread than the event loop that owns the connection. That is how
LangGraph runs a sync node of an async graph. A blocking call on the event
loop’s own thread raises
asyncio.InvalidStateErrorinstead of deadlocking. - Search without a
queryreads every item of the matching namespaces, which suits namespaces of up to a few thousand items, such as one user’s memories. For larger collections use a query. - Thread ids, checkpoint namespaces, store keys and namespace labels must not
contain the control characters U+001E and U+001F. The store also rejects
.in a label, as LangGraph’s other stores do. list_namespacescan show a namespace whose items all expired until something searches it.
Next steps
Section titled “Next steps”Was this page helpful?
Thanks for the feedback.