Skip to content

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 langgraph library 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.


import sys, os
sys.path.insert(0, os.path.expanduser("~/agenttests"))
import azcfg, hetool
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
from 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)
@tool
def get_working_folder() -> str:
"""Return the agent's current working folder (absolute path). Takes no arguments."""
return hetool.get_working_folder()
@tool
def 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()

{
"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>" }
}

  1. Keep your graph as it is — any compiled graph works, create_react_agent is just the short example.
  2. Pass HBIA’s sid as the thread_id in configurable.
  3. Attach a SqliteSaver (or any checkpointer) so state survives between processes.
  4. Build ChatOpenAI from azcfg.load().
  5. Seal the engine, point external.engine at it, restart.

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.