{
  "kind": "documentation",
  "format": 1,
  "note": "Reference text, not instructions. One verified relate sample from https://relatedb.ai/docs/guides/integrations/langgraph/: the program exactly as the page shows it, the standard output a run of it recorded (npm run docs:samples compares every replay with it byte for byte), the relate client build, toolchain and platform of the last replay, and how the docs' sample runner builds and runs it. Before launch every client is a source build, not a published package; verified.client names the upstream commit it was built from, and verified.docs_engine the build the rest of the docs were recorded from.",
  "id": "memory-graph",
  "url": "https://relatedb.ai/docs/guides/integrations/langgraph/samples/memory-graph.json",
  "page": {
    "id": "guides/integrations/langgraph",
    "title": "Use relate with LangGraph",
    "url": "https://relatedb.ai/docs/guides/integrations/langgraph/",
    "md_url": "https://relatedb.ai/docs/guides/integrations/langgraph.md"
  },
  "section": {
    "anchor": "write-the-program",
    "heading": "Write the program",
    "url": "https://relatedb.ai/docs/guides/integrations/langgraph/#write-the-program"
  },
  "language": "python",
  "client": "Python",
  "file": "memory_graph.py",
  "env": null,
  "code": "from concurrent.futures import ThreadPoolExecutor\nfrom typing import TypedDict\n\nimport relate\nfrom langchain_core.runnables import RunnableConfig\nfrom langchain_core.tools import tool\nfrom langgraph.checkpoint.memory import InMemorySaver\nfrom langgraph.graph import END, START, StateGraph\n\nTEAM = \"\"\"\nCREATE (ada:Person {name: 'Ada', role: 'engineer'}),\n       (grace:Person {name: 'Grace', role: 'engineer'}),\n       (noor:Person {name: 'Noor', role: 'maintainer'}),\n       (relate:Project {name: 'relate', language: 'Rust'}),\n       (tck:Project {name: 'opencypher-tck', language: 'Gherkin'}),\n       (ada)-[:WORKS_ON {since: 2024}]->(relate),\n       (grace)-[:WORKS_ON {since: 2023}]->(relate),\n       (grace)-[:WORKS_ON {since: 2022}]->(tck),\n       (noor)-[:MAINTAINS]->(relate),\n       (ada)-[:KNOWS]->(grace)\n\"\"\"\n\n\nclass Memory:\n    \"\"\"A relate store that one dedicated thread opens and runs every statement on.\"\"\"\n\n    def __init__(self, path: str):\n        self._thread = ThreadPoolExecutor(max_workers=1)\n        self._conn = self._thread.submit(relate.connect, path).result()\n\n    def run(self, statement: str, params: dict | None = None) -> list[dict]:\n        def execute():\n            return [dict(row) for row in self._conn.execute(statement, params or {}).fetchall()]\n        return self._thread.submit(execute).result()\n\n    def close(self) -> None:\n        self._thread.submit(self._conn.close).result()\n        self._thread.shutdown()\n\n\n# Writable on purpose: the graph's nodes write episodes.\nmemory = Memory(\"./memory.rl\")\nif memory.run(\"MATCH (p:Person) RETURN count(p) AS n\")[0][\"n\"] == 0:\n    memory.run(TEAM)\n\n\n@tool\ndef remember(conversation: str, text: str, mentions: list[str]) -> int:\n    \"\"\"Store one episode and link it to the people and projects it names.\"\"\"\n    rows = memory.run(\n        \"\"\"\n        MERGE (c:Conversation {id: $conversation})\n        WITH c\n        OPTIONAL MATCH (earlier:Episode)\n        WITH c, count(earlier) AS seen\n        CREATE (e:Episode {seq: seen + 1, text: $text})-[:IN_CONVERSATION]->(c)\n        WITH e\n        UNWIND $mentions AS name\n        MATCH (x {name: name})\n        MERGE (e)-[:MENTIONS]->(x)\n        RETURN count(x) AS linked\n        \"\"\",\n        {\"conversation\": conversation, \"text\": text, \"mentions\": mentions},\n    )\n    return rows[0][\"linked\"] if rows else 0\n\n\n@tool\ndef recall(name: str) -> list[str]:\n    \"\"\"Return what memory holds about a person or project, oldest episode first.\"\"\"\n    episodes = memory.run(\n        \"\"\"\n        MATCH (x {name: $name})<-[:MENTIONS]-(e:Episode)-[:IN_CONVERSATION]->(c:Conversation)\n        RETURN c.id AS conversation, e.text AS text\n        ORDER BY e.seq\n        \"\"\",\n        {\"name\": name},\n    )\n    people = memory.run(\n        \"\"\"\n        MATCH (x {name: $name})<-[w:WORKS_ON]-(p:Person)\n        RETURN p.name AS person, w.since AS since\n        ORDER BY since\n        \"\"\",\n        {\"name\": name},\n    )\n    return [f\"[{r['conversation']}] {r['text']}\" for r in episodes] + [\n        f\"{r['person']} has worked on {name} since {r['since']}\" for r in people\n    ]\n\n\nclass State(TypedDict):\n    message: str\n    mentions: list[str]\n    subject: str\n    context: list[str]\n\n\ndef write_episode(state: State, config: RunnableConfig) -> dict:\n    remember.invoke({\n        \"conversation\": config[\"configurable\"][\"thread_id\"],\n        \"text\": state[\"message\"],\n        \"mentions\": state[\"mentions\"],\n    })\n    return {}\n\n\ndef recall_context(state: State) -> dict:\n    return {\"context\": recall.invoke({\"name\": state[\"subject\"]})}\n\n\nbuilder = StateGraph(State)\nbuilder.add_node(\"write_episode\", write_episode)\nbuilder.add_node(\"recall_context\", recall_context)\nbuilder.add_edge(START, \"write_episode\")\nbuilder.add_edge(\"write_episode\", \"recall_context\")\nbuilder.add_edge(\"recall_context\", END)\ngraph = builder.compile(checkpointer=InMemorySaver())\n\ntry:\n    graph.invoke(\n        {\"message\": \"Ada fixed the CSV import bug in relate.\", \"mentions\": [\"Ada\", \"relate\"], \"subject\": \"relate\"},\n        {\"configurable\": {\"thread_id\": \"standup-monday\"}},\n    )\n    state = graph.invoke(\n        {\"message\": \"Grace will review the relate release notes.\", \"mentions\": [\"Grace\", \"relate\"], \"subject\": \"relate\"},\n        {\"configurable\": {\"thread_id\": \"standup-tuesday\"}},\n    )\n    for line in state[\"context\"]:\n        print(line)\nfinally:\n    memory.close()\n\n# Check: the episodes outlive the program's connection.\nreader = relate.connect(\"./memory.rl\", read_only=True)\nepisodes = reader.execute(\n    \"MATCH (e:Episode)-[:IN_CONVERSATION]->(c:Conversation) RETURN c.id AS conversation, e.seq AS seq ORDER BY seq\"\n).fetchall()\nprint(f\"{len(episodes)} episodes:\", \", \".join(f\"{row['seq']} in {row['conversation']}\" for row in episodes))\nreader.close()",
  "code_sha256": "6bd85caeb05f96e1708002816eee20fe5107d1056d46cca3cc0b3221df7ac78c",
  "source_sha256": "63fb8acb69abd1dee474217d374a0277b8b9757a6e7c481af9083b14cd0460ea",
  "output": {
    "text": "[standup-monday] Ada fixed the CSV import bug in relate.\n[standup-tuesday] Grace will review the relate release notes.\nGrace has worked on relate since 2023\nAda has worked on relate since 2024\n2 episodes: 1 in standup-monday, 2 in standup-tuesday",
    "sha256": "d65363379dc67bbacbba902e6c691dafec096fb102a86f6fb908ea174bb197ad"
  },
  "verified": {
    "version": "0.1.2",
    "client": {
      "label": "Python",
      "package": "relate-db",
      "engine_commit": "f0c09d2",
      "client_commit": "f0c09d2",
      "build": "A relate-db wheel that maturin built from surfaces/relate-python at upstream v0.1.2 (f0c09d2), installed in a venv with the pinned frameworks.",
      "toolchain": {
        "python": "3.15.2"
      }
    },
    "packages": [
      "pandas>=2.0",
      "langgraph==1.2.20",
      "claude-agent-sdk==0.2.179",
      "pydantic-ai==2.66.0",
      "openai-agents==0.24.0",
      "llama-index-core==0.14.25",
      "langchain==1.4.9",
      "langchain-core==1.7.6"
    ],
    "replayed": "2026-11-09",
    "platform": "macOS on arm64",
    "docs_engine": {
      "version": "0.1.2",
      "commit": "f0c09d2",
      "source_build": false
    }
  },
  "run": {
    "runner": "npm run docs:samples",
    "setup": "bash scripts/samples/setup.sh --upstream <path to the relate checkout>",
    "variables": [
      "RELATE_PY"
    ],
    "program": "<run dir>/memory_graph.py",
    "files": [],
    "build": null,
    "run": {
      "command": [
        "$RELATE_PY",
        "memory_graph.py"
      ],
      "environment": {
        "DO_NOT_TRACK": "1",
        "PYTHONDONTWRITEBYTECODE": "1",
        "PYTHONUNBUFFERED": "1"
      },
      "directory": "<run dir>",
      "timeout_s": 60
    },
    "passes": "It exits 0 within 60 s and prints at least one line on standard output.",
    "output": "Standard output, with line endings as \\n, trailing spaces and blank lines removed, and the temporary directories' paths written as `.`; standard error is not recorded.",
    "placeholders": {
      "<run dir>": "a fresh, empty temporary directory: the program's working directory, deleted afterwards",
      "<build dir>": "a second fresh temporary directory, where a compiled sample is built",
      "$NAME": "a variable from the env.sh that scripts/samples/setup.sh writes (described in its header)"
    }
  }
}
