{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d143834a",
   "metadata": {},
   "source": [
    "# Agentic KG Local Flows\n",
    "\n",
    "## Goal\n",
    "\n",
    "Walk through a local, offline version of the Graphiti + Docling + agentic KG maintenance flow. The notebook mirrors the production contracts without requiring Neo4j, Ollama, or the AlphaSwarm services to be running."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa350ded",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "The default path is deterministic and self-contained. Set `RUN_LIVE_GRAPHITI=1` only after installing `alphaswarm-kb[graphiti]`, starting Neo4j, and exposing an Ollama-compatible OpenAI endpoint at `http://localhost:11434/v1`. The live smoke cell reads these optional environment variables:\n",
    "\n",
    "- `GRAPHITI_NEO4J_URI` (default `bolt://localhost:7687`)\n",
    "- `GRAPHITI_NEO4J_USER` (default `neo4j`)\n",
    "- `GRAPHITI_NEO4J_PASSWORD` (default `password` for disposable local Neo4j)\n",
    "- `GRAPHITI_NEO4J_DATABASE` (default `neo4j`)\n",
    "- `OLLAMA_OPENAI_BASE_URL` (default `http://localhost:11434/v1`)\n",
    "- `OLLAMA_CHAT_MODEL` (default `llama3.1`)\n",
    "- `OLLAMA_EMBEDDING_MODEL` (default `nomic-embed-text`)\n",
    "- `OLLAMA_EMBEDDING_DIM` (default `768`)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "4219c701",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-20T22:19:48.039324Z",
     "iopub.status.busy": "2026-06-20T22:19:48.039254Z",
     "iopub.status.idle": "2026-06-20T22:19:48.044176Z",
     "shell.execute_reply": "2026-06-20T22:19:48.043325Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'tenant_id': '00000000-0000-0000-0000-00000000000a', 'corpus': 'internal_docs', 'source_uri': 'file:///tmp/alpha-swarm/kg-maintenance-memo.md', 'live_graphiti_enabled': False}\n"
     ]
    }
   ],
   "source": [
    "from __future__ import annotations\n",
    "\n",
    "import hashlib\n",
    "import json\n",
    "import os\n",
    "from dataclasses import dataclass, field\n",
    "from datetime import UTC, datetime\n",
    "from pprint import pprint\n",
    "from uuid import UUID, uuid4\n",
    "\n",
    "TENANT_ID = UUID(\"00000000-0000-0000-0000-00000000000a\")\n",
    "CORPUS_NAME = \"internal_docs\"\n",
    "SOURCE_URI = \"file:///tmp/alpha-swarm/kg-maintenance-memo.md\"\n",
    "RUN_LIVE_GRAPHITI = os.getenv(\"RUN_LIVE_GRAPHITI\") == \"1\"\n",
    "\n",
    "print({\n",
    "    \"tenant_id\": str(TENANT_ID),\n",
    "    \"corpus\": CORPUS_NAME,\n",
    "    \"source_uri\": SOURCE_URI,\n",
    "    \"live_graphiti_enabled\": RUN_LIVE_GRAPHITI,\n",
    "})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae39ad5b",
   "metadata": {},
   "source": [
    "## Steps\n",
    "\n",
    "### 1. Parse a Document\n",
    "\n",
    "In production, `DoclingParser` calls `DocumentConverter().convert(path).document.export_to_markdown()`. This cell uses a small parsed markdown fixture so the notebook can run anywhere."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "d2cd43f5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-20T22:19:48.045908Z",
     "iopub.status.busy": "2026-06-20T22:19:48.045797Z",
     "iopub.status.idle": "2026-06-20T22:19:48.048541Z",
     "shell.execute_reply": "2026-06-20T22:19:48.048221Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ParsedDoc(text_blocks=['# KG maintenance memo',\n",
      "                       'Graphiti memory should use tenant and corpus group_id '\n",
      "                       'namespaces.',\n",
      "                       'Docling parsing should preserve source path, content '\n",
      "                       'hash, parser name, and block order.',\n",
      "                       'Quarantine review assistants may summarize and '\n",
      "                       'recommend, but promotion remains human-gated.'],\n",
      "          metadata={'source_path': 'file:///tmp/alpha-swarm/kg-maintenance-memo.md',\n",
      "                    'title': 'KG maintenance memo'},\n",
      "          parser_name='docling')\n"
     ]
    }
   ],
   "source": [
    "sample_markdown = \"\"\"# KG maintenance memo\n",
    "\n",
    "Graphiti memory should use tenant and corpus group_id namespaces.\n",
    "\n",
    "Docling parsing should preserve source path, content hash, parser name, and block order.\n",
    "\n",
    "Quarantine review assistants may summarize and recommend, but promotion remains human-gated.\n",
    "\"\"\"\n",
    "\n",
    "@dataclass(frozen=True)\n",
    "class ParsedDoc:\n",
    "    text_blocks: list[str]\n",
    "    metadata: dict[str, object]\n",
    "    parser_name: str = \"docling\"\n",
    "\n",
    "\n",
    "def parse_with_docling_fixture(markdown: str) -> ParsedDoc:\n",
    "    blocks = [block.strip() for block in markdown.split(\"\\n\\n\") if block.strip()]\n",
    "    return ParsedDoc(\n",
    "        text_blocks=blocks,\n",
    "        metadata={\"title\": \"KG maintenance memo\", \"source_path\": SOURCE_URI},\n",
    "    )\n",
    "\n",
    "parsed = parse_with_docling_fixture(sample_markdown)\n",
    "pprint(parsed)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d1ef75e",
   "metadata": {},
   "source": [
    "### 2. Convert Parsed Blocks to KB Datapoints\n",
    "\n",
    "The KB implementation stores parser provenance and temporal metadata in `PermissionedDataPoint` envelopes before calling `IMemoryEngine.remember`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "eb938717",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-20T22:19:48.049622Z",
     "iopub.status.busy": "2026-06-20T22:19:48.049552Z",
     "iopub.status.idle": "2026-06-20T22:19:48.052411Z",
     "shell.execute_reply": "2026-06-20T22:19:48.052045Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "created 4 datapoints\n",
      "{'corpus_name': 'internal_docs',\n",
      " 'id': 'f66e05b9-039d-4448-98cb-d63729ba83e7',\n",
      " 'properties': {'block_index': 0,\n",
      "                'content': '# KG maintenance memo',\n",
      "                'content_hash': '73cc11d167ac3f637e753fde7405436a00a13ab75b517d1c4776373d2896a622',\n",
      "                'parser_name': 'docling',\n",
      "                'source_uri': 'file:///tmp/alpha-swarm/kg-maintenance-memo.md',\n",
      "                'title': 'KG maintenance memo'},\n",
      " 'provenance': {'data_id': 'file:///tmp/alpha-swarm/kg-maintenance-memo.md#block-0:73cc11d167ac3f63',\n",
      "                'dataset_id': 'internal-docs',\n",
      "                'extractor_chain': ['docling']},\n",
      " 'temporal': {'created_at': '2026-06-20T22:19:48.050829+00:00',\n",
      "              'expired_at': None,\n",
      "              'valid_from': '2026-06-20T22:19:48.050829+00:00',\n",
      "              'valid_to': None},\n",
      " 'tenant_id': '00000000-0000-0000-0000-00000000000a',\n",
      " 'type': 'PermissionedDataPoint'}\n"
     ]
    }
   ],
   "source": [
    "def datapoints_from_parsed_doc(parsed: ParsedDoc) -> list[dict[str, object]]:\n",
    "    now = datetime.now(UTC).isoformat()\n",
    "    datapoints = []\n",
    "    for block_index, content in enumerate(parsed.text_blocks):\n",
    "        content_hash = hashlib.sha256(content.encode(\"utf-8\")).hexdigest()\n",
    "        datapoints.append({\n",
    "            \"id\": str(uuid4()),\n",
    "            \"type\": \"PermissionedDataPoint\",\n",
    "            \"tenant_id\": str(TENANT_ID),\n",
    "            \"corpus_name\": CORPUS_NAME,\n",
    "            \"properties\": {\n",
    "                \"content\": content,\n",
    "                \"title\": parsed.metadata[\"title\"],\n",
    "                \"parser_name\": parsed.parser_name,\n",
    "                \"source_uri\": SOURCE_URI,\n",
    "                \"block_index\": block_index,\n",
    "                \"content_hash\": content_hash,\n",
    "            },\n",
    "            \"temporal\": {\"valid_from\": now, \"created_at\": now, \"valid_to\": None, \"expired_at\": None},\n",
    "            \"provenance\": {\n",
    "                \"dataset_id\": \"internal-docs\",\n",
    "                \"data_id\": f\"{SOURCE_URI}#block-{block_index}:{content_hash[:16]}\",\n",
    "                \"extractor_chain\": [parsed.parser_name],\n",
    "            },\n",
    "        })\n",
    "    return datapoints\n",
    "\n",
    "datapoints = datapoints_from_parsed_doc(parsed)\n",
    "print(f\"created {len(datapoints)} datapoints\")\n",
    "pprint(datapoints[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35f3fcc2",
   "metadata": {},
   "source": [
    "### 3. Remember Episodes in a Graphiti-Like Namespace\n",
    "\n",
    "Graphiti supports `group_id` namespaces. The adapter uses a tenant + corpus namespace so one Graphiti instance can hold isolated graphs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "368b1f98",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-20T22:19:48.053720Z",
     "iopub.status.busy": "2026-06-20T22:19:48.053649Z",
     "iopub.status.idle": "2026-06-20T22:19:48.056989Z",
     "shell.execute_reply": "2026-06-20T22:19:48.056661Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'accepted': 4, 'group_id': 'tenant:00000000-0000-0000-0000-00000000000a:corpus:internal_docs'}\n",
      "{'episode_body': '{\"corpus_name\": \"internal_docs\", \"id\": '\n",
      "                 '\"f66e05b9-039d-4448-98cb-d63729ba83e7\", \"properties\": '\n",
      "                 '{\"block_index\": 0, \"content\": \"# KG maintenance memo\", '\n",
      "                 '\"content_hash\": '\n",
      "                 '\"73cc11d167ac3f637e753fde7405436a00a13ab75b517d1c4776373d2896a622\", '\n",
      "                 '\"parser_name\": \"docling\", \"source_uri\": '\n",
      "                 '\"file:///tmp/alpha-swarm/kg-maintenance-memo.md\", \"title\": '\n",
      "                 '\"KG maintenance memo\"}, \"provenance\": {\"data_id\": '\n",
      "                 '\"file:///tmp/alpha-swarm/kg-maintenance-memo.md#block-0:73cc11d167ac3f63\", '\n",
      "                 '\"dataset_id\": \"internal-docs\", \"extractor_chain\": '\n",
      "                 '[\"docling\"]}, \"temporal\": {\"created_at\": '\n",
      "                 '\"2026-06-20T22:19:48.050829+00:00\", \"expired_at\": null, '\n",
      "                 '\"valid_from\": \"2026-06-20T22:19:48.050829+00:00\", '\n",
      "                 '\"valid_to\": null}, \"tenant_id\": '\n",
      "                 '\"00000000-0000-0000-0000-00000000000a\", \"type\": '\n",
      "                 '\"PermissionedDataPoint\"}',\n",
      " 'group_id': 'tenant:00000000-0000-0000-0000-00000000000a:corpus:internal_docs',\n",
      " 'name': 'internal_docs:f66e05b9-039d-4448-98cb-d63729ba83e7',\n",
      " 'reference_time': '2026-06-20T22:19:48.050829+00:00',\n",
      " 'source': 'json',\n",
      " 'source_description': 'alphaswarm_kb/internal_docs/file:///tmp/alpha-swarm/kg-maintenance-memo.md#block-0:73cc11d167ac3f63'}\n"
     ]
    }
   ],
   "source": [
    "@dataclass\n",
    "class FakeGraphitiMemory:\n",
    "    corpus_name: str\n",
    "    episodes: list[dict[str, object]] = field(default_factory=list)\n",
    "\n",
    "    def group_id_for(self, tenant_id: UUID) -> str:\n",
    "        return f\"tenant:{tenant_id}:corpus:{self.corpus_name}\"\n",
    "\n",
    "    def remember(self, datapoints: list[dict[str, object]]) -> int:\n",
    "        for dp in datapoints:\n",
    "            body = json.dumps(dp, sort_keys=True)\n",
    "            self.episodes.append({\n",
    "                \"name\": f\"{self.corpus_name}:{dp['id']}\",\n",
    "                \"episode_body\": body,\n",
    "                \"source\": \"json\",\n",
    "                \"source_description\": f\"alphaswarm_kb/{self.corpus_name}/{dp['provenance']['data_id']}\",\n",
    "                \"reference_time\": dp[\"temporal\"][\"valid_from\"],\n",
    "                \"group_id\": self.group_id_for(TENANT_ID),\n",
    "            })\n",
    "        return len(datapoints)\n",
    "\n",
    "    def search(self, query: str, *, top_k: int = 5) -> list[dict[str, object]]:\n",
    "        query_terms = query.lower().split()\n",
    "        hits = []\n",
    "        for episode in self.episodes:\n",
    "            body = json.loads(episode[\"episode_body\"])\n",
    "            content = body[\"properties\"][\"content\"]\n",
    "            score = sum(term in content.lower() for term in query_terms) / max(len(query_terms), 1)\n",
    "            if score:\n",
    "                hits.append({\n",
    "                    \"id\": body[\"id\"],\n",
    "                    \"score\": score,\n",
    "                    \"content\": content,\n",
    "                    \"group_id\": episode[\"group_id\"],\n",
    "                    \"provenance\": body[\"provenance\"],\n",
    "                })\n",
    "        return sorted(hits, key=lambda row: row[\"score\"], reverse=True)[:top_k]\n",
    "\n",
    "memory = FakeGraphitiMemory(CORPUS_NAME)\n",
    "accepted = memory.remember(datapoints)\n",
    "print({\"accepted\": accepted, \"group_id\": memory.group_id_for(TENANT_ID)})\n",
    "pprint(memory.episodes[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "baef42bb",
   "metadata": {},
   "source": [
    "### 4. Query KB Recall and Graph-RAG Context\n",
    "\n",
    "The production flow composes `data.kb.recall` with SOKG graph context such as `/graph/rag/profile`. This offline cell shows the handoff shape."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e4418958",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-20T22:19:48.057887Z",
     "iopub.status.busy": "2026-06-20T22:19:48.057825Z",
     "iopub.status.idle": "2026-06-20T22:19:48.059903Z",
     "shell.execute_reply": "2026-06-20T22:19:48.059543Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'graph_context': {'anchors': ['GraphitiMemoryEngine',\n",
      "                               'DoclingParser',\n",
      "                               'quarantine_review'],\n",
      "                   'citations': ['file:///tmp/alpha-swarm/kg-maintenance-memo.md#block-1:d61f3f495f7c3abb',\n",
      "                                 'file:///tmp/alpha-swarm/kg-maintenance-memo.md#block-3:88d9182f63002911'],\n",
      "                   'profile': 'kg.graphrag_analyst',\n",
      "                   'prompt_context': 'Graphiti memory should use tenant and '\n",
      "                                     'corpus group_id namespaces.\\n'\n",
      "                                     'Quarantine review assistants may '\n",
      "                                     'summarize and recommend, but promotion '\n",
      "                                     'remains human-gated.',\n",
      "                   'query': 'Graphiti namespace human gated'},\n",
      " 'recall_hits': [{'content': 'Graphiti memory should use tenant and corpus '\n",
      "                             'group_id namespaces.',\n",
      "                  'group_id': 'tenant:00000000-0000-0000-0000-00000000000a:corpus:internal_docs',\n",
      "                  'id': '78b26605-48ed-4452-a2f1-8443dc5e5aeb',\n",
      "                  'provenance': {'data_id': 'file:///tmp/alpha-swarm/kg-maintenance-memo.md#block-1:d61f3f495f7c3abb',\n",
      "                                 'dataset_id': 'internal-docs',\n",
      "                                 'extractor_chain': ['docling']},\n",
      "                  'score': 0.5},\n",
      "                 {'content': 'Quarantine review assistants may summarize and '\n",
      "                             'recommend, but promotion remains human-gated.',\n",
      "                  'group_id': 'tenant:00000000-0000-0000-0000-00000000000a:corpus:internal_docs',\n",
      "                  'id': '59f5e440-d5be-4b2e-ab9b-81880ffa6392',\n",
      "                  'provenance': {'data_id': 'file:///tmp/alpha-swarm/kg-maintenance-memo.md#block-3:88d9182f63002911',\n",
      "                                 'dataset_id': 'internal-docs',\n",
      "                                 'extractor_chain': ['docling']},\n",
      "                  'score': 0.5}]}\n"
     ]
    }
   ],
   "source": [
    "recall_hits = memory.search(\"Graphiti namespace human gated\", top_k=3)\n",
    "graph_context = {\n",
    "    \"profile\": \"kg.graphrag_analyst\",\n",
    "    \"query\": \"Graphiti namespace human gated\",\n",
    "    \"anchors\": [\"GraphitiMemoryEngine\", \"DoclingParser\", \"quarantine_review\"],\n",
    "    \"citations\": [hit[\"provenance\"][\"data_id\"] for hit in recall_hits],\n",
    "    \"prompt_context\": \"\\n\".join(hit[\"content\"] for hit in recall_hits),\n",
    "}\n",
    "\n",
    "pprint({\"recall_hits\": recall_hits, \"graph_context\": graph_context})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57a399af",
   "metadata": {},
   "source": [
    "### 5. Inspect Default-Off Agentic Maintenance Schedules\n",
    "\n",
    "The agent workflows are schedulable but disabled by default. Mutating graph actions remain human-gated."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ddd6baa6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-20T22:19:48.061120Z",
     "iopub.status.busy": "2026-06-20T22:19:48.061036Z",
     "iopub.status.idle": "2026-06-20T22:19:48.063321Z",
     "shell.execute_reply": "2026-06-20T22:19:48.063041Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{'allowed_actions': ['scan', 'ingest_dry_run', 'remember'],\n",
      "  'name': 'kg.document_ingest_sweep',\n",
      "  'requires_human_approval': True,\n",
      "  'schedule': {'enabled': False, 'interval_seconds': 86400}},\n",
      " {'allowed_actions': ['summarize', 'recommend'],\n",
      "  'name': 'kg.graph_health_check',\n",
      "  'requires_human_approval': True,\n",
      "  'schedule': {'enabled': False, 'interval_seconds': 3600}},\n",
      " {'allowed_actions': ['summarize', 'recommend'],\n",
      "  'mutating_actions_enabled': False,\n",
      "  'name': 'kg.quarantine_review_assistant',\n",
      "  'requires_human_approval': True,\n",
      "  'schedule': {'cron': '0 * * * *', 'enabled': False}},\n",
      " {'allowed_actions': ['health', 'remember_test_episode', 'recall_test_episode'],\n",
      "  'name': 'kg.graphiti_memory_smoke',\n",
      "  'requires_human_approval': True,\n",
      "  'schedule': {'enabled': False, 'interval_seconds': 3600}}]\n"
     ]
    }
   ],
   "source": [
    "maintenance_workflows = [\n",
    "    {\n",
    "        \"name\": \"kg.document_ingest_sweep\",\n",
    "        \"schedule\": {\"interval_seconds\": 86400, \"enabled\": False},\n",
    "        \"allowed_actions\": [\"scan\", \"ingest_dry_run\", \"remember\"],\n",
    "        \"requires_human_approval\": True,\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"kg.graph_health_check\",\n",
    "        \"schedule\": {\"interval_seconds\": 3600, \"enabled\": False},\n",
    "        \"allowed_actions\": [\"summarize\", \"recommend\"],\n",
    "        \"requires_human_approval\": True,\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"kg.quarantine_review_assistant\",\n",
    "        \"schedule\": {\"cron\": \"0 * * * *\", \"enabled\": False},\n",
    "        \"allowed_actions\": [\"summarize\", \"recommend\"],\n",
    "        \"mutating_actions_enabled\": False,\n",
    "        \"requires_human_approval\": True,\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"kg.graphiti_memory_smoke\",\n",
    "        \"schedule\": {\"interval_seconds\": 3600, \"enabled\": False},\n",
    "        \"allowed_actions\": [\"health\", \"remember_test_episode\", \"recall_test_episode\"],\n",
    "        \"requires_human_approval\": True,\n",
    "    },\n",
    "]\n",
    "\n",
    "pprint(maintenance_workflows)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86047c44",
   "metadata": {},
   "source": [
    "## Checks\n",
    "\n",
    "These assertions are the offline acceptance checks for the tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "d576e286",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-20T22:19:48.064300Z",
     "iopub.status.busy": "2026-06-20T22:19:48.064253Z",
     "iopub.status.idle": "2026-06-20T22:19:48.065872Z",
     "shell.execute_reply": "2026-06-20T22:19:48.065612Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "offline tutorial checks passed\n"
     ]
    }
   ],
   "source": [
    "assert accepted == len(datapoints)\n",
    "assert memory.group_id_for(TENANT_ID).startswith(\"tenant:\")\n",
    "assert all(hit[\"group_id\"] == memory.group_id_for(TENANT_ID) for hit in recall_hits)\n",
    "assert graph_context[\"citations\"], \"Graph context should carry provenance citations\"\n",
    "assert all(workflow[\"schedule\"][\"enabled\"] is False for workflow in maintenance_workflows)\n",
    "assert maintenance_workflows[2][\"mutating_actions_enabled\"] is False\n",
    "print(\"offline tutorial checks passed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ded35986",
   "metadata": {},
   "source": [
    "## Next Steps\n",
    "\n",
    "- Run the production KB tests in `alphaswarm_kb` to verify `GraphitiMemoryEngine` and document ingest.\n",
    "- Start Neo4j and Ollama, then set `RUN_LIVE_GRAPHITI=1` for a live smoke path after installing `alphaswarm-kb[graphiti]`.\n",
    "- Keep `kg.*` maintenance workflows disabled until operator review defines promotion and release gates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "818d9ffc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-20T22:19:48.066825Z",
     "iopub.status.busy": "2026-06-20T22:19:48.066760Z",
     "iopub.status.idle": "2026-06-20T22:19:48.069811Z",
     "shell.execute_reply": "2026-06-20T22:19:48.069499Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Live Graphiti path skipped; offline fake memory covered the tutorial flow.\n"
     ]
    }
   ],
   "source": [
    "if RUN_LIVE_GRAPHITI:\n",
    "    from pathlib import Path\n",
    "    from tempfile import TemporaryDirectory\n",
    "\n",
    "    from alphaswarm_kb.domain.models.tenant import Principal, TenantContext\n",
    "    from alphaswarm_kb.infrastructure.adapters.memory.graphiti import GraphitiMemoryEngine\n",
    "    from alphaswarm_kb.rag.document_ingest import DocumentIngestRequest, parse_document_to_datapoints\n",
    "    from alphaswarm_kb.rag.parsers.base import BaseDocParser, ParsedDoc as KBParsedDoc\n",
    "\n",
    "    class NotebookDoclingFixtureParser(BaseDocParser):\n",
    "        name = \"docling\"\n",
    "\n",
    "        @classmethod\n",
    "        def available(cls) -> bool:\n",
    "            return True\n",
    "\n",
    "        def parse(self, path: Path | str) -> KBParsedDoc:\n",
    "            return KBParsedDoc(\n",
    "                text_blocks=[block.strip() for block in sample_markdown.split(\"\\n\\n\") if block.strip()],\n",
    "                metadata={\"title\": \"KG maintenance memo\", \"source_path\": str(path)},\n",
    "                parser_name=self.name,\n",
    "            )\n",
    "\n",
    "    async def run_live_graphiti_smoke() -> dict[str, object]:\n",
    "        live_ctx = TenantContext(\n",
    "            tenant_id=TENANT_ID,\n",
    "            principal=Principal(id=\"agent|kg-local-notebook\", kind=\"agent\"),\n",
    "        )\n",
    "        with TemporaryDirectory() as tmpdir:\n",
    "            source_path = Path(tmpdir) / \"kg-maintenance-memo.md\"\n",
    "            source_path.write_text(sample_markdown, encoding=\"utf-8\")\n",
    "            live_request = DocumentIngestRequest(\n",
    "                path=source_path,\n",
    "                corpus_name=CORPUS_NAME,\n",
    "                dataset_id=\"notebook-tutorial-docs\",\n",
    "                source_uri=SOURCE_URI,\n",
    "            )\n",
    "            live_datapoints = parse_document_to_datapoints(\n",
    "                live_request,\n",
    "                ctx=live_ctx,\n",
    "                parser=NotebookDoclingFixtureParser(),\n",
    "            )\n",
    "\n",
    "        engine = GraphitiMemoryEngine(\n",
    "            CORPUS_NAME,\n",
    "            neo4j_uri=os.getenv(\"GRAPHITI_NEO4J_URI\", \"bolt://localhost:7687\"),\n",
    "            neo4j_user=os.getenv(\"GRAPHITI_NEO4J_USER\", \"neo4j\"),\n",
    "            neo4j_password=os.getenv(\"GRAPHITI_NEO4J_PASSWORD\", \"password\"),\n",
    "            database=os.getenv(\"GRAPHITI_NEO4J_DATABASE\", \"neo4j\"),\n",
    "            llm_provider=\"ollama\",\n",
    "            llm_base_url=os.getenv(\"OLLAMA_OPENAI_BASE_URL\", \"http://localhost:11434/v1\"),\n",
    "            llm_model=os.getenv(\"OLLAMA_CHAT_MODEL\", \"llama3.1\"),\n",
    "            embedding_model=os.getenv(\"OLLAMA_EMBEDDING_MODEL\", \"nomic-embed-text\"),\n",
    "            embedding_dim=int(os.getenv(\"OLLAMA_EMBEDDING_DIM\", \"768\")),\n",
    "        )\n",
    "        try:\n",
    "            health = await engine.health()\n",
    "            accepted_live = await engine.remember(live_datapoints, ctx=live_ctx)\n",
    "            recall_live = await engine.recall(\"Graphiti namespace human gated\", ctx=live_ctx, top_k=3)\n",
    "            return {\n",
    "                \"health\": health,\n",
    "                \"accepted\": accepted_live,\n",
    "                \"hit_count\": len(recall_live.hits),\n",
    "                \"group_id\": engine.group_id_for(live_ctx),\n",
    "            }\n",
    "        finally:\n",
    "            await engine.close()\n",
    "\n",
    "    live_smoke = await run_live_graphiti_smoke()\n",
    "    pprint(live_smoke)\n",
    "else:\n",
    "    print(\"Live Graphiti path skipped; offline fake memory covered the tutorial flow.\")\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
