Skip to content

Ask the HotKV docs

Answers come only from these docs and can be wrong; check the linked pages before you rely on them.

LangGraph checkpointer and store

Last updated

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.

Terminal window
pip install langgraph-checkpoint-hotkv

It needs Python 3.10 or newer and langgraph-checkpoint 4.2 or newer.

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.

checkpointer.delete_thread("1") # forget a conversation
checkpointer.prune(["1"], strategy="keep_latest") # keep only the newest checkpoint of a thread
checkpointer.copy_thread("1", "2") # branch a whole thread

prune 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.

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 .score

fields (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.

HotKVStore.from_conn_string(url, ttl={"default_ttl": 60}) # minutes, for every new item
store.put(ns, key, value, ttl=5) # or per item

A 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.

  • 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.InvalidStateError instead of deadlocking.
  • Search without a query reads 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_namespaces can show a namespace whose items all expired until something searches it.