{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Advanced Context Engineering: The Agent's Brain\n",
    "\n",
    "Welcome to the **Master Class** on Semantica Context Engineering. This notebook demonstrates how to build a production-grade memory system for your AI agents.\n",
    "\n",
    "Unlike simple chatbots that forget everything after a session, a **Context-Aware Agent** needs:\n",
    "*   **Long-term Memory**: To recall facts from weeks ago.\n",
    "*   **Structured Knowledge**: To understand how entities (People, Projects, Topics) are connected.\n",
    "*   **Hybrid Retrieval**: To combine fuzzy text search with precise graph traversal.\n",
    "\n",
    "## Learning Objectives\n",
    "\n",
    "In this walkthrough, we will:\n",
    "1.  **Initialize Production Stores**: Replace toy examples with real **Vector Stores** (FAISS) and **Graph Stores** (Neo4j).\n",
    "2.  **Build the Agent Context**: Configure the central brain that orchestrates memory.\n",
    "3.  **Ingest Knowledge**: Store complex documents and auto-extract entities.\n",
    "4.  **Inject Relationships**: Manually teach the agent about connections in the world.\n",
    "5.  **Perform GraphRAG**: Execute advanced queries that \"hop\" through the knowledge graph to find answers standard RAG misses.\n",
    "6.  **Manage Lifecycle**: Learn to prune old memories and keep the system healthy.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2cf97cbc",
   "metadata": {},
   "source": [
    "## 1. Installation\n",
    "\n",
    "To get started, simply install the package:\n",
    "\n",
    "```bash\n",
    "pip install semantica\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "88491af5",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING: Ignoring invalid distribution ~gno (C:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
      "WARNING: Ignoring invalid distribution ~lotly (C:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
      "WARNING: Ignoring invalid distribution ~ython-socketio (C:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
      "WARNING: Ignoring invalid distribution ~gno (C:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
      "WARNING: Ignoring invalid distribution ~lotly (C:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
      "WARNING: Ignoring invalid distribution ~ython-socketio (C:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
      "WARNING: Ignoring invalid distribution ~gno (C:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
      "WARNING: Ignoring invalid distribution ~lotly (C:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
      "WARNING: Ignoring invalid distribution ~ython-socketio (C:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages)\n",
      "C:\\Users\\Mohd Kaif\\AppData\\Roaming\\Python\\Python311\\site-packages\\IPython\\utils\\_process_win32.py:124: ResourceWarning: unclosed file <_io.BufferedWriter name=3>\n",
      "  return process_handler(cmd, _system_body)\n",
      "ResourceWarning: Enable tracemalloc to get the object allocation traceback\n",
      "C:\\Users\\Mohd Kaif\\AppData\\Roaming\\Python\\Python311\\site-packages\\IPython\\utils\\_process_win32.py:124: ResourceWarning: unclosed file <_io.BufferedReader name=4>\n",
      "  return process_handler(cmd, _system_body)\n",
      "ResourceWarning: Enable tracemalloc to get the object allocation traceback\n",
      "C:\\Users\\Mohd Kaif\\AppData\\Roaming\\Python\\Python311\\site-packages\\IPython\\utils\\_process_win32.py:124: ResourceWarning: unclosed file <_io.BufferedReader name=5>\n",
      "  return process_handler(cmd, _system_body)\n",
      "ResourceWarning: Enable tracemalloc to get the object allocation traceback\n"
     ]
    }
   ],
   "source": [
    "!pip install -qU semantica "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "d6401d91",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Libraries imported successfully.\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "import os\n",
    "import time\n",
    "from typing import Any, List, Dict, Optional\n",
    "\n",
    "# Add project root to path to import semantica\n",
    "sys.path.append(os.path.abspath(os.path.join(os.getcwd(), \"../../\")))\n",
    "\n",
    "# Core Imports\n",
    "from semantica.context import AgentContext, ContextGraph, AgentMemory\n",
    "from semantica.vector_store import VectorStore\n",
    "from semantica.graph_store import GraphStore\n",
    "\n",
    "print(\"Libraries imported successfully.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e263672",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dbad46dd",
   "metadata": {},
   "source": [
    "## 2. Initialize Storage Backends\n",
    "\n",
    "We will now connect to our persistent storage layers. Semantica abstracts these behind unified interfaces, so you can swap backends (e.g., switch from FAISS to Weaviate) without changing your application logic.\n",
    "\n",
    "### Vector Store (The Library)\n",
    "Holds the *content* of memories and documents, indexed by semantic meaning."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "812158a5",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "fastembed not available. Install with: pip install fastembed. Using fallback embedding method.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "VectorStore initialized (Backend: FAISS)\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    # Initialize FAISS Vector Store\n",
    "    # You can also use: backend=\"weaviate\", backend=\"qdrant\", etc.\n",
    "    vs = VectorStore(backend=\"faiss\", dimension=768)\n",
    "    print(\"VectorStore initialized (Backend: FAISS)\")\n",
    "except ImportError:\n",
    "    print(\"FAISS not installed. Using in-memory fallback (not persistent).\")\n",
    "    vs = VectorStore(backend=\"inmemory\", dimension=768)\n",
    "except Exception as e:\n",
    "    print(f\"VectorStore Error: {e}\")\n",
    "    vs = None"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8933cfef",
   "metadata": {},
   "source": [
    "### Graph Store (The Map)\n",
    "Holds the *connections* between entities. This is crucial for reasoning."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "7c2aa896",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Connectivity check failed: Couldn't connect to localhost:7687 (resolved to ('[::1]:7687', '127.0.0.1:7687')):\n",
      "Failed to establish connection to ResolvedIPv6Address(('::1', 7687, 0, 0)) (reason [WinError 10061] No connection could be made because the target machine actively refused it)\n",
      "Failed to establish connection to ResolvedIPv4Address(('127.0.0.1', 7687)) (reason [WinError 10061] No connection could be made because the target machine actively refused it)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "GraphStore Connection Failed: Failed to connect to Neo4j: Could not verify connectivity to Neo4j\n",
      "   Switching to in-memory ContextGraph (Non-persistent fallback)\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    # Initialize Neo4j Graph Store\n",
    "    # Ensure your Docker container is running!\n",
    "    gs = GraphStore(\n",
    "        backend=\"neo4j\",\n",
    "        uri=\"bolt://localhost:7687\",\n",
    "        user=\"neo4j\",\n",
    "        password=\"password\"\n",
    "    )\n",
    "    \n",
    "    # Test connection\n",
    "    if gs.connect():\n",
    "        print(\"GraphStore connected (Backend: Neo4j)\")\n",
    "    else:\n",
    "        raise ConnectionError(\"Could not connect to Neo4j\")\n",
    "\n",
    "except Exception as e:\n",
    "    print(f\"GraphStore Connection Failed: {e}\")\n",
    "    print(\"   Switching to in-memory ContextGraph (Non-persistent fallback)\")\n",
    "    gs = ContextGraph() # Fallback implementation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e17b7765",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cebbe65f",
   "metadata": {},
   "source": [
    "## 3. The Agent Context\n",
    "\n",
    "The `AgentContext` is the high-level orchestrator. It sits on top of the Vector and Graph stores and manages the flow of information.\n",
    "\n",
    "**Configuration for GraphRAG:**\n",
    "*   `use_graph_expansion=True`: When retrieving, don't just look at the doc, look at its neighbors.\n",
    "*   `max_expansion_hops=2`: How far to traverse? (e.g., A -> B -> C).\n",
    "*   `hybrid_alpha=0.6`: Weighting. 0.0 is pure Vector, 1.0 is pure Graph. 0.6 favors graph slightly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "f3b2eff6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Agent Context is online and ready.\n"
     ]
    }
   ],
   "source": [
    "if vs:\n",
    "    context = AgentContext(\n",
    "        vector_store=vs,\n",
    "        knowledge_graph=gs,\n",
    "        retention_days=90,          # Remember things for 3 months\n",
    "        use_graph_expansion=True,   # Enable GraphRAG\n",
    "        max_expansion_hops=2,       # 2-Hop reasoning\n",
    "        hybrid_alpha=0.6            # Balanced retrieval\n",
    "    )\n",
    "    print(\"Agent Context is online and ready.\")\n",
    "else:\n",
    "    print(\"Cannot proceed without VectorStore.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2db1ef53",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7efb347",
   "metadata": {},
   "source": [
    "## 4. Ingestion: Teaching the Agent\n",
    "\n",
    "We can store different types of information. The system is smart enough to distinguish between a conversational memory and a factual document.\n",
    "\n",
    "### A. Episodic Memory (Conversations)\n",
    "These are raw logs of interactions. They provide the \"personal\" history."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "71adf433",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Memory Stored: mem_2ec8206c1601\n"
     ]
    }
   ],
   "source": [
    "user_id = \"user_123\"\n",
    "session_id = \"session_alpha\"\n",
    "\n",
    "# Store a user preference\n",
    "mem_id = context.store(\n",
    "    content=\"I am working on a new project called 'Project Apollo' which uses Python and React.\",\n",
    "    conversation_id=session_id,\n",
    "    user_id=user_id,\n",
    "    metadata={\"type\": \"user_preference\"}\n",
    ")\n",
    "print(f\"Memory Stored: {mem_id}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00179ec6",
   "metadata": {},
   "source": [
    "### B. Semantic Knowledge (Documents)\n",
    "When we feed documents, we want to **extract entities** and **link them**. \n",
    "\n",
    "*(Note: In a real setup, this uses an LLM to parse entities. Here we use the context module's native extraction capabilities.)*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "3a98ca53",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Knowledge Ingestion Stats: {'stored_count': 2, 'memory_ids': ['mem_4e6d93ce75a2', 'mem_50eecb14c339'], 'graph_nodes': 2, 'graph_edges': 0}\n"
     ]
    }
   ],
   "source": [
    "documents = [\n",
    "    {\n",
    "        \"content\": \"Project Apollo is a next-gen web framework designed for high scalability.\",\n",
    "        \"metadata\": {\"source\": \"internal_wiki\", \"category\": \"projects\"}\n",
    "    },\n",
    "    {\n",
    "        \"content\": \"Python 3.12 introduces significant performance improvements for async workloads.\",\n",
    "        \"metadata\": {\"source\": \"tech_news\", \"category\": \"languages\"}\n",
    "    }\n",
    "]\n",
    "\n",
    "# Store documents and trigger graph build\n",
    "stats = context.store(\n",
    "    documents,\n",
    "    extract_entities=True,      # Extract entities from text\n",
    "    extract_relationships=True, # Infer relationships\n",
    "    link_entities=True          # Connect to existing graph nodes\n",
    ")\n",
    "\n",
    "print(\"Knowledge Ingestion Stats:\", stats)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "912bb201",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2632192",
   "metadata": {},
   "source": [
    "## 5. Graph Engineering: Manual Injection\n",
    "\n",
    "Sometimes automatic extraction isn't enough. You want to enforce specific business logic or relationships. We can use `build_graph` to manually inject nodes and edges.\n",
    "\n",
    "**We will define:**\n",
    "*   **User** (Alice)\n",
    "*   **Role** (Admin)\n",
    "*   **Project** (Apollo)\n",
    "*   **Relationship**: Alice *MANAGES* Project Apollo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "970b605c",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\Users\\Mohd Kaif\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\neo4j\\_sync\\driver.py:515: ResourceWarning: unclosed  BoltDriver: <neo4j._sync.driver.BoltDriver object at 0x00000221C7B9FB90>.\n",
      "  _unclosed_resource_warn(self)\n",
      "ResourceWarning: Enable tracemalloc to get the object allocation traceback\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Manual Graph Build Complete: {'node_count': 6, 'edge_count': 0}\n"
     ]
    }
   ],
   "source": [
    "# 1. Define Nodes\n",
    "entities = [\n",
    "    {\"id\": \"alice\", \"type\": \"PERSON\", \"text\": \"Alice\", \"properties\": {\"role\": \"Admin\"}},\n",
    "    {\"id\": \"project_apollo\", \"type\": \"PROJECT\", \"text\": \"Project Apollo\"},\n",
    "    {\"id\": \"python\", \"type\": \"TECH\", \"text\": \"Python\"},\n",
    "    {\"id\": \"react\", \"type\": \"TECH\", \"text\": \"React\"}\n",
    "]\n",
    "\n",
    "# 2. Define Edges (The Knowledge)\n",
    "relationships = [\n",
    "    {\"source\": \"alice\", \"target\": \"project_apollo\", \"type\": \"MANAGES\", \"weight\": 1.0},\n",
    "    {\"source\": \"project_apollo\", \"target\": \"python\", \"type\": \"USES_TECH\", \"weight\": 1.0},\n",
    "    {\"source\": \"project_apollo\", \"target\": \"react\", \"type\": \"USES_TECH\", \"weight\": 1.0}\n",
    "]\n",
    "\n",
    "# 3. Inject into Graph\n",
    "graph_stats = context.build_graph(\n",
    "    entities=entities,\n",
    "    relationships=relationships\n",
    ")\n",
    "\n",
    "print(\"Manual Graph Build Complete:\", graph_stats)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf04b4a0",
   "metadata": {},
   "source": [
    "### Visualizing the Graph Logic\n",
    "Let's query the graph directly to see what \"Project Apollo\" looks like."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "dc61c324",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Neighbors of 'project_apollo':\n"
     ]
    }
   ],
   "source": [
    "# Helper to print graph neighbors\n",
    "def inspect_node(node_id):\n",
    "    if hasattr(gs, \"get_neighbors\"):\n",
    "        neighbors = gs.get_neighbors(node_id)\n",
    "        print(f\"\\nNeighbors of '{node_id}':\")\n",
    "        for n in neighbors:\n",
    "            # Handle different return formats between stores\n",
    "            rel_type = n.get('relationship') or n.get('type') or 'linked'\n",
    "            target = n.get('id') or n.get('node_id')\n",
    "            print(f\"   └── [{rel_type}] ──> {target}\")\n",
    "    else:\n",
    "        print(\"Graph store does not support neighbor inspection.\")\n",
    "\n",
    "inspect_node(\"project_apollo\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a163434b",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51261f5f",
   "metadata": {},
   "source": [
    "## 6. Hybrid Retrieval (GraphRAG)\n",
    "\n",
    "Now for the magic. We ask a question that requires connecting the dots.\n",
    "\n",
    "**Query**: *\"Who is responsible for the Python web framework project?\"*\n",
    "\n",
    "**Logic Flow:**\n",
    "1.  **Vector Search**: Finds \"Project Apollo\" (described as web framework).\n",
    "2.  **Graph Expansion**: Looks at \"Project Apollo\" in the graph.\n",
    "3.  **Discovery**: Sees `(Alice)-[MANAGES]->(Project Apollo)`.\n",
    "4.  **Result**: Returns Alice, even though her name wasn't in the project description text!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "69381e8c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Asking: 'Who is responsible for the Python web framework project?'...\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\Users\\Mohd Kaif\\semantica\\semantica\\vector_store\\vector_store.py:480: RuntimeWarning: invalid value encountered in divide\n",
      "  similarities = np.dot(vectors, query_vector) / (vector_norms * query_norm)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Retrieved 3 context items:\n",
      "\n",
      "1. [Score: 0.54] Project Apollo is a next-gen web framework designed for high scalability....\n",
      "\n",
      "2. [Score: 0.32] Python 3.12 introduces significant performance improvements for async workloads....\n",
      "\n",
      "3. [Score: 0.21] I am working on a new project called 'Project Apollo' which uses Python and React....\n",
      "\n"
     ]
    }
   ],
   "source": [
    "query = \"Who is responsible for the Python web framework project?\"\n",
    "print(f\"Asking: '{query}'...\\n\")\n",
    "\n",
    "results = context.retrieve(\n",
    "    query,\n",
    "    max_results=3,\n",
    "    use_graph=True,         # Vital for finding Alice\n",
    "    expand_graph=True,      # Hop to neighbors\n",
    "    include_entities=True   # Return structured entity data\n",
    ")\n",
    "\n",
    "print(f\"Retrieved {len(results)} context items:\\n\")\n",
    "\n",
    "for i, res in enumerate(results, 1):\n",
    "    print(f\"{i}. [Score: {res['score']:.2f}] {res['content'][:120]}...\")\n",
    "    \n",
    "    # Did we find graph connections?\n",
    "    if 'related_entities' in res and res['related_entities']:\n",
    "        print(\"   Graph Insights:\")\n",
    "        for ent in res['related_entities'][:3]:\n",
    "            print(f\"      - {ent.get('text', 'Entity')} ({ent.get('type', 'Unknown')})\")\n",
    "    print(\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c21fbb00",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c18870af",
   "metadata": {},
   "source": [
    "## 7. Lifecycle Management\n",
    "\n",
    "A production system needs maintenance. You can query history, check health, and prune old data.\n",
    "\n",
    "### Conversation History"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "0574b1b7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Chat History for session_alpha:\n"
     ]
    }
   ],
   "source": [
    "# Get recent chat history for context window\n",
    "history = context.conversation(\n",
    "    conversation_id=session_id,\n",
    "    limit=5\n",
    ")\n",
    "\n",
    "print(f\"Chat History for {session_id}:\")\n",
    "for msg in history:\n",
    "    print(f\" - {msg['content']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a40483fc",
   "metadata": {},
   "source": [
    "### System Health & Stats"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "cd472495",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "System Vital Signs:\n",
      "   - Total Memories: 3\n",
      "   - Graph Nodes:    N/A\n",
      "   - Graph Edges:    N/A\n"
     ]
    }
   ],
   "source": [
    "stats = context.stats()\n",
    "print(\"System Vital Signs:\")\n",
    "print(f\"   - Total Memories: {stats.get('total_items', 0)}\")\n",
    "print(f\"   - Graph Nodes:    {stats.get('graph_stats', {}).get('node_count', 'N/A')}\")\n",
    "print(f\"   - Graph Edges:    {stats.get('graph_stats', {}).get('edge_count', 'N/A')}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "You have successfully built a **Context-Aware Agent** using Semantica's production modules.\n",
    "\n",
    "**Key Achievements:**\n",
    "1.  **Persistence**: Swapped in FAISS and Neo4j for real-world storage.\n",
    "2.  **GraphRAG**: Demonstrated how graph relationships improve retrieval accuracy.\n",
    "3.  **Entity Injection**: Manually taught the agent about business relationships.\n",
    "\n",
    "This architecture is ready to scale to millions of vectors and graph nodes."
   ]
  }
 ],
 "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.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
