{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "68e1c158",
   "metadata": {},
   "source": [
    "# Building Semantic Memory with Embeddings\n",
    "\n",
    "So far, we've mostly been treating the kernel as a stateless orchestration engine.\n",
    "We send text into a model API and receive text out.\n",
    "\n",
    "In a [previous notebook](04-kernel-arguments-chat.ipynb), we used `kernel arguments` to pass in additional\n",
    "text into prompts to enrich them with more data. This allowed us to create a basic chat experience.\n",
    "\n",
    "However, if you solely relied on kernel arguments, you would quickly realize that eventually your prompt\n",
    "would grow so large that you would run into the model's token limit. What we need is a way to persist state\n",
    "and build both short-term and long-term memory to empower even more intelligent applications.\n",
    "\n",
    "To do this, we dive into the key concept of `Semantic Memory` in the Semantic Kernel.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6713abcd",
   "metadata": {},
   "source": [
    "Import Semantic Kernel SDK from pypi.org and other dependencies for this example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a77bdf89",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Note: if using a virtual environment, do not run this cell\n",
    "%pip install -U semantic-kernel[azure]\n",
    "from semantic_kernel import __version__\n",
    "\n",
    "__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "318033fe",
   "metadata": {},
   "source": [
    "Initial configuration for the notebook to run properly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d8a3db35",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Make sure paths are correct for the imports\n",
    "\n",
    "import os\n",
    "import sys\n",
    "\n",
    "notebook_dir = os.path.abspath(\"\")\n",
    "parent_dir = os.path.dirname(notebook_dir)\n",
    "grandparent_dir = os.path.dirname(parent_dir)\n",
    "\n",
    "\n",
    "sys.path.append(grandparent_dir)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c5f9651",
   "metadata": {},
   "source": [
    "### Configuring the Kernel\n",
    "\n",
    "Let's get started with the necessary configuration to run Semantic Kernel. For Notebooks, we require a `.env` file with the proper settings for the model you use. Create a new file named `.env` and place it in this directory. Copy the contents of the `.env.example` file from this directory and paste it into the `.env` file that you just created.\n",
    "\n",
    "**NOTE: Please make sure to include `GLOBAL_LLM_SERVICE` set to either OpenAI, AzureOpenAI, or HuggingFace in your .env file. If this setting is not included, the Service will default to AzureOpenAI.**\n",
    "\n",
    "#### Option 1: using OpenAI\n",
    "\n",
    "Add your [OpenAI Key](https://openai.com/product/) key to your `.env` file (org Id only if you have multiple orgs):\n",
    "\n",
    "```\n",
    "GLOBAL_LLM_SERVICE=\"OpenAI\"\n",
    "OPENAI_API_KEY=\"sk-...\"\n",
    "OPENAI_ORG_ID=\"\"\n",
    "OPENAI_CHAT_MODEL_ID=\"\"\n",
    "OPENAI_TEXT_MODEL_ID=\"\"\n",
    "OPENAI_EMBEDDING_MODEL_ID=\"\"\n",
    "```\n",
    "The names should match the names used in the `.env` file, as shown above.\n",
    "\n",
    "#### Option 2: using Azure OpenAI\n",
    "\n",
    "Add your [Azure Open AI Service key](https://learn.microsoft.com/azure/cognitive-services/openai/quickstart?pivots=programming-language-studio) settings to the `.env` file in the same folder:\n",
    "\n",
    "```\n",
    "GLOBAL_LLM_SERVICE=\"AzureOpenAI\"\n",
    "AZURE_OPENAI_API_KEY=\"...\"\n",
    "AZURE_OPENAI_ENDPOINT=\"https://...\"\n",
    "AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=\"...\"\n",
    "AZURE_OPENAI_TEXT_DEPLOYMENT_NAME=\"...\"\n",
    "AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME=\"...\"\n",
    "AZURE_OPENAI_API_VERSION=\"...\"\n",
    "```\n",
    "The names should match the names used in the `.env` file, as shown above.\n",
    "\n",
    "As alternative to `AZURE_OPENAI_API_KEY`, it's possible to authenticate using `credential` parameter, more information here: [Azure Identity](https://learn.microsoft.com/en-us/python/api/overview/azure/identity-readme).\n",
    "\n",
    "In the following example, `AzureCliCredential` is used. To authenticate using Azure CLI:\n",
    "\n",
    "1. Install [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli).\n",
    "2. Run `az login` command in terminal and follow the authentication steps.\n",
    "\n",
    "For more advanced configuration, please follow the steps outlined in the [setup guide](./CONFIGURING_THE_KERNEL.md)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "815cac6e",
   "metadata": {},
   "source": [
    "We will load our settings and get the LLM service to use for the notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "1b95af24",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using service type: Service.OpenAI\n"
     ]
    }
   ],
   "source": [
    "from services import Service\n",
    "\n",
    "from samples.service_settings import ServiceSettings\n",
    "\n",
    "service_settings = ServiceSettings()\n",
    "\n",
    "# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
    "selectedService = (\n",
    "    Service.AzureOpenAI\n",
    "    if service_settings.global_llm_service is None\n",
    "    else Service(service_settings.global_llm_service.lower())\n",
    ")\n",
    "print(f\"Using service type: {selectedService}\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "d8ddffc1",
   "metadata": {},
   "source": [
    "In order to use memory, we need to instantiate the Kernel with a Memory Storage\n",
    "and an Embedding service. In this example, we make use of the `VolatileMemoryStore` which can be thought of as a temporary in-memory storage. This memory is not written to disk and is only available during the app session.\n",
    "\n",
    "When developing your app you will have the option to plug in persistent storage like Azure AI Search, Azure Cosmos Db, PostgreSQL, SQLite, etc. Semantic Memory allows also to index external data sources, without duplicating all the information as you will see further down in this notebook.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f8dcbc6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel import Kernel\n",
    "from semantic_kernel.connectors.ai.open_ai import (\n",
    "    AzureChatCompletion,\n",
    "    AzureTextEmbedding,\n",
    "    OpenAIChatCompletion,\n",
    "    OpenAITextEmbedding,\n",
    ")\n",
    "\n",
    "kernel = Kernel()\n",
    "\n",
    "chat_service_id = \"chat\"\n",
    "\n",
    "# Configure AI service used by the kernel\n",
    "if selectedService == Service.AzureOpenAI:\n",
    "    from azure.identity import AzureCliCredential\n",
    "\n",
    "    credential = AzureCliCredential()\n",
    "    azure_chat_service = AzureChatCompletion(service_id=chat_service_id, credential=credential)\n",
    "    embedding_gen = AzureTextEmbedding(service_id=\"embedding\", credential=credential)\n",
    "    kernel.add_service(azure_chat_service)\n",
    "    kernel.add_service(embedding_gen)\n",
    "elif selectedService == Service.OpenAI:\n",
    "    oai_chat_service = OpenAIChatCompletion(\n",
    "        service_id=chat_service_id,\n",
    "    )\n",
    "    embedding_gen = OpenAITextEmbedding(\n",
    "        service_id=\"embedding\",\n",
    "    )\n",
    "    kernel.add_service(oai_chat_service)\n",
    "    kernel.add_service(embedding_gen)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da74fa33",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass, field\n",
    "from typing import Annotated\n",
    "from uuid import uuid4\n",
    "\n",
    "from semantic_kernel.data.vector import VectorStoreField, vectorstoremodel\n",
    "\n",
    "\n",
    "@vectorstoremodel(collection_name=\"simple-model\")\n",
    "@dataclass\n",
    "class SimpleModel:\n",
    "    \"\"\"Simple model to store some text with a ID.\"\"\"\n",
    "\n",
    "    text: Annotated[str, VectorStoreField(\"data\", is_full_text_indexed=True)]\n",
    "    id: Annotated[str, VectorStoreField(\"key\")] = field(default_factory=lambda: str(uuid4()))\n",
    "    embedding: Annotated[\n",
    "        list[float] | str | None, VectorStoreField(\"vector\", dimensions=1536, embedding_generator=embedding_gen)\n",
    "    ] = None\n",
    "\n",
    "    def __post_init__(self):\n",
    "        if self.embedding is None:\n",
    "            self.embedding = self.text"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "e7fefb6a",
   "metadata": {},
   "source": [
    "At its core, Semantic Memory is a set of data structures that allow you to store the meaning of text that come from different data sources, and optionally to store the source text too. These texts can be from the web, e-mail providers, chats, a database, or from your local directory, and are hooked up to the Semantic Kernel through data source connectors.\n",
    "\n",
    "The texts are embedded or compressed into a vector of floats representing mathematically the texts' contents and meaning. You can read more about embeddings [here](https://aka.ms/sk/embeddings).\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "2a7e7ca4",
   "metadata": {},
   "source": [
    "### Manually adding memories\n",
    "\n",
    "Let's create some initial memories \"About Me\". We can add memories to our `VolatileMemoryStore` by using `SaveInformationAsync`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d096504c",
   "metadata": {},
   "outputs": [],
   "source": [
    "records = [\n",
    "    SimpleModel(text=\"Your budget for 2024 is $100,000\"),\n",
    "    SimpleModel(text=\"Your savings from 2023 are $50,000\"),\n",
    "    SimpleModel(text=\"Your investments are $80,000\"),\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "5338d3ac",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['e6f2c669-0996-4cc5-8312-59e7d996a32b',\n",
       " '9042c465-115d-422d-b5f7-5ce35bd36455',\n",
       " 'ecc3ec62-b1b5-4a41-a761-63280d3a4329']"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from semantic_kernel.connectors.in_memory import InMemoryStore\n",
    "\n",
    "in_memory_store = InMemoryStore()\n",
    "\n",
    "collection = in_memory_store.get_collection(record_type=SimpleModel)\n",
    "await collection.ensure_collection_exists()\n",
    "# Add records to the collection\n",
    "await collection.upsert(records)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "2calf857",
   "metadata": {},
   "source": [
    "Let's try searching the memory:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "628c843e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.data.vector import VectorSearchProtocol\n",
    "\n",
    "\n",
    "async def search_memory_examples(collection: VectorSearchProtocol, questions: list[str]) -> None:\n",
    "    for question in questions:\n",
    "        print(f\"Question: {question}\")\n",
    "        results = await collection.search(question, top=1)\n",
    "        async for result in results.results:\n",
    "            print(f\"Answer: {result.record.text}\")\n",
    "            print(f\"Score: {result.score}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe185991",
   "metadata": {},
   "source": [
    "The default distance metric for the InMemoryCollection is `cosine`, this means that the closer the vectors are, the more similar they are. The `search` method will return the top `3` results by default, but you can change this by passing in the `top` parameter, this is set to `1` here to get only the most relevant result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "24764c48",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Question: What is my budget for 2024?\n",
      "Answer: Your budget for 2024 is $100,000\n",
      "Score: 0.20661429378068685\n",
      "\n",
      "Question: What are my savings from 2023?\n",
      "Answer: Your savings from 2023 are $50,000\n",
      "Score: 0.2028375831967465\n",
      "\n",
      "Question: What are my investments?\n",
      "Answer: Your investments are $80,000\n",
      "Score: 0.33724588631042385\n",
      "\n"
     ]
    }
   ],
   "source": [
    "await search_memory_examples(\n",
    "    collection,\n",
    "    questions=[\n",
    "        \"What is my budget for 2024?\",\n",
    "        \"What are my savings from 2023?\",\n",
    "        \"What are my investments?\",\n",
    "    ],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2133d9d6",
   "metadata": {},
   "source": [
    "Next, we will add a search function to our kernel that will allow us to search the memory store for relevant information. This function will use the `search` method of the `InMemoryStore` to find the most relevant memories based on a query."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "9c1fdace",
   "metadata": {},
   "outputs": [],
   "source": [
    "func = kernel.add_function(\n",
    "    plugin_name=\"memory\",\n",
    "    function=collection.create_search_function(\n",
    "        function_name=\"recall\",\n",
    "        description=\"Searches the memory for relevant information based on the input query.\",\n",
    "    ),\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5dbaf80",
   "metadata": {},
   "source": [
    "Then we will create a prompt that will use the search function to find relevant information in the memory store and return it as part of the prompt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "fb8549b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from semantic_kernel.functions import KernelFunction\n",
    "from semantic_kernel.prompt_template import PromptTemplateConfig\n",
    "\n",
    "\n",
    "async def setup_chat_with_memory(\n",
    "    kernel: Kernel,\n",
    "    service_id: str,\n",
    ") -> KernelFunction:\n",
    "    prompt = \"\"\"\n",
    "    ChatBot can have a conversation with you about any topic.\n",
    "    It can give explicit instructions or say 'I don't know' if\n",
    "    it does not have an answer.\n",
    "\n",
    "    Information about me, from previous conversations:\n",
    "    - {{recall 'budget by year'}} What is my budget for 2024?\n",
    "    - {{recall 'savings from previous year'}} What are my savings from 2023?\n",
    "    - {{recall 'investments'}} What are my investments?\n",
    "\n",
    "    {{$request}}\n",
    "    \"\"\".strip()\n",
    "\n",
    "    prompt_template_config = PromptTemplateConfig(\n",
    "        template=prompt,\n",
    "        execution_settings={\n",
    "            service_id: kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)\n",
    "        },\n",
    "    )\n",
    "\n",
    "    return kernel.add_function(\n",
    "        function_name=\"chat_with_memory\",\n",
    "        plugin_name=\"chat\",\n",
    "        prompt_template_config=prompt_template_config,\n",
    "    )"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "645b55a1",
   "metadata": {},
   "source": [
    "Now that we've included our memories, let's chat!\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "e3875a34",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Setting up a chat (with memory!)\n",
      "Begin chatting (type 'exit' to exit):\n",
      "\n",
      "Welcome to the chat bot!    \n",
      "  Type 'exit' to exit.    \n",
      "  Try asking a question about your finances (i.e. \"talk to me about my finances\").\n"
     ]
    }
   ],
   "source": [
    "print(\"Setting up a chat (with memory!)\")\n",
    "chat_func = await setup_chat_with_memory(kernel, chat_service_id)\n",
    "\n",
    "print(\"Begin chatting (type 'exit' to exit):\\n\")\n",
    "print(\n",
    "    \"Welcome to the chat bot!\\\n",
    "    \\n  Type 'exit' to exit.\\\n",
    "    \\n  Try asking a question about your finances (i.e. \\\"talk to me about my finances\\\").\"\n",
    ")\n",
    "\n",
    "\n",
    "async def chat(user_input: str):\n",
    "    print(f\"User: {user_input}\")\n",
    "    answer = await kernel.invoke(chat_func, request=user_input)\n",
    "    print(f\"ChatBot:> {answer}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "6b55f64f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User: What is my budget for 2024?\n",
      "ChatBot:> Your budget for 2024 is $100,000.\n"
     ]
    }
   ],
   "source": [
    "await chat(\"What is my budget for 2024?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "243f9eb2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User: talk to me about my finances\n",
      "ChatBot:> Based on our previous conversations, your investments amount to $80,000. Your financial overview includes a budget for 2024 of $100,000 and savings from 2023 totaling $50,000. If you'd like, I can help you analyze your financial goals, suggest investment strategies, or assist with budgeting. Let me know how you'd like to proceed!\n"
     ]
    }
   ],
   "source": [
    "await chat(\"talk to me about my finances\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "0a51542b",
   "metadata": {},
   "source": [
    "### Adding documents to your memory\n",
    "\n",
    "Many times in your applications you'll want to bring in external documents into your memory. Let's see how we can do this using our VolatileMemoryStore.\n",
    "\n",
    "Let's first get some data using some of the links in the Semantic Kernel repo.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "170e7142",
   "metadata": {},
   "outputs": [],
   "source": [
    "@vectorstoremodel(collection_name=\"github-files\")\n",
    "@dataclass\n",
    "class GitHubFile:\n",
    "    \"\"\"\n",
    "    Model to store GitHub file URLs and their descriptions.\n",
    "    \"\"\"\n",
    "\n",
    "    url: Annotated[str, VectorStoreField(\"data\", is_full_text_indexed=True)]\n",
    "    text: Annotated[str, VectorStoreField(\"data\", is_full_text_indexed=True)]\n",
    "    key: Annotated[str, VectorStoreField(\"key\")] = field(default_factory=lambda: str(uuid4()))\n",
    "    embedding: Annotated[\n",
    "        list[float] | str | None, VectorStoreField(\"vector\", dimensions=1536, embedding_generator=embedding_gen)\n",
    "    ] = None\n",
    "\n",
    "    def __post_init__(self):\n",
    "        if self.embedding is None:\n",
    "            self.embedding = f\"{self.url} {self.text}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "0d971eeb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['81585815-6be9-4b45-b608-af1f9918567f',\n",
       " '83d5e6ba-a6f9-4d05-aa27-0b5470c1aa83',\n",
       " 'e015e196-ddb3-4157-9291-dcef201d5308']"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "github_files = []\n",
    "github_files.append(\n",
    "    GitHubFile(\n",
    "        url=\"https://github.com/microsoft/semantic-kernel/blob/main/README.md\",\n",
    "        text=\"README: Installation, getting started, and how to contribute\",\n",
    "    )\n",
    ")\n",
    "github_files.append(\n",
    "    GitHubFile(\n",
    "        url=\"https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/02-running-prompts-from-file.ipynb\",\n",
    "        text=\"Jupyter notebook describing how to pass prompts from a file to a semantic plugin or function\",\n",
    "    )\n",
    ")\n",
    "github_files.append(\n",
    "    GitHubFile(\n",
    "        url=\"https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/00-getting-started.ipynb\",\n",
    "        text=\"Jupyter notebook describing how to get started with Semantic Kernel\",\n",
    "    )\n",
    ")\n",
    "\n",
    "github_memory = in_memory_store.get_collection(record_type=GitHubFile)\n",
    "await github_memory.ensure_collection_exists()\n",
    "\n",
    "# Add records to the collection\n",
    "await github_memory.upsert(github_files)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "143911c3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "===========================\n",
      "Query: I love Jupyter notebooks, how should I get started?\n",
      "\n",
      "Result e015e196-ddb3-4157-9291-dcef201d5308:\n",
      "  URL        : https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/00-getting-started.ipynb\n",
      "  Text       : Jupyter notebook describing how to get started with Semantic Kernel\n",
      "  Relevance  : 0.38310321217805676\n",
      "\n",
      "Result 83d5e6ba-a6f9-4d05-aa27-0b5470c1aa83:\n",
      "  URL        : https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/02-running-prompts-from-file.ipynb\n",
      "  Text       : Jupyter notebook describing how to pass prompts from a file to a semantic plugin or function\n",
      "  Relevance  : 0.5391694801842719\n",
      "\n",
      "Result 81585815-6be9-4b45-b608-af1f9918567f:\n",
      "  URL        : https://github.com/microsoft/semantic-kernel/blob/main/README.md\n",
      "  Text       : README: Installation, getting started, and how to contribute\n",
      "  Relevance  : 0.6495028830870797\n",
      "\n"
     ]
    }
   ],
   "source": [
    "ask = \"I love Jupyter notebooks, how should I get started?\"\n",
    "print(\"===========================\\n\" + \"Query: \" + ask + \"\\n\")\n",
    "\n",
    "memories = await github_memory.search(ask, top=5)\n",
    "\n",
    "async for result in memories.results:\n",
    "    memory = result.record\n",
    "    print(f\"Result {memory.key}:\")\n",
    "    print(f\"  URL        : {memory.url}\")\n",
    "    print(f\"  Text       : {memory.text}\")\n",
    "    print(f\"  Relevance  : {result.score}\")\n",
    "    print()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "59294dac",
   "metadata": {},
   "source": [
    "Now you might be wondering what happens if you have so much data that it doesn't fit into your RAM? That's where you want to make use of an external Vector Database made specifically for storing and retrieving embeddings. Fortunately, semantic kernel makes this easy thanks to an extensive list of available connectors. In the following section, we will connect to an existing Azure AI Search service that we will use as an external Vector Database to store and retrieve embeddings.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e78fd381",
   "metadata": {},
   "source": [
    "_Please note you will need an AzureAI Search api_key or token credential and endpoint for the following example to work properly._"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "77fdfa86",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['81585815-6be9-4b45-b608-af1f9918567f',\n",
       " '83d5e6ba-a6f9-4d05-aa27-0b5470c1aa83',\n",
       " 'e015e196-ddb3-4157-9291-dcef201d5308']"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from semantic_kernel.connectors.azure_ai_search import AzureAISearchCollection\n",
    "\n",
    "azs_memory = AzureAISearchCollection(record_type=GitHubFile)\n",
    "# We explicitly delete the collection if it exists to ensure a clean state\n",
    "await azs_memory.ensure_collection_deleted()\n",
    "await azs_memory.ensure_collection_exists()\n",
    "# Add records to the collection\n",
    "await azs_memory.upsert(github_files)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94f9e83b",
   "metadata": {},
   "source": [
    "The implementation of Semantic Kernel allows to easily swap memory store for another. Here, we will re-use the functions we initially created for `InMemoryStore` with our new external Vector Store leveraging Azure AI Search\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0bbe830",
   "metadata": {},
   "source": [
    "Let's now try to query from Azure AI Search! Note that the `score` might be different because the AzureAISearchCollection uses a different default distance metric then InMemoryCollection, if you specify the `distance_function` in the model, you can get the same results as with the InMemoryCollection.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "1a09d0ca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Question: What is Semantic Kernel?\n",
      "Answer: README: Installation, getting started, and how to contribute\n",
      "Score: 0.72694075\n",
      "\n",
      "Question: How do I get started on it with notebooks?\n",
      "Answer: Jupyter notebook describing how to get started with Semantic Kernel\n",
      "Score: 0.68709856\n",
      "\n",
      "Question: Where can I find more info on prompts?\n",
      "Answer: Jupyter notebook describing how to pass prompts from a file to a semantic plugin or function\n",
      "Score: 0.6572231\n",
      "\n"
     ]
    }
   ],
   "source": [
    "await search_memory_examples(\n",
    "    azs_memory,\n",
    "    questions=[\n",
    "        \"What is Semantic Kernel?\",\n",
    "        \"How do I get started on it with notebooks?\",\n",
    "        \"Where can I find more info on prompts?\",\n",
    "    ],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f0800fc",
   "metadata": {},
   "source": [
    "Make sure to cleanup!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "82cd429e",
   "metadata": {},
   "outputs": [],
   "source": [
    "await azs_memory.ensure_collection_deleted()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d33dcdc",
   "metadata": {},
   "source": [
    "We have laid the foundation which will allow us to store an arbitrary amount of data in an external Vector Store above and beyond what could fit in memory at the expense of a little more latency.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python",
   "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.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
