{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "gpuType": "T4"
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# RAG using Langchain"
      ],
      "metadata": {
        "id": "fJXPuMBgsO_I"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Packages loading & import"
      ],
      "metadata": {
        "id": "J189g9PHscOJ"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install langchain\n",
        "!pip install langchain_community\n",
        "!pip install langchain_huggingface\n",
        "!pip install langchain_text_splitters\n",
        "!pip install langchain_chroma\n",
        "!pip install rank-bm25\n",
        "!pip install huggingface_hub"
      ],
      "metadata": {
        "id": "LztGdoQClLDK"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "import os\n",
        "import json\n",
        "import bs4\n",
        "import nltk\n",
        "import torch\n",
        "import pickle\n",
        "import numpy as np\n",
        "\n",
        "# from pyserini.index import IndexWriter\n",
        "# from pyserini.search import SimpleSearcher\n",
        "from numpy.linalg import norm\n",
        "from rank_bm25 import BM25Okapi\n",
        "from nltk.tokenize import word_tokenize\n",
        "\n",
        "from langchain_community.llms import Ollama\n",
        "from langchain.chains.combine_documents import create_stuff_documents_chain\n",
        "from langchain.chains import create_retrieval_chain\n",
        "from langchain.vectorstores import Chroma\n",
        "from sentence_transformers import SentenceTransformer\n",
        "from langchain_huggingface import HuggingFaceEmbeddings\n",
        "from langchain_community.embeddings import JinaEmbeddings\n",
        "from langchain.text_splitter import RecursiveCharacterTextSplitter, TokenTextSplitter\n",
        "from langchain.docstore.document import Document\n",
        "from langchain_core.prompts import ChatPromptTemplate\n",
        "from langchain_community.document_loaders import WebBaseLoader\n",
        "from transformers import AutoModel, AutoModelForCausalLM, AutoTokenizer\n",
        "\n",
        "from tqdm import tqdm"
      ],
      "metadata": {
        "id": "R7fVDp_MSM6d"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "nltk.download('punkt')\n",
        "nltk.download('punkt_tab')"
      ],
      "metadata": {
        "id": "KZaXzYlMakAa"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Hugging face login\n",
        "- Please apply the model first: https://huggingface.co/meta-llama/Llama-3.2-1B-Instruct\n",
        "- If you haven't been granted access to this model, you can use other LLM model that doesn't have to apply.\n",
        "- You must save the hf token otherwise you need to regenrate the token everytime.\n",
        "- When using Ollama, no login is required to access and utilize the llama model."
      ],
      "metadata": {
        "id": "_rqtezQ8shF0"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from huggingface_hub import login\n",
        "\n",
        "hf_token = \"Put your Hugging Face Access Token Key Here\"\n",
        "login(token=hf_token, add_to_git_credential=True)"
      ],
      "metadata": {
        "id": "Wr0_DNixrDT8"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "!huggingface-cli whoami"
      ],
      "metadata": {
        "id": "zfN6G4fzr8CG"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## TODO1: Set up the environment of Ollama"
      ],
      "metadata": {
        "id": "DMtZo6OVsUP5"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Introduction to Ollama\n",
        "- Ollama is a platform designed for running and managing large language models (LLMs) directly **on local devices**, providing a balance between performance, privacy, and control.\n",
        "- There are also other tools support users to manage LLM on local devices and accelerate it like *vllm*, *Llamafile*, *GPT4ALL*...etc."
      ],
      "metadata": {
        "id": "PhZ_q0V4ciJc"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Launch colabxterm"
      ],
      "metadata": {
        "id": "xv8QM9xj3oUL"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# TODO1-1: You should install colab-xterm and launch it.\n",
        "# Write your commands here."
      ],
      "metadata": {
        "id": "Y9agmoxFd7um"
      },
      "execution_count": 2,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# TODO1-2: You should install Ollama.\n",
        "# You may need root privileges if you use a local machine instead of Colab."
      ],
      "metadata": {
        "id": "wVQMKksD6y4K",
        "outputId": "b07a3ee0-b0d6-453a-b49c-142c56e7c4ee",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            ">>> Installing ollama to /usr/local\n",
            ">>> Downloading Linux amd64 bundle\n",
            "############################################################################################# 100.0%\n",
            ">>> Creating ollama user...\n",
            ">>> Adding ollama user to video group...\n",
            ">>> Adding current user to ollama group...\n",
            ">>> Creating ollama systemd service...\n",
            "\u001b[1m\u001b[31mWARNING:\u001b[m systemd is not running\n",
            "\u001b[1m\u001b[31mWARNING:\u001b[m Unable to detect NVIDIA/AMD GPU. Install lspci or lshw to automatically detect and install GPU dependencies.\n",
            ">>> The Ollama API is now available at 127.0.0.1:11434.\n",
            ">>> Install complete. Run \"ollama\" from the command line.\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "%xterm"
      ],
      "metadata": {
        "id": "10NK09FKd_th"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# TODO1-3: Pull Llama3.2:1b via Ollama and start the Ollama service in the xterm\n",
        "# Write your commands in the xterm"
      ],
      "metadata": {
        "id": "jbj5MMnh8Htl"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Ollama testing\n",
        "You can test your Ollama status with the following cells."
      ],
      "metadata": {
        "id": "22kvHBerCCqm"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "v_hBBGBOnH4P"
      },
      "outputs": [],
      "source": [
        "# Setting up the model that this tutorial will use\n",
        "MODEL = \"llama3.2:1b\" # https://ollama.com/library/llama3.2:3b\n",
        "EMBED_MODEL = \"jinaai/jina-embeddings-v2-base-en\""
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Initialize an instance of the Ollama model\n",
        "llm = Ollama(model=MODEL)\n",
        "# Invoke the model to generate responses\n",
        "response = llm.invoke(\"What is the capital of Taiwan?\")\n",
        "print(response)"
      ],
      "metadata": {
        "id": "T2sq6FoDWjVl"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Build a simple RAG system by using LangChain"
      ],
      "metadata": {
        "id": "X5ndgmF6wYHY"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### TODO2: Load the cat-facts dataset and prepare the retrieval database"
      ],
      "metadata": {
        "id": "Lzul2yQR5q4I"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "!wget https://huggingface.co/ngxson/demo_simple_rag_py/resolve/main/cat-facts.txt"
      ],
      "metadata": {
        "id": "RJLNgILQ55UH"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# TODO2-1: Load the cat-facts dataset (as `refs`, which is a list of strings for all the cat facts)\n",
        "# Write your code here"
      ],
      "metadata": {
        "id": "aMWMylYS-XRM"
      },
      "execution_count": 4,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "from langchain_core.documents import Document\n",
        "docs = [Document(page_content=doc, metadata={\"id\": i}) for i, doc in enumerate(refs)]"
      ],
      "metadata": {
        "id": "V66CXJr1BAu0"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# Create an embedding model\n",
        "model_kwargs = {'trust_remote_code': True}\n",
        "encode_kwargs = {'normalize_embeddings': False}\n",
        "embeddings_model = HuggingFaceEmbeddings(\n",
        "    model_name=EMBED_MODEL,\n",
        "    model_kwargs=model_kwargs,\n",
        "    encode_kwargs=encode_kwargs\n",
        ")"
      ],
      "metadata": {
        "id": "NmPvFlGxe-mr"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# TODO2-2: Prepare the retrieval database\n",
        "# You should create a Chroma vector store.\n",
        "# search_type can be “similarity” (default), “mmr”, or “similarity_score_threshold”\n",
        "vector_store = Chroma.from_documents(\n",
        "    # Write your code here\n",
        ")\n",
        "retriever = vector_store.as_retriever(\n",
        "    # Write your code here\n",
        ")"
      ],
      "metadata": {
        "id": "A3Rn4z4siC6e"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Prompt setting"
      ],
      "metadata": {
        "id": "ZmtvJMdH61Y4"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# TODO3: Set up the `system_prompt` and configure the prompt.\n",
        "system_prompt = # Write your code here\n",
        "prompt = ChatPromptTemplate.from_messages(\n",
        "    [\n",
        "        (\"system\", system_prompt),\n",
        "        (\"human\", \"{input}\"),\n",
        "    ]\n",
        ")"
      ],
      "metadata": {
        "id": "-oar4tEQONkl"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "- For the vectorspace, the common algorithm would be used like Faiss, Chroma...(https://python.langchain.com/docs/integrations/vectorstores/) to deal with the extreme huge database."
      ],
      "metadata": {
        "id": "rG9tEFr8wvLX"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# TODO4: Build and run the RAG system\n",
        "# TODO4-1: Load the QA chain\n",
        "# You should create a chain for passing a list of Documents to a model.\n",
        "question_answer_chain = # Write your code here\n",
        "\n",
        "# TODO4-2: Create retrieval chain\n",
        "# You should create retrieval chain that retrieves documents and then passes them on.\n",
        "chain = # Write your code here\n"
      ],
      "metadata": {
        "id": "yVbi3irCUQn9"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# Question (queries) and answer pairs\n",
        "# Please do not modify this cell.\n",
        "queries = [\n",
        "    \"How much of a day do cats spend sleeping on average?\",\n",
        "    \"What is the technical term for a cat's hairball?\",\n",
        "    \"What do scientists believe caused cats to lose their sweet tooth?\",\n",
        "    \"What is the top speed a cat can travel over short distances?\",\n",
        "    \"What is the name of the organ in a cat's mouth that helps it smell?\",\n",
        "    \"Which wildcat is considered the ancestor of all domestic cats?\",\n",
        "    \"What is the group term for cats?\",\n",
        "    \"How many different sounds can cats make?\",\n",
        "    \"What is the name of the first cat in space?\",\n",
        "    \"How many toes does a cat have on its back paws?\"\n",
        "]\n",
        "answers = [\n",
        "    \"2/3\",\n",
        "    \"Bezoar\",\n",
        "    \"a mutation in a key taste receptor\",\n",
        "    [\"31 mph\", \"49 km\"],\n",
        "    \"Jacobson’s organ\",\n",
        "    \"the African Wild Cat\",\n",
        "    \"clowder\",\n",
        "    \"100\",\n",
        "    [\"Felicette\", \"Astrocat\"],\n",
        "    \"four\",\n",
        "]"
      ],
      "metadata": {
        "id": "OLn0u3E-UwTK"
      },
      "execution_count": 1,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "counts = 0\n",
        "for i, query in enumerate(queries):\n",
        "    # TODO4-3: Run the RAG system\n",
        "    response = # Write your code here\n",
        "    print(f\"Query: {query}\\nResponse: {response['answer']}\\n\")\n",
        "    # The following lines perform evaluations.\n",
        "    # if the answer shows up in your response, the response is considered correct.\n",
        "    if type(answers[i]) == list:\n",
        "        for answer in answers[i]:\n",
        "            if answer.lower() in response['answer'].lower():\n",
        "                counts += 1\n",
        "                break\n",
        "    else:\n",
        "        if answers[i].lower() in response['answer'].lower():\n",
        "            counts += 1\n",
        "\n",
        "# TODO5: Improve to let the LLM correctly answer the ten questions.\n",
        "print(f\"Correct numbers: {counts}\")"
      ],
      "metadata": {
        "id": "6fLKCHWkizlr"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}