LangGraph
Verified 2026-08-19 — PASS, 6s. Session persistence: native SqliteSaver checkpointer.
LangGraph’s checkpointer rehydrates graph state from disk, so there is no transcript to manage. The
session id is the thread_id.
Licence: use the
langgraphlibrary only.langgraph-api/ LangGraph Platform is Elastic Licence 2.0, which prohibits offering it as a hosted service — do not pull it into a product you deploy for others. The library itself is MIT.
The runner
Section titled “The runner”import sys, ossys.path.insert(0, os.path.expanduser("~/agenttests"))import azcfg, hetool
from langchain_openai import ChatOpenAIfrom langchain_core.tools import toolfrom langgraph.prebuilt import create_react_agentfrom langgraph.checkpoint.sqlite import SqliteSaver
DB = os.path.expanduser("~/agenttests/langgraph/checkpoints.sqlite")
c = azcfg.load()llm = ChatOpenAI(model=c["model"], base_url=c["base_url"], api_key=c["api_key"], temperature=0)
@tooldef get_working_folder() -> str: """Return the agent's current working folder (absolute path). Takes no arguments.""" return hetool.get_working_folder()
@tooldef run_shell(command: str) -> str: """Run a shell command in the working folder and return its output.""" return hetool.run_shell(command)
TOOLS = [get_working_folder, run_shell]
def invoke(thread_id, message): """No local transcript — LangGraph rehydrates state from the checkpoint.""" with SqliteSaver.from_conn_string(DB) as saver: agent = create_react_agent(llm, TOOLS, checkpointer=saver) cfg = {"configurable": {"thread_id": thread_id}} result = agent.invoke({"messages": [{"role": "user", "content": message}]}, cfg) return result["messages"][-1].content.strip()Engine definition
Section titled “Engine definition”{ "type": "process", "file": "/home/you/langgraph/.venv/bin/python3", "nativeSession": true, "argsNew": ["/home/you/he_adapter.py", "/home/you/langgraph_session.py", "", "{message}"], "argsResume": ["/home/you/he_adapter.py", "/home/you/langgraph_session.py", "{sessionId}", "{message}"], "replyField": "result", "sessionIdField": "session_id", "approve": false, "workdir": "/home/you/work", "proxy": { "shape": "openai", "baseUrlEnv": "OPENAI_BASE_URL", "keyEnv": "OPENAI_API_KEY", "modelEnv": "OPENAI_MODEL", "model": "<route>|<provider model>" }}Adoption
Section titled “Adoption”- Keep your graph as it is — any compiled graph works,
create_react_agentis just the short example. - Pass HBIA’s
sidas thethread_idinconfigurable. - Attach a
SqliteSaver(or any checkpointer) so state survives between processes. - Build
ChatOpenAIfromazcfg.load(). - Seal the engine, point
external.engineat it, restart.
Gotchas
Section titled “Gotchas”SqliteSaver.from_conn_string is a context manager. Use with; the connection must be open for
the whole invoke, and closing it early gives an agent that silently forgets.
Every hop routes through the proxy. A reason → tool → answer loop makes multiple LLM calls, all routed. Good for gating, and worth knowing when reading metering: one turn is not one call.
A tool with no arguments needs saying so. "Takes no arguments." in the docstring stops models
inventing a parameter and failing the call.
temperature=0 for deterministic testing; drop it in production if you want variety.
Build the LLM at module scope only if the route is stable. In this runner azcfg.load() runs at
import, which is fine because the env is injected per spawn. If you ever reuse the process across
turns, move it inside invoke.