From 6f6eb170a903aad913f1ad697fbd2c73b401b02c Mon Sep 17 00:00:00 2001 From: Rithik Kumar <46047011+rithikJha@users.noreply.github.com> Date: Thu, 29 Aug 2024 13:48:10 +0530 Subject: [PATCH] docs: revamp Python example: Overview page and remove redundant examples and notebooks (#1574) before: ![Screenshot 2024-08-29 131656](https://github.com/user-attachments/assets/81cb5d70-5dff-4e57-8bbe-3461327aed7d) After: ![Screenshot 2024-08-29 131715](https://github.com/user-attachments/assets/62109a37-7f66-4fd4-90ed-906a85472117) --------- Co-authored-by: Ayush Chaurasia --- docs/mkdocs.yml | 6 - docs/src/examples/examples_python.md | 33 +- docs/src/notebooks/code_qa_bot.ipynb | 378 ---------- docs/src/notebooks/multimodal_search.ipynb | 297 -------- .../notebooks/youtube_transcript_search.ipynb | 702 ------------------ 5 files changed, 19 insertions(+), 1397 deletions(-) delete mode 100644 docs/src/notebooks/code_qa_bot.ipynb delete mode 100644 docs/src/notebooks/multimodal_search.ipynb delete mode 100644 docs/src/notebooks/youtube_transcript_search.ipynb diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 9685303e..0b6be3e5 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -168,9 +168,6 @@ nav: - AI Agent: examples/python_examples/aiagent.md - Recommender System: examples/python_examples/recommendersystem.md - Miscellaneous: - - YouTube Transcript Search: notebooks/youtube_transcript_search.ipynb - - Documentation QA Bot using LangChain: notebooks/code_qa_bot.ipynb - - Multimodal search using CLIP: notebooks/multimodal_search.ipynb - Serverless QA Bot with S3 and Lambda: examples/serverless_lancedb_with_s3_and_lambda.md - Serverless QA Bot with Modal: examples/serverless_qa_bot_with_modal_and_langchain.md - πŸ‘Ύ JavaScript: @@ -276,9 +273,6 @@ nav: - AI Agent: examples/python_examples/aiagent.md - Recommender System: examples/python_examples/recommendersystem.md - Miscellaneous: - - YouTube Transcript Search: notebooks/youtube_transcript_search.ipynb - - Documentation QA Bot using LangChain: notebooks/code_qa_bot.ipynb - - Multimodal search using CLIP: notebooks/multimodal_search.ipynb - Serverless QA Bot with S3 and Lambda: examples/serverless_lancedb_with_s3_and_lambda.md - Serverless QA Bot with Modal: examples/serverless_qa_bot_with_modal_and_langchain.md - πŸ‘Ύ JavaScript: diff --git a/docs/src/examples/examples_python.md b/docs/src/examples/examples_python.md index 6c4a056a..2c7d17d6 100644 --- a/docs/src/examples/examples_python.md +++ b/docs/src/examples/examples_python.md @@ -1,17 +1,22 @@ -# Examples: Python +# Overview : Python Examples -To help you get started, we provide some examples, projects and applications that use the LanceDB Python API. You can always find the latest examples in our [VectorDB Recipes](https://github.com/lancedb/vectordb-recipes) repository. +To help you get started, we provide some examples, projects, and applications that use the LanceDB Python API. These examples are designed to get you right into the code with minimal introduction, enabling you to move from an idea to a proof of concept in minutes. -| Example | Interactive Envs | Scripts | -|-------- | ---------------- | ------ | -| | | | -| [Youtube transcript search bot](https://github.com/lancedb/vectordb-recipes/tree/main/examples/youtube_bot/) | Open In Colab| [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://github.com/lancedb/vectordb-recipes/tree/main/examples/youtube_bot/main.py)| -| [Langchain: Code Docs QA bot](https://github.com/lancedb/vectordb-recipes/tree/main/examples/Code-Documentation-QA-Bot/) | Open In Colab| [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://github.com/lancedb/vectordb-recipes/tree/main/examples/Code-Documentation-QA-Bot/main.py) | -| [AI Agents: Reducing Hallucination](https://github.com/lancedb/vectordb-recipes/tree/main/examples/reducing_hallucinations_ai_agents/) | Open In Colab| [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://github.com/lancedb/vectordb-recipes/tree/main/examples/reducing_hallucinations_ai_agents/main.py)| -| [Multimodal CLIP: DiffusionDB](https://github.com/lancedb/vectordb-recipes/tree/main/examples/multimodal_clip/) | Open In Colab| [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://github.com/lancedb/vectordb-recipes/tree/main/examples/multimodal_clip/main.py) | -| [Multimodal CLIP: Youtube videos](https://github.com/lancedb/vectordb-recipes/tree/main/examples/multimodal_video_search/) | Open In Colab| [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://github.com/lancedb/vectordb-recipes/tree/main/examples/multimodal_video_search/main.py) | -| [Movie Recommender](https://github.com/lancedb/vectordb-recipes/tree/main/examples/movie-recommender/) | Open In Colab | [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://github.com/lancedb/vectordb-recipes/tree/main/examples/movie-recommender/main.py) | -| [Audio Search](https://github.com/lancedb/vectordb-recipes/tree/main/examples/audio_search/) | Open In Colab | [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://github.com/lancedb/vectordb-recipes/tree/main/examples/audio_search/main.py) | -| [Multimodal Image + Text Search](https://github.com/lancedb/vectordb-recipes/tree/main/examples/multimodal_search/) | Open In Colab | [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](https://github.com/lancedb/vectordb-recipes/tree/main/examples/multimodal_search/main.py) | -| [Evaluating Prompts with Prompttools](https://github.com/lancedb/vectordb-recipes/tree/main/examples/prompttools-eval-prompts/) | Open In Colab | | +You can find the latest examples in our [VectorDB Recipes](https://github.com/lancedb/vectordb-recipes) repository. + +**Introduction** + +Explore applied examples available as Colab notebooks or Python scripts to integrate into your applications. You can also checkout our blog posts related to the particular example for deeper understanding. + +| Explore | Description | +|----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Build from Scratch with LanceDB πŸ› οΈπŸš€](python_examples/build_from_scratch.md) | Start building your GenAI applications from the ground up using LanceDB's efficient vector-based document retrieval capabilities! Get started quickly with a solid foundation. | +| [Multimodal Search with LanceDB πŸ€Ήβ€β™‚οΈπŸ”](python_examples/multimodal.md) | Combine text and image queries to find the most relevant results using LanceDB’s multimodal capabilities. Leverage the efficient vector-based similarity search. | +| [RAG: Revolutionize Information Retrieval with LanceDB πŸ”“πŸ§](python_examples/rag.md) | Build RAG (Retrieval-Augmented Generation) with LanceDB for efficient vector-based information retrieval and more accurate responses from AI. | +| [Vector Search: Unlock Efficient Document Retrieval πŸ”“πŸ‘€](python_examples/vector_search.md) | Use LanceDB's vector search capabilities to perform efficient and accurate similarity searches, enabling rapid discovery and retrieval of relevant documents in Large datasets. | +| [Chatbot Application with LanceDB πŸ€–](python_examples/chatbot.md) | Create chatbots that retrieves relevant context for coherent and context-aware replies, enhancing user experience through advanced conversational AI. | +| [Evaluation: Assessing Text Performance with Precision πŸ“ŠπŸ’‘](python_examples/evaluations.md) | Develop evaluation applications that allows you to input reference and candidate texts to measure their performance across various metrics. | +| [AI Agents: Intelligent Collaboration πŸ€–](python_examples/aiagent.md) | Enable AI agents to communicate and collaborate efficiently through dense vector representations, achieving shared goals seamlessly. | +| [Recommender Systems: Personalized Discovery πŸΏπŸ“Ί](python_examples/recommendersystem.md) | Deliver personalized experiences by efficiently storing and querying item embeddings with LanceDB's powerful vector database capabilities. | +| **Miscellaneous Examples🌟** | Find other unique examples and creative solutions using LanceDB, showcasing the flexibility and broad applicability of the platform. | diff --git a/docs/src/notebooks/code_qa_bot.ipynb b/docs/src/notebooks/code_qa_bot.ipynb deleted file mode 100644 index 5ff18a0c..00000000 --- a/docs/src/notebooks/code_qa_bot.ipynb +++ /dev/null @@ -1,378 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "13cb272e", - "metadata": {}, - "source": [ - "# Code documentation Q&A bot example with LangChain\n", - "\n", - "This Q&A bot will allow you to query your own documentation easily using questions. We'll also demonstrate the use of LangChain and LanceDB using the OpenAI API. \n", - "\n", - "In this example we'll use Pandas 2.0 documentation, but, this could be replaced for your own docs as well\n", - "\n", - "\"Open\n", - "\n", - "Scripts - [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](./examples/Code-Documentation-QA-Bot/main.py) [![JavaScript](https://img.shields.io/badge/javascript-%23323330.svg?style=for-the-badge&logo=javascript&logoColor=%23F7DF1E)](./examples/Code-Documentation-QA-Bot/index.js)" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "66638d6c", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install --quiet openai langchain\n", - "!pip install --quiet -U lancedb" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "d1cdcac3", - "metadata": {}, - "source": [ - "First, let's get some setup out of the way. As we're using the OpenAI API, ensure that you've set your key (and organization if needed):" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "id": "58ee1868", - "metadata": {}, - "outputs": [], - "source": [ - "from openai import OpenAI\n", - "import os\n", - "\n", - "# Configuring the environment variable OPENAI_API_KEY\n", - "if \"OPENAI_API_KEY\" not in os.environ:\n", - " os.environ[\"OPENAI_API_KEY\"] = \"sk-...\"\n", - "client = OpenAI()\n", - "assert len(client.models.list().data) > 0" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "34f524d3", - "metadata": {}, - "source": [ - "# Loading in our code documentation, generating embeddings and storing our documents in LanceDB\n", - "\n", - "We're going to use the power of LangChain to help us create our Q&A bot. It comes with several APIs that can make our development much easier as well as a LanceDB integration for vectorstore." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "id": "b55d22f1", - "metadata": {}, - "outputs": [], - "source": [ - "import lancedb\n", - "import re\n", - "import pickle\n", - "import requests\n", - "import zipfile\n", - "from pathlib import Path\n", - "\n", - "from langchain.document_loaders import UnstructuredHTMLLoader\n", - "from langchain.embeddings import OpenAIEmbeddings\n", - "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", - "from langchain.vectorstores import LanceDB\n", - "from langchain.llms import OpenAI\n", - "from langchain.chains import RetrievalQA" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "56cc6d50", - "metadata": {}, - "source": [ - "To make this easier, we've downloaded Pandas documentation and stored the raw HTML files for you to download. We'll download them and then use LangChain's HTML document readers to parse them and store them in LanceDB as a vector store, along with relevant metadata." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7da77e75", - "metadata": {}, - "outputs": [], - "source": [ - "pandas_docs = requests.get(\"https://eto-public.s3.us-west-2.amazonaws.com/datasets/pandas_docs/pandas.documentation.zip\")\n", - "with open('/tmp/pandas.documentation.zip', 'wb') as f:\n", - " f.write(pandas_docs.content)\n", - "\n", - "file = zipfile.ZipFile(\"/tmp/pandas.documentation.zip\")\n", - "file.extractall(path=\"/tmp/pandas_docs\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ae42496c", - "metadata": {}, - "source": [ - "We'll create a simple helper function that can help to extract metadata, so we can use this downstream when we're wanting to query with filters. In this case, we want to keep the lineage of the uri or path for each document that we process:" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "id": "d171d062", - "metadata": {}, - "outputs": [], - "source": [ - "def get_document_title(document):\n", - " m = str(document.metadata[\"source\"])\n", - " title = re.findall(\"pandas.documentation(.*).html\", m)\n", - " if title[0] is not None:\n", - " return(title[0])\n", - " return ''" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "130162ad", - "metadata": {}, - "source": [ - "# Pre-processing and loading the documentation\n", - "\n", - "Next, let's pre-process and load the documentation. To make sure we don't need to do this repeatedly if we were updating code, we're caching it using pickle so we can retrieve it again (this could take a few minutes to run the first time you do it). We'll also add some more metadata to the docs here such as the title and version of the code:" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "id": "33bfe7d8", - "metadata": {}, - "outputs": [], - "source": [ - "docs_path = Path(\"docs.pkl\")\n", - "docs = []\n", - "\n", - "if not docs_path.exists():\n", - " for p in Path(\"/tmp/pandas_docs/pandas.documentation\").rglob(\"*.html\"):\n", - " print(p)\n", - " if p.is_dir():\n", - " continue\n", - " loader = UnstructuredHTMLLoader(p)\n", - " raw_document = loader.load()\n", - " \n", - " m = {}\n", - " m[\"title\"] = get_document_title(raw_document[0])\n", - " m[\"version\"] = \"2.0rc0\"\n", - " raw_document[0].metadata = raw_document[0].metadata | m\n", - " raw_document[0].metadata[\"source\"] = str(raw_document[0].metadata[\"source\"])\n", - " docs = docs + raw_document\n", - "\n", - " with docs_path.open(\"wb\") as fh:\n", - " pickle.dump(docs, fh)\n", - "else:\n", - " with docs_path.open(\"rb\") as fh:\n", - " docs = pickle.load(fh)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "c3852dd3", - "metadata": {}, - "source": [ - "# Generating embeddings from our docs\n", - "\n", - "Now that we have our raw documents loaded, we need to pre-process them to generate embeddings:" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "id": "82230563", - "metadata": {}, - "outputs": [], - "source": [ - "text_splitter = RecursiveCharacterTextSplitter(\n", - " chunk_size=1000,\n", - " chunk_overlap=200,\n", - ")\n", - "documents = text_splitter.split_documents(docs)\n", - "embeddings = OpenAIEmbeddings()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "43e68215", - "metadata": {}, - "source": [ - "# Storing and querying with LanceDB\n", - "\n", - "Let's connect to LanceDB so we can store our documents. We'll create a Table to store them in:" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "id": "74780a58", - "metadata": {}, - "outputs": [], - "source": [ - "db = lancedb.connect('/tmp/lancedb')\n", - "table = db.create_table(\"pandas_docs\", data=[\n", - " {\"vector\": embeddings.embed_query(\"Hello World\"), \"text\": \"Hello World\", \"id\": \"1\"}\n", - "], mode=\"overwrite\")\n", - "docsearch = LanceDB.from_documents(documents, embeddings, connection=table)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "3cb1dc5d", - "metadata": {}, - "source": [ - "Now let's create our RetrievalQA chain using the LanceDB vector store:" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "id": "6a5891ad", - "metadata": {}, - "outputs": [], - "source": [ - "qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type=\"stuff\", retriever=docsearch.as_retriever())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "28d93b85", - "metadata": {}, - "source": [ - "And that's it! We're all set up. The next step is to run some queries, let's try a few:" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "id": "70d88316", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "' The major differences in pandas 2.0 include installing optional dependencies with pip extras, the ability to use any numpy numeric dtype in an Index, and enhancements, notable bug fixes, backwards incompatible API changes, deprecations, and performance improvements.'" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "query = \"What are the major differences in pandas 2.0?\"\n", - "qa.run(query)" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "id": "85a0397c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "' 2.0.0rc0'" - ] - }, - "execution_count": 51, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "query = \"What's the current version of pandas?\"\n", - "qa.run(query)" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "id": "923f86c6", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "' Optional dependencies can be installed with pip install \"pandas[all]\" or \"pandas[performance]\". This will install all recommended performance dependencies such as numexpr, bottleneck and numba.'" - ] - }, - "execution_count": 52, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "query = \"How do I make use of installing optional dependencies?\"\n", - "qa.run(query)" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "id": "02082f83", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\" \\n\\nPandas 2.0 includes a number of API breaking changes, such as increased minimum versions for dependencies, the use of os.linesep for DataFrame.to_csv's line_terminator, and reorganization of the library. See the release notes for a full list of changes.\"" - ] - }, - "execution_count": 53, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "query = \"What are the backwards incompatible API changes in Pandas 2.0?\"\n", - "qa.run(query)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "75cea547", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.10.11" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/src/notebooks/multimodal_search.ipynb b/docs/src/notebooks/multimodal_search.ipynb deleted file mode 100644 index ddbda8d7..00000000 --- a/docs/src/notebooks/multimodal_search.ipynb +++ /dev/null @@ -1,297 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "![example](https://github.com/lancedb/vectordb-recipes/assets/15766192/799f94a1-a01d-4a5b-a627-2a733bbb4227)\n", - "\n", - " \"Open| [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](./examples/multimodal_clip/main.py) |" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip available: \u001B[0m\u001B[31;49m22.3.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m23.1.2\u001B[0m\n", - "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpip install --upgrade pip\u001B[0m\n", - "\n", - "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip available: \u001B[0m\u001B[31;49m22.3.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m23.1.2\u001B[0m\n", - "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpip install --upgrade pip\u001B[0m\n" - ] - } - ], - "source": [ - "!pip install --quiet -U lancedb\n", - "!pip install --quiet gradio transformers torch torchvision" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import io\n", - "\n", - "import PIL\n", - "import duckdb\n", - "import lancedb" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## First run setup: Download data and pre-process" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "### Get dataset\n", - "\n", - "!wget https://eto-public.s3.us-west-2.amazonaws.com/datasets/diffusiondb_lance.tar.gz\n", - "!tar -xvf diffusiondb_lance.tar.gz\n", - "!mv diffusiondb_test rawdata.lance\n" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# remove null prompts\n", - "import lance\n", - "import pyarrow.compute as pc\n", - "\n", - "# download s3://eto-public/datasets/diffusiondb/small_10k.lance to this uri\n", - "data = lance.dataset(\"~/datasets/rawdata.lance\").to_table()\n", - "\n", - "# First data processing and full-text-search index\n", - "db = lancedb.connect(\"~/datasets/demo\")\n", - "tbl = db.create_table(\"diffusiondb\", data.filter(~pc.field(\"prompt\").is_null()))\n", - "tbl = tbl.create_fts_index([\"prompt\"])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create / Open LanceDB Table" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "db = lancedb.connect(\"~/datasets/demo\")\n", - "tbl = db.open_table(\"diffusiondb\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create CLIP embedding function for the text" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "from transformers import CLIPModel, CLIPProcessor, CLIPTokenizerFast\n", - "\n", - "MODEL_ID = \"openai/clip-vit-base-patch32\"\n", - "\n", - "tokenizer = CLIPTokenizerFast.from_pretrained(MODEL_ID)\n", - "model = CLIPModel.from_pretrained(MODEL_ID)\n", - "processor = CLIPProcessor.from_pretrained(MODEL_ID)\n", - "\n", - "def embed_func(query):\n", - " inputs = tokenizer([query], padding=True, return_tensors=\"pt\")\n", - " text_features = model.get_text_features(**inputs)\n", - " return text_features.detach().numpy()[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Search functions for Gradio" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def find_image_vectors(query):\n", - " emb = embed_func(query)\n", - " code = (\n", - " \"import lancedb\\n\"\n", - " \"db = lancedb.connect('~/datasets/demo')\\n\"\n", - " \"tbl = db.open_table('diffusiondb')\\n\\n\"\n", - " f\"embedding = embed_func('{query}')\\n\"\n", - " \"tbl.search(embedding).limit(9).to_pandas()\"\n", - " )\n", - " return (_extract(tbl.search(emb).limit(9).to_pandas()), code)\n", - "\n", - "def find_image_keywords(query):\n", - " code = (\n", - " \"import lancedb\\n\"\n", - " \"db = lancedb.connect('~/datasets/demo')\\n\"\n", - " \"tbl = db.open_table('diffusiondb')\\n\\n\"\n", - " f\"tbl.search('{query}').limit(9).to_pandas()\"\n", - " )\n", - " return (_extract(tbl.search(query).limit(9).to_pandas()), code)\n", - "\n", - "def find_image_sql(query):\n", - " code = (\n", - " \"import lancedb\\n\"\n", - " \"import duckdb\\n\"\n", - " \"db = lancedb.connect('~/datasets/demo')\\n\"\n", - " \"tbl = db.open_table('diffusiondb')\\n\\n\"\n", - " \"diffusiondb = tbl.to_lance()\\n\"\n", - " f\"duckdb.sql('{query}').to_df()\"\n", - " ) \n", - " diffusiondb = tbl.to_lance()\n", - " return (_extract(duckdb.sql(query).to_df()), code)\n", - "\n", - "def _extract(df):\n", - " image_col = \"image\"\n", - " return [(PIL.Image.open(io.BytesIO(row[image_col])), row[\"prompt\"]) for _, row in df.iterrows()]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup Gradio interface" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running on local URL: http://127.0.0.1:7881\n", - "\n", - "To create a public link, set `share=True` in `launch()`.\n" - ] - }, - { - "data": { - "text/html": [ - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import gradio as gr\n", - "\n", - "\n", - "with gr.Blocks() as demo:\n", - " with gr.Row():\n", - " with gr.Tab(\"Embeddings\"):\n", - " vector_query = gr.Textbox(value=\"portraits of a person\", show_label=False)\n", - " b1 = gr.Button(\"Submit\")\n", - " with gr.Tab(\"Keywords\"):\n", - " keyword_query = gr.Textbox(value=\"ninja turtle\", show_label=False)\n", - " b2 = gr.Button(\"Submit\")\n", - " with gr.Tab(\"SQL\"):\n", - " sql_query = gr.Textbox(value=\"SELECT * from diffusiondb WHERE image_nsfw >= 2 LIMIT 9\", show_label=False)\n", - " b3 = gr.Button(\"Submit\")\n", - " with gr.Row():\n", - " code = gr.Code(label=\"Code\", language=\"python\")\n", - " with gr.Row():\n", - " gallery = gr.Gallery(\n", - " label=\"Found images\", show_label=False, elem_id=\"gallery\"\n", - " ).style(columns=[3], rows=[3], object_fit=\"contain\", height=\"auto\") \n", - " \n", - " b1.click(find_image_vectors, inputs=vector_query, outputs=[gallery, code])\n", - " b2.click(find_image_keywords, inputs=keyword_query, outputs=[gallery, code])\n", - " b3.click(find_image_sql, inputs=sql_query, outputs=[gallery, code])\n", - " \n", - "demo.launch()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.11.4 64-bit", - "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.4" - }, - "vscode": { - "interpreter": { - "hash": "b0fa6594d8f4cbf19f97940f81e996739fb7646882a419484c72d19e05852a7e" - } - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/src/notebooks/youtube_transcript_search.ipynb b/docs/src/notebooks/youtube_transcript_search.ipynb deleted file mode 100644 index bd6f2bdc..00000000 --- a/docs/src/notebooks/youtube_transcript_search.ipynb +++ /dev/null @@ -1,702 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "42bf01fb", - "metadata": {}, - "source": [ - "# Youtube Transcript Search QA Bot\n", - "\n", - "This Q&A bot will allow you to search through youtube transcripts using natural language! By going through this notebook, we'll introduce how you can use LanceDB to store and manage your data easily.\n", - "\n", - "\n", - "\"Open\n", - "\n", - "Scripts - [![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)](./examples/youtube_bot/main.py) [![JavaScript](https://img.shields.io/badge/javascript-%23323330.svg?style=for-the-badge&logo=javascript&logoColor=%23F7DF1E)](./examples/youtube_bot/index.js)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "48547ddb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.1.1\u001b[0m\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", - "\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.1.1\u001b[0m\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" - ] - } - ], - "source": [ - "!pip install --quiet openai datasets\n", - "!pip install --quiet -U lancedb" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "22e570f4", - "metadata": {}, - "source": [ - "## Download the data\n", - "\n", - "For this dataset we're using the HuggingFace dataset `jamescalam/youtube-transcriptions`.\n", - "\n", - "From the [website](https://huggingface.co/datasets/jamescalam/youtube-transcriptions):\n", - "\n", - "```\n", - "The YouTube transcriptions dataset contains technical tutorials (currently from James Briggs, Daniel Bourke, and AI Coffee Break) transcribed using OpenAI's Whisper (large). Each row represents roughly a sentence-length chunk of text alongside the video URL and timestamp.\n", - "```\n", - "\n", - "We'll use the training split with 700 videos and 208619 sentences" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "a8987fcb", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Found cached dataset json (/Users/changshe/.cache/huggingface/datasets/jamescalam___json/jamescalam--youtube-transcriptions-08d889f6a5386b9b/0.0.0/0f7e3662623656454fcd2b650f34e886a7db4b9104504885bd462096cc7a9f51)\n" - ] - }, - { - "data": { - "text/plain": [ - "Dataset({\n", - " features: ['title', 'published', 'url', 'video_id', 'channel_id', 'id', 'text', 'start', 'end'],\n", - " num_rows: 208619\n", - "})" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datasets import load_dataset\n", - "\n", - "data = load_dataset('jamescalam/youtube-transcriptions', split='train')\n", - "data" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "5ac2b6a3", - "metadata": {}, - "source": [ - "## Prepare context\n", - "\n", - "Each item in the dataset contains just a short chunk of text. We'll need to merge a bunch of these chunks together on a rolling basis. For this demo, we'll merge 20 rows and step over 4 rows at a time. LanceDB offers chaining support so you can write declarative, readable and parameterized queries. Here we serialize to Pandas as well:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "121a7087", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titlepublishedurlvideo_idchannel_ididtextstartend
177622$5 MILLION AI for FREE2022-08-12 15:18:07https://youtu.be/3EjtHs_lXnk3EjtHs_lXnkUCfzlCWGWYyIQ0aLC5w48gBQ3EjtHs_lXnk-t0.0Imagine an AI where all in the same model you ...0.024.0
\n", - "
" - ], - "text/plain": [ - " title published \\\n", - "177622 $5 MILLION AI for FREE 2022-08-12 15:18:07 \n", - "\n", - " url video_id channel_id \\\n", - "177622 https://youtu.be/3EjtHs_lXnk 3EjtHs_lXnk UCfzlCWGWYyIQ0aLC5w48gBQ \n", - "\n", - " id text \\\n", - "177622 3EjtHs_lXnk-t0.0 Imagine an AI where all in the same model you ... \n", - "\n", - " start end \n", - "177622 0.0 24.0 " - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from lancedb.context import contextualize\n", - "\n", - "df = (contextualize(data.to_pandas())\n", - " .groupby(\"title\").text_col(\"text\")\n", - " .window(20).stride(4)\n", - " .to_pandas())\n", - "df.head(1)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "3044e0b0", - "metadata": {}, - "source": [ - "## Create embedding function\n", - "To create embeddings out of the text, we'll call the OpenAI embeddings API to get embeddings.\n", - "Make sure you have an API key setup and that your account has available credits." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "c8104467", - "metadata": {}, - "outputs": [], - "source": [ - "from openai import OpenAI\n", - "import os\n", - "\n", - "# Configuring the environment variable OPENAI_API_KEY\n", - "if \"OPENAI_API_KEY\" not in os.environ:\n", - " # OR set the key here as a variable\n", - " os.environ[\"OPENAI_API_KEY\"] = \"sk-...\"\n", - "\n", - "client = OpenAI()\n", - "assert len(client.models.list().data) > 0" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "db586267", - "metadata": {}, - "source": [ - "We'll use the ada2 text embeddings model" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "8eefc159", - "metadata": {}, - "outputs": [], - "source": [ - "def embed_func(c):\n", - " rs = client.embeddings.create(input=c, model=\"text-embedding-ada-002\")\n", - " return [\n", - " data.embedding\n", - " for data in rs.data\n", - " ]" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "2106b5bb", - "metadata": {}, - "source": [ - "## Create the LanceDB Table\n", - "OpenAI API often fails or times out. So LanceDB's API provides retry and throttling features behind the scenes to make it easier to call these APIs. In LanceDB the primary abstraction you'll use to work with your data is a Table. A Table is designed to store large numbers of columns and huge quantities of data! For those interested, a LanceDB is columnar-based, and uses Lance, an open data format to store data." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "13f15068", - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c6f1c76d9567421d88911923388d2530", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - " 0%| | 0/49 [00:00\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titlepublishedurlvideo_idchannel_ididtextstartendvector
0$5 MILLION AI for FREE2022-08-12 15:18:07https://youtu.be/3EjtHs_lXnk3EjtHs_lXnkUCfzlCWGWYyIQ0aLC5w48gBQ3EjtHs_lXnk-t0.0Imagine an AI where all in the same model you ...0.024.0[-0.02439424, -0.0007703846, 0.016625028, -0.0...
\n", - "" - ], - "text/plain": [ - " title published url \\\n", - "0 $5 MILLION AI for FREE 2022-08-12 15:18:07 https://youtu.be/3EjtHs_lXnk \n", - "\n", - " video_id channel_id id \\\n", - "0 3EjtHs_lXnk UCfzlCWGWYyIQ0aLC5w48gBQ 3EjtHs_lXnk-t0.0 \n", - "\n", - " text start end \\\n", - "0 Imagine an AI where all in the same model you ... 0.0 24.0 \n", - "\n", - " vector \n", - "0 [-0.02439424, -0.0007703846, 0.016625028, -0.0... " - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import lancedb\n", - "from lancedb.embeddings import with_embeddings\n", - "\n", - "data = with_embeddings(embed_func, df, show_progress=True)\n", - "data.to_pandas().head(1)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "53e4bff1", - "metadata": {}, - "source": [ - "Now we're ready to save the data and create a new LanceDB table" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "92d53abd", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "48935" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "!rm -rf /tmp/lancedb\n", - "\n", - "db = lancedb.connect(\"/tmp/lancedb\")\n", - "tbl = db.create_table(\"chatbot\", data)\n", - "len(tbl)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "8ef34fca", - "metadata": {}, - "source": [ - "The table is backed by a Lance dataset so it's easy to integrate into other tools (e.g., pandas)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "22892cfd", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titlepublishedurlvideo_idchannel_ididtextstartendvector
0$5 MILLION AI for FREE2022-08-12 15:18:07https://youtu.be/3EjtHs_lXnk3EjtHs_lXnkUCfzlCWGWYyIQ0aLC5w48gBQ3EjtHs_lXnk-t0.0Imagine an AI where all in the same model you ...0.024.0[-0.02439424, -0.0007703846, 0.016625028, -0.0...
\n", - "
" - ], - "text/plain": [ - " title published url \\\n", - "0 $5 MILLION AI for FREE 2022-08-12 15:18:07 https://youtu.be/3EjtHs_lXnk \n", - "\n", - " video_id channel_id id \\\n", - "0 3EjtHs_lXnk UCfzlCWGWYyIQ0aLC5w48gBQ 3EjtHs_lXnk-t0.0 \n", - "\n", - " text start end \\\n", - "0 Imagine an AI where all in the same model you ... 0.0 24.0 \n", - "\n", - " vector \n", - "0 [-0.02439424, -0.0007703846, 0.016625028, -0.0... " - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tbl.to_pandas().head(1)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "23afc2f9", - "metadata": {}, - "source": [ - "## Create and answer the prompt\n", - "\n", - "For a given context (bunch of text), we can ask the OpenAI Completion API to answer an arbitrary question using the following prompt:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "06d8b867", - "metadata": {}, - "outputs": [], - "source": [ - "def create_prompt(query, context):\n", - " limit = 3750\n", - "\n", - " prompt_start = (\n", - " \"Answer the question based on the context below.\\n\\n\"+\n", - " \"Context:\\n\"\n", - " )\n", - " prompt_end = (\n", - " f\"\\n\\nQuestion: {query}\\nAnswer:\"\n", - " )\n", - " # append contexts until hitting limit\n", - " for i in range(1, len(context)):\n", - " if len(\"\\n\\n---\\n\\n\".join(context.text[:i])) >= limit:\n", - " prompt = (\n", - " prompt_start +\n", - " \"\\n\\n---\\n\\n\".join(context.text[:i-1]) +\n", - " prompt_end\n", - " )\n", - " break\n", - " elif i == len(context)-1:\n", - " prompt = (\n", - " prompt_start +\n", - " \"\\n\\n---\\n\\n\".join(context.text) +\n", - " prompt_end\n", - " )\n", - " return prompt" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "e09c5142", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'The 12th person on the moon was Harrison Schmitt, and he landed on December 11, 1972.'" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def complete(prompt):\n", - " res = client.completions.create(\n", - " model='text-davinci-003',\n", - " prompt=prompt,\n", - " temperature=0,\n", - " max_tokens=400,\n", - " top_p=1,\n", - " frequency_penalty=0,\n", - " presence_penalty=0,\n", - " stop=None\n", - " )\n", - " return res.choices[0].text\n", - "\n", - "# check that it works\n", - "query = \"who was the 12th person on the moon and when did they land?\"\n", - "complete(query)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "28705959", - "metadata": {}, - "source": [ - "## Let's put it all together now" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "c71f5b31", - "metadata": {}, - "outputs": [], - "source": [ - "query = (\"Which training method should I use for sentence transformers \"\n", - " \"when I only have pairs of related sentences?\")" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "603ba92c", - "metadata": {}, - "outputs": [], - "source": [ - "# Embed the question\n", - "emb = embed_func(query)[0]" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "559a095b", - "metadata": {}, - "source": [ - "\n", - "Again we'll use LanceDB's chaining query API. This time, we'll perform similarity search to find similar embeddings to our query. We can easily tweak the parameters in the query to produce the best result." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "80db5c15", - "metadata": {}, - "outputs": [], - "source": [ - "# Use LanceDB to get top 3 most relevant context\n", - "context = tbl.search(emb).limit(3).to_pandas()" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "8fcef773", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'NLI with multiple negative ranking loss.'" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Get the answer from completion API\n", - "prompt = create_prompt(query, context)\n", - "complete(prompt)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "25714299", - "metadata": {}, - "outputs": [ - { - "data": { - "image/jpeg": "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", - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from IPython.display import YouTubeVideo\n", - "\n", - "top_match = context.iloc[0]\n", - "YouTubeVideo(top_match[\"url\"].split(\"/\")[-1], start=int(top_match[\"start\"]))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "78b7eb11", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.10.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}