From 5421ee8adae4ceb950f32242028e3fef428228be Mon Sep 17 00:00:00 2001
From: Max Hniebergall <137079448+maxhniebergall@users.noreply.github.com>
Date: Wed, 24 Jul 2024 17:42:03 -0400
Subject: [PATCH 1/5] Add new notebook for hugging face integration
---
...ce-integration-millions-of-documents.ipynb | 554 ++++++++++++++++++
1 file changed, 554 insertions(+)
create mode 100644 notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents.ipynb
diff --git a/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents.ipynb b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents.ipynb
new file mode 100644
index 00000000..627fa779
--- /dev/null
+++ b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents.ipynb
@@ -0,0 +1,554 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "7a765629",
+ "metadata": {},
+ "source": [
+ "# Semantic Search using the Inference API with the Hugging Face Inference Endpoints Service\n",
+ "\n",
+ "TODO [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/elastic/elasticsearch-labs/blob/main/notebooks/search/TODO)\n",
+ "\n",
+ "\n",
+ "Learn how to use the [Inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-apis.html) with the Hugging Face Inference Endpoint service for semantic search."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f9101eb9",
+ "metadata": {},
+ "source": [
+ "# 🧰 Requirements\n",
+ "\n",
+ "For this example, you will need:\n",
+ "\n",
+ "- An Elastic deployment:\n",
+ " - We'll be using [Elastic Cloud](https://www.elastic.co/guide/en/cloud/current/ec-getting-started.html) for this example (available with a [free trial](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook))\n",
+ "\n",
+ "- Elasticsearch 8.14 or above.\n",
+ " \n",
+ "- A paid [Hugging Face Inference Endpoint](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) is required to use the Inference API with \n",
+ "the Hugging Face Inference Endpoint service."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4cd69cc0",
+ "metadata": {},
+ "source": [
+ "# Create Elastic Cloud deployment or serverless project\n",
+ "\n",
+ "If you don't have an Elastic Cloud deployment, sign up [here](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook) for a free trial."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f27dffbf",
+ "metadata": {},
+ "source": [
+ "# Install packages and connect with Elasticsearch Client\n",
+ "\n",
+ "To get started, we'll need to connect to our Elastic deployment using the Python client (version 8.12.0 or above).\n",
+ "Because we're using an Elastic Cloud deployment, we'll use the **Cloud ID** to identify our deployment.\n",
+ "\n",
+ "First we need to `pip` install the following packages:\n",
+ "\n",
+ "- `elasticsearch`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "8c4b16bc",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: elasticsearch in /Users/mh/.venv/lib/python3.11/site-packages (8.14.0)\n",
+ "Requirement already satisfied: elastic-transport<9,>=8.13 in /Users/mh/.venv/lib/python3.11/site-packages (from elasticsearch) (8.13.1)\n",
+ "Requirement already satisfied: urllib3<3,>=1.26.2 in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch) (2.1.0)\n",
+ "Requirement already satisfied: certifi in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch) (2023.11.17)\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.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.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",
+ "Requirement already satisfied: datasets in /Users/mh/.venv/lib/python3.11/site-packages (2.20.0)\n",
+ "Requirement already satisfied: filelock in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.13.1)\n",
+ "Requirement already satisfied: numpy>=1.17 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (1.23.5)\n",
+ "Requirement already satisfied: pyarrow>=15.0.0 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (17.0.0)\n",
+ "Requirement already satisfied: pyarrow-hotfix in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.6)\n",
+ "Requirement already satisfied: dill<0.3.9,>=0.3.0 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.3.8)\n",
+ "Requirement already satisfied: pandas in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (1.5.3)\n",
+ "Requirement already satisfied: requests>=2.32.2 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (2.32.3)\n",
+ "Requirement already satisfied: tqdm>=4.66.3 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (4.66.4)\n",
+ "Requirement already satisfied: xxhash in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.4.1)\n",
+ "Requirement already satisfied: multiprocess in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.70.16)\n",
+ "Requirement already satisfied: fsspec<=2024.5.0,>=2023.1.0 in /Users/mh/.venv/lib/python3.11/site-packages (from fsspec[http]<=2024.5.0,>=2023.1.0->datasets) (2024.5.0)\n",
+ "Requirement already satisfied: aiohttp in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.9.5)\n",
+ "Requirement already satisfied: huggingface-hub>=0.21.2 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.24.2)\n",
+ "Requirement already satisfied: packaging in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (23.2)\n",
+ "Requirement already satisfied: pyyaml>=5.1 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (6.0.1)\n",
+ "Requirement already satisfied: aiosignal>=1.1.2 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.3.1)\n",
+ "Requirement already satisfied: attrs>=17.3.0 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (23.2.0)\n",
+ "Requirement already satisfied: frozenlist>=1.1.1 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.4.1)\n",
+ "Requirement already satisfied: multidict<7.0,>=4.5 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (6.0.5)\n",
+ "Requirement already satisfied: yarl<2.0,>=1.0 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.9.4)\n",
+ "Requirement already satisfied: typing-extensions>=3.7.4.3 in /Users/mh/.venv/lib/python3.11/site-packages (from huggingface-hub>=0.21.2->datasets) (4.12.2)\n",
+ "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (3.3.2)\n",
+ "Requirement already satisfied: idna<4,>=2.5 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (3.6)\n",
+ "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (2.1.0)\n",
+ "Requirement already satisfied: certifi>=2017.4.17 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (2023.11.17)\n",
+ "Requirement already satisfied: python-dateutil>=2.8.1 in /Users/mh/.venv/lib/python3.11/site-packages (from pandas->datasets) (2.8.2)\n",
+ "Requirement already satisfied: pytz>=2020.1 in /Users/mh/.venv/lib/python3.11/site-packages (from pandas->datasets) (2023.3.post1)\n",
+ "Requirement already satisfied: six>=1.5 in /Users/mh/.venv/lib/python3.11/site-packages (from python-dateutil>=2.8.1->pandas->datasets) (1.16.0)\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.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.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",
+ "Note: you may need to restart the kernel to use updated packages.\n"
+ ]
+ }
+ ],
+ "source": [
+ "!pip install elasticsearch\n",
+ "%pip install datasets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "41ef96b3",
+ "metadata": {},
+ "source": [
+ "Next, we need to import the modules we need. 🔐 NOTE: getpass enables us to securely prompt the user for credentials without echoing them to the terminal, or storing it in memory."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "690ff9af",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/Users/mh/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
+ " from .autonotebook import tqdm as notebook_tqdm\n"
+ ]
+ }
+ ],
+ "source": [
+ "from elasticsearch import Elasticsearch, helpers, exceptions\n",
+ "from urllib.request import urlopen\n",
+ "from getpass import getpass\n",
+ "import json\n",
+ "import time\n",
+ "from timeit import default_timer as timer\n",
+ "import datasets\n",
+ "import csv"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "23fa2b6c",
+ "metadata": {},
+ "source": [
+ "Now we can instantiate the Python Elasticsearch client.\n",
+ "\n",
+ "First we prompt the user for their password and Cloud ID.\n",
+ "Then we create a `client` object that instantiates an instance of the `Elasticsearch` class."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "195cc597",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id\n",
+ "ELASTIC_CLOUD_ID = getpass(\"Elastic Cloud ID: \")\n",
+ "\n",
+ "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key\n",
+ "ELASTIC_API_KEY = getpass(\"Elastic Api Key: \")\n",
+ "\n",
+ "# Create the client instance\n",
+ "client = Elasticsearch(\n",
+ " # For local development\n",
+ " # hosts=[\"http://localhost:9200\"]\n",
+ " cloud_id=ELASTIC_CLOUD_ID,\n",
+ " api_key=ELASTIC_API_KEY,\n",
+ " request_timeout=120, \n",
+ " max_retries=10,\n",
+ " retry_on_timeout=True\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b1115ffb",
+ "metadata": {},
+ "source": [
+ "### Test the Client\n",
+ "Before you continue, confirm that the client has connected with this test."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "cc0de5ea",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'name': 'instance-0000000001', 'cluster_name': 'dd945ac31a6c456686ee5117f1ec38a2', 'cluster_uuid': 'puJnzmQtThKzbfH1e3_bVw', 'version': {'number': '8.14.3', 'build_flavor': 'default', 'build_type': 'docker', 'build_hash': 'd55f984299e0e88dee72ebd8255f7ff130859ad0', 'build_date': '2024-07-07T22:04:49.882652950Z', 'build_snapshot': False, 'lucene_version': '9.10.0', 'minimum_wire_compatibility_version': '7.17.0', 'minimum_index_compatibility_version': '7.0.0'}, 'tagline': 'You Know, for Search'}\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(client.info())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "659c5890",
+ "metadata": {},
+ "source": [
+ "Refer to [the documentation](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect to a self-managed deployment.\n",
+ "\n",
+ "Read [this page](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect using API keys."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "840d92f0",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Create the inference endpoint object\n",
+ "\n",
+ "Let's create the inference endpoint by using the [Create inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html).\n",
+ "\n",
+ "You'll need an Hugging Face API key (access token) for this that you can find in your Hugging Face account under the [Access Tokens](https://huggingface.co/settings/tokens).\n",
+ "\n",
+ "You will also need to have created a [Hugging Face Inference Endpoint service instance](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) and noted the `url` of your instance. For this notebook, we deployed the `multilingual-e5-small` model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0d007737",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "API_KEY = getpass(\"Huggingface API key: \")\n",
+ "try:\n",
+ " client.inference.delete_model(inference_id=\"my_hf_endpoint_object\")\n",
+ "except Exception as e:\n",
+ " print(e)\n",
+ "client.inference.put_model(\n",
+ " inference_id='my_hf_endpoint_object',\n",
+ " body={\n",
+ " \"service\": \"hugging_face\",\n",
+ " \"service_settings\": {\"api_key\": API_KEY, \n",
+ " \"url\": \"\",\n",
+ " \"similarity\": \"dot_product\"\n",
+ " },\n",
+ " },\n",
+ " task_type=\"text_embedding\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "67f4201d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.inference.inference(\n",
+ " inference_id='my_hf_endpoint_object',\n",
+ " input=\"this is the raw text of my document!\"\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1f2e48b7",
+ "metadata": {},
+ "source": [
+ "**IMPORTANT:** If you use Elasticsearch 8.12, you must change `inference_id` in the snippet above to `model_id`! "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0346151d",
+ "metadata": {},
+ "source": [
+ "#"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1024d070",
+ "metadata": {},
+ "source": [
+ "## Create an ingest pipeline with an inference processor\n",
+ "\n",
+ "Create an ingest pipeline with an inference processor by using the [`put_pipeline`](https://www.elastic.co/guide/en/elasticsearch/reference/master/put-pipeline-api.html) method. Reference the `inference_id` created above as `model_id` to infer on the data that is being ingested by the pipeline."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6ace9e2e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.ingest.put_pipeline(\n",
+ " id=\"hf_pipeline\",\n",
+ " processors=[\n",
+ " {\n",
+ " \"inference\": {\n",
+ " \"model_id\": \"my_hf_endpoint_object\",\n",
+ " \"input_output\": {\n",
+ " \"input_field\": \"text\",\n",
+ " \"output_field\": \"text_embedding\",\n",
+ " },\n",
+ " }\n",
+ " }\n",
+ " ],\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "76d07567",
+ "metadata": {},
+ "source": [
+ "Let's note a few important parameters from that API call:\n",
+ "\n",
+ "- `inference`: A processor that performs inference using a machine learning model.\n",
+ "- `model_id`: Specifies the ID of the inference endpoint to be used. In this example, the inference ID is set to `my_hf_endpoint_object`. Use the inference ID you defined when created the inference task.\n",
+ "- `input_output`: Specifies input and output fields.\n",
+ "- `input_field`: Field name from which the `dense_vector` representation is created.\n",
+ "- `output_field`: Field name which contains inference results. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "28e12d7a",
+ "metadata": {},
+ "source": [
+ "## Create index\n",
+ "\n",
+ "The mapping of the destination index - the index that contains the embeddings that the model will create based on your input text - must be created. The destination index must have a field with the [dense_vector](https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html) field type to index the output of the model we deployed in Hugging Face (`multilingual-e5-small`).\n",
+ "\n",
+ "Let's create an index named `hf-endpoint-index` with the mappings we need."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6ddcbca3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.indices.create(\n",
+ " index=\"hf-endpoint-index\",\n",
+ " settings = {\n",
+ " \"index\": {\n",
+ " \"number_of_replicas\": \"1\",\n",
+ " \"number_of_shards\": \"1\",\n",
+ " \"default_pipeline\": \"hf_pipeline\",\n",
+ " }\n",
+ " },\n",
+ " mappings = {\n",
+ " \"properties\": {\n",
+ " \"text\": {\"type\": \"text\"},\n",
+ " \"text_embedding\": {\n",
+ " \"type\": \"dense_vector\",\n",
+ " \"dims\": 384,\n",
+ " \"similarity\": \"dot_product\",\n",
+ " },\n",
+ " },\n",
+ " \"_source\": {\"excludes\": [\"passage_embedding.predicted_value\"]},\n",
+ " }\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "12aa3452",
+ "metadata": {},
+ "source": [
+ "## If you are using Elasticsearch serverless or v8.15+ then you will have access to the new `semantic_text` field\n",
+ "`semantic_text` has significantly faster ingest times and is recommended.\n",
+ "\n",
+ "https://github.com/elastic/elasticsearch/blob/main/docs/reference/mapping/types/semantic-text.asciidoc"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5eeb3755",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.indices.create(\n",
+ " index=\"hf-semantic-text-index\",\n",
+ " mappings={\n",
+ " \"properties\": {\n",
+ " \"infer_field\": {\n",
+ " \"type\": \"semantic_text\",\n",
+ " \"inference_id\": \"my_hf_endpoint_object\"\n",
+ " }\n",
+ " }\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "07c187a9",
+ "metadata": {},
+ "source": [
+ "## Insert Documents\n",
+ "\n",
+ "In this example, we want to show the power of using GPUs in Hugging Face's Inference Endpoint service by indexing millions of multilingual documents from the miracl corpus. The speed at which these documents ingest will depend on whether you use a semantic text field (faster) or an ingest pipeline (slower) and will also depend on how much hardware your rent for your Hugging Face inference endpoint. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d68737cb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "langs = ['ar', 'bn', 'en', 'es', 'fa', 'fi', 'fr', 'hi', 'id', 'ja', 'ko', 'ru', 'sw', 'te', 'th', 'zh']\n",
+ "\n",
+ "\n",
+ "all_langs_datasets = [iter(datasets.load_dataset('miracl/miracl-corpus', lang)['train']) for lang in langs]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7ccf9835",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "MAX_BULK_SIZE = 1000\n",
+ "MAX_BULK_UPLOADS = 1000\n",
+ "\n",
+ "sentinel = object()\n",
+ "for j in range():\n",
+ " start = timer()\n",
+ " try:\n",
+ " time.sleep(1)\n",
+ " documents = []\n",
+ " while len(documents) < MAX_BULK_SIZE - len(all_langs_datasets):\n",
+ " for ds in all_langs_datasets:\n",
+ " text = next(ds, sentinel)\n",
+ " if text is not sentinel:\n",
+ " documents.append(\n",
+ " {\n",
+ " \"_index\": \"hf-endpoint-index\",\n",
+ " \"_source\": {\"text\": text['text']},\n",
+ " }\n",
+ " )\n",
+ " # if you are using semantic text, use this append instead:\n",
+ " # documents.append(\n",
+ " # {\n",
+ " # \"_index\": \"hf-semantic-text-index\",\n",
+ " # \"_source\": {\"infer_field\": text['text']},\n",
+ " # }\n",
+ " # )\n",
+ "\n",
+ " try:\n",
+ " response = helpers.bulk(client, documents, raise_on_error=False, timeout=\"60s\")\n",
+ "\n",
+ " print(\"Doc set:\", j, end=\"; \")\n",
+ " end=timer()\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(\"exception:\", str(e))\n",
+ " time.sleep(30)\n",
+ " except Exception as e:\n",
+ " print(j, e)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cf0f6df7",
+ "metadata": {},
+ "source": [
+ "## Semantic search\n",
+ "\n",
+ "After the dataset has been enriched with the embeddings, you can query the data using [semantic search](https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-semantic-search). Pass a `query_vector_builder` to the k-nearest neighbor (kNN) vector search API, and provide the query text and the model you have used to create the embeddings."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d9b21b71",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "response = client.search(\n",
+ " index=\"hf-endpoint-index\",\n",
+ " size=3,\n",
+ " knn={\n",
+ " \"field\": \"text_embedding\",\n",
+ " \"query_vector_builder\": {\n",
+ " \"text_embedding\": {\n",
+ " \"model_id\": \"my_hf_endpoint_object\",\n",
+ " \"model_text\": \"Fighting movie\",\n",
+ " }\n",
+ " },\n",
+ " \"k\": 10,\n",
+ " \"num_candidates\": 100,\n",
+ " },\n",
+ ")\n",
+ "\n",
+ "for hit in response[\"hits\"][\"hits\"]:\n",
+ " doc_id = hit[\"_id\"]\n",
+ " score = hit[\"_score\"]\n",
+ " text = hit[\"_source\"][\"text\"]\n",
+ " print(f\"Score: {score}\\nText: {text}\\n\")\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7e4055ba",
+ "metadata": {},
+ "source": [
+ "**NOTE:** The value of `model_id` in the `query_vector_builder` must match the value of `inference_id` you created in the [first step](#create-the-inference-endpoint)."
+ ]
+ }
+ ],
+ "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.11.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
From ef9facadb6720aca7b6043b8e2c6c055cfe7c923 Mon Sep 17 00:00:00 2001
From: Max Hniebergall
Date: Mon, 12 Aug 2024 13:54:22 -0400
Subject: [PATCH 2/5] Replace notebook with updated version using cohere rerank
retriever
---
...s-of-documents-with-cohere-reranking.ipynb | 929 ++++++++++++++++++
...ce-integration-millions-of-documents.ipynb | 554 -----------
2 files changed, 929 insertions(+), 554 deletions(-)
create mode 100644 notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb
delete mode 100644 notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents.ipynb
diff --git a/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb
new file mode 100644
index 00000000..1109a69c
--- /dev/null
+++ b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb
@@ -0,0 +1,929 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "7a765629",
+ "metadata": {},
+ "source": [
+ "# Semantic Search using the Inference API with the Hugging Face Inference Endpoints Service\n",
+ "\n",
+ "TODO [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/elastic/elasticsearch-labs/blob/main/notebooks/search/TODO)\n",
+ "\n",
+ "\n",
+ "Learn how to use the [Inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-apis.html) with the Hugging Face Inference Endpoint service for semantic search."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f9101eb9",
+ "metadata": {},
+ "source": [
+ "# 🧰 Requirements\n",
+ "\n",
+ "For this example, you will need:\n",
+ "\n",
+ "- An Elastic deployment:\n",
+ " - We'll be using [Elastic serverless](https://www.elastic.co/docs/current/serverless) for this example (available with a [free trial](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook))\n",
+ "\n",
+ "- Elasticsearch 8.14 or above.\n",
+ " \n",
+ "- A paid [Hugging Face Inference Endpoint](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) is required to use the Inference API with \n",
+ "the Hugging Face Inference Endpoint service."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4cd69cc0",
+ "metadata": {},
+ "source": [
+ "# Create Elastic Cloud deployment or serverless project\n",
+ "\n",
+ "If you don't have an Elastic Cloud deployment, sign up [here](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook) for a free trial."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f27dffbf",
+ "metadata": {},
+ "source": [
+ "# Install packages and connect with Elasticsearch Client\n",
+ "\n",
+ "To get started, we'll need to connect to our Elastic deployment using the Python client (version 8.12.0 or above).\n",
+ "Because we're using an Elastic Cloud deployment, we'll use the **Cloud ID** to identify our deployment.\n",
+ "\n",
+ "First we need to `pip` install the following packages:\n",
+ "\n",
+ "- `elasticsearch`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "8c4b16bc",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: elasticsearch in /Users/mh/.venv/lib/python3.11/site-packages (8.14.0)\n",
+ "Requirement already satisfied: elastic-transport<9,>=8.13 in /Users/mh/.venv/lib/python3.11/site-packages (from elasticsearch) (8.13.1)\n",
+ "Requirement already satisfied: urllib3<3,>=1.26.2 in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch) (2.1.0)\n",
+ "Requirement already satisfied: certifi in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch) (2023.11.17)\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.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.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",
+ "Requirement already satisfied: datasets in /Users/mh/.venv/lib/python3.11/site-packages (2.20.0)\n",
+ "Requirement already satisfied: filelock in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.13.1)\n",
+ "Requirement already satisfied: numpy>=1.17 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (1.23.5)\n",
+ "Requirement already satisfied: pyarrow>=15.0.0 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (17.0.0)\n",
+ "Requirement already satisfied: pyarrow-hotfix in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.6)\n",
+ "Requirement already satisfied: dill<0.3.9,>=0.3.0 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.3.8)\n",
+ "Requirement already satisfied: pandas in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (1.5.3)\n",
+ "Requirement already satisfied: requests>=2.32.2 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (2.32.3)\n",
+ "Requirement already satisfied: tqdm>=4.66.3 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (4.66.4)\n",
+ "Requirement already satisfied: xxhash in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.4.1)\n",
+ "Requirement already satisfied: multiprocess in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.70.16)\n",
+ "Requirement already satisfied: fsspec<=2024.5.0,>=2023.1.0 in /Users/mh/.venv/lib/python3.11/site-packages (from fsspec[http]<=2024.5.0,>=2023.1.0->datasets) (2024.5.0)\n",
+ "Requirement already satisfied: aiohttp in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.9.5)\n",
+ "Requirement already satisfied: huggingface-hub>=0.21.2 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.24.2)\n",
+ "Requirement already satisfied: packaging in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (23.2)\n",
+ "Requirement already satisfied: pyyaml>=5.1 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (6.0.1)\n",
+ "Requirement already satisfied: aiosignal>=1.1.2 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.3.1)\n",
+ "Requirement already satisfied: attrs>=17.3.0 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (23.2.0)\n",
+ "Requirement already satisfied: frozenlist>=1.1.1 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.4.1)\n",
+ "Requirement already satisfied: multidict<7.0,>=4.5 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (6.0.5)\n",
+ "Requirement already satisfied: yarl<2.0,>=1.0 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.9.4)\n",
+ "Requirement already satisfied: typing-extensions>=3.7.4.3 in /Users/mh/.venv/lib/python3.11/site-packages (from huggingface-hub>=0.21.2->datasets) (4.12.2)\n",
+ "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (3.3.2)\n",
+ "Requirement already satisfied: idna<4,>=2.5 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (3.6)\n",
+ "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (2.1.0)\n",
+ "Requirement already satisfied: certifi>=2017.4.17 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (2023.11.17)\n",
+ "Requirement already satisfied: python-dateutil>=2.8.1 in /Users/mh/.venv/lib/python3.11/site-packages (from pandas->datasets) (2.8.2)\n",
+ "Requirement already satisfied: pytz>=2020.1 in /Users/mh/.venv/lib/python3.11/site-packages (from pandas->datasets) (2023.3.post1)\n",
+ "Requirement already satisfied: six>=1.5 in /Users/mh/.venv/lib/python3.11/site-packages (from python-dateutil>=2.8.1->pandas->datasets) (1.16.0)\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.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.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",
+ "Note: you may need to restart the kernel to use updated packages.\n"
+ ]
+ }
+ ],
+ "source": [
+ "!pip install elasticsearch\n",
+ "%pip install datasets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "41ef96b3",
+ "metadata": {},
+ "source": [
+ "Next, we need to import the modules we need. 🔐 NOTE: getpass enables us to securely prompt the user for credentials without echoing them to the terminal, or storing it in memory."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "690ff9af",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/Users/mh/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
+ " from .autonotebook import tqdm as notebook_tqdm\n"
+ ]
+ }
+ ],
+ "source": [
+ "from elasticsearch import Elasticsearch, helpers\n",
+ "from getpass import getpass\n",
+ "import datasets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "23fa2b6c",
+ "metadata": {},
+ "source": [
+ "Now we can instantiate the Python Elasticsearch client.\n",
+ "\n",
+ "First we prompt the user for their password and Cloud ID.\n",
+ "Then we create a `client` object that instantiates an instance of the `Elasticsearch` class."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "195cc597",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id\n",
+ "ELASTIC_CLOUD_ID = getpass(\"Elastic Cloud ID: \")\n",
+ "\n",
+ "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key\n",
+ "ELASTIC_API_KEY = getpass(\"Elastic Api Key: \")\n",
+ "\n",
+ "# Create the client instance\n",
+ "client = Elasticsearch(\n",
+ " # For local development\n",
+ " # hosts=[\"http://localhost:9200\"]\n",
+ " cloud_id=ELASTIC_CLOUD_ID,\n",
+ " api_key=ELASTIC_API_KEY,\n",
+ " request_timeout=120, \n",
+ " max_retries=10,\n",
+ " retry_on_timeout=True\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b1115ffb",
+ "metadata": {},
+ "source": [
+ "### Test the Client\n",
+ "Before you continue, confirm that the client has connected with this test."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "id": "cc0de5ea",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'name': 'serverless', 'cluster_name': 'eccdee94244a41ad8c9e7788576116e0', 'cluster_uuid': 'nNzsd294QbWzcXy9m0kkcQ', 'version': {'number': '8.11.0', 'build_flavor': 'serverless', 'build_type': 'docker', 'build_hash': '00000000', 'build_date': '2023-10-31', 'build_snapshot': False, 'lucene_version': '9.7.0', 'minimum_wire_compatibility_version': '8.11.0', 'minimum_index_compatibility_version': '8.11.0'}, 'tagline': 'You Know, for Search'}\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(client.info())\n",
+ "\n",
+ "# define this now so we can use it later\n",
+ "def pretty_search_response(response):\n",
+ " if len(response[\"hits\"][\"hits\"]) == 0:\n",
+ " print(\"Your search returned no results.\")\n",
+ " else:\n",
+ " for hit in response[\"hits\"][\"hits\"]:\n",
+ " id = hit[\"_id\"]\n",
+ " score = hit[\"_score\"]\n",
+ " text = hit[\"_source\"][\"text_field\"]\n",
+ "\n",
+ " pretty_output = f\"\\nID: {id}\\nScore: {score}\\nText: {text}\"\n",
+ "\n",
+ " print(pretty_output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "659c5890",
+ "metadata": {},
+ "source": [
+ "Refer to [the documentation](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect to a self-managed deployment.\n",
+ "\n",
+ "Read [this page](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect using API keys."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "840d92f0",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Create the inference endpoint object\n",
+ "\n",
+ "Let's create the inference endpoint by using the [Create inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html).\n",
+ "\n",
+ "You'll need an Hugging Face API key (access token) for this that you can find in your Hugging Face account under the [Access Tokens](https://huggingface.co/settings/tokens).\n",
+ "\n",
+ "You will also need to have created a [Hugging Face Inference Endpoint service instance](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) and noted the `url` of your instance. For this notebook, we deployed the `multilingual-e5-small` model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "0d007737",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ObjectApiResponse({'model_id': 'my_hf_endpoint_object', 'inference_id': 'my_hf_endpoint_object', 'task_type': 'text_embedding', 'service': 'hugging_face', 'service_settings': {'url': 'https://yb0j0ol2xzvro0oc.us-east-1.aws.endpoints.huggingface.cloud', 'similarity': 'dot_product', 'dimensions': 384, 'rate_limit': {'requests_per_minute': 3000}}, 'task_settings': {}})"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "API_KEY = getpass(\"Huggingface API key: \")\n",
+ "client.inference.put_model(\n",
+ " inference_id='my_hf_endpoint_object',\n",
+ " body={\n",
+ " \"service\": \"hugging_face\",\n",
+ " \"service_settings\": {\"api_key\": API_KEY, \n",
+ " \"url\": \"https://yb0j0ol2xzvro0oc.us-east-1.aws.endpoints.huggingface.cloud\",\n",
+ " \"similarity\": \"dot_product\"\n",
+ " },\n",
+ " },\n",
+ " task_type=\"text_embedding\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "67f4201d",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ObjectApiResponse({'text_embedding': [{'embedding': [0.026027203, -0.011120652, -0.048804738, -0.108695105, 0.06134937, -0.003066093, 0.053232085, 0.103629395, 0.046043355, 0.0055427994, 0.036174323, 0.022110537, 0.084891565, -0.008215214, -0.017915571, 0.041923355, 0.048264034, -0.0404355, -0.02609504, -0.023076748, 0.0077286777, 0.023034474, 0.010379155, 0.06257496, 0.025658935, 0.040398516, -0.059809092, 0.032451782, 0.020798752, -0.053219322, -0.0447653, -0.033474423, 0.085040554, -0.051343303, 0.081006914, 0.026895791, -0.031822708, -0.06217641, 0.069435075, -0.055062667, -0.014967285, -0.0040517864, 0.03874908, 0.07854211, 0.017526977, 0.040629108, -0.023190023, 0.056913305, -0.06422566, -0.009403182, -0.06666503, 0.035270344, 0.004515737, 0.07347306, 0.011125566, -0.07184689, -0.08095445, -0.04214626, -0.108447045, -0.019494658, 0.06303337, 0.019757038, -0.014584281, 0.060923614, 0.06465893, 0.108431116, 0.04072316, 0.03705652, -0.06975359, -0.050562095, -0.058487326, 0.05989619, 0.008454561, -0.02706363, -0.017974045, 0.030698266, 0.046484154, -0.06212431, 0.009513307, -0.056369964, -0.052940592, -0.05834985, -0.02096531, 0.03910419, -0.054484386, 0.06231919, 0.044607673, -0.064030685, 0.067746714, -0.0291515, 0.06992093, 0.06300958, -0.07530936, -0.06167211, -0.0681666, -0.042375665, -0.05200085, 0.058336657, 0.039630838, -0.03444309, 0.030615594, -0.042388055, 0.03127304, -0.059075136, -0.05925558, 0.019864058, 0.0311022, -0.11285156, 0.02264027, -0.0676216, 0.011842404, -0.0157365, 0.06580391, 0.023665493, -0.05072435, -0.039492164, -0.06390325, -0.067074455, 0.032680944, -0.05243909, 0.06721114, -0.005195616, -0.0458316, -0.046202496, -0.07942237, -0.011754681, 0.026515028, 0.04761297, 0.08130492, 0.0118014645, 0.025956452, 0.039976373, 0.050196614, 0.052609406, 0.063223615, 0.06121741, -0.028745022, 0.0008677591, 0.038760003, -0.021240402, -0.073974326, 0.0548761, -0.047403768, 0.025582938, 0.0585596, 0.056284837, 0.08381001, -0.02149303, 0.09447917, -0.04940235, 0.018470071, -0.044996567, 0.08062048, 0.05162519, 0.053831138, -0.052980945, -0.08226773, -0.068137355, 0.028439872, 0.049932946, -0.07633764, -0.08649836, -0.07108301, 0.017650153, -0.065348, -0.038191773, 0.040068675, 0.05870959, -0.04707911, -0.04340612, -0.044621766, 0.030800574, -0.042227603, 0.0604754, 0.010891958, 0.057460006, -0.046362966, 0.046009373, 0.07293652, 0.09398854, -0.017035728, -0.010618687, -0.09326647, -0.03877647, -0.026517635, -0.047411792, -0.073266074, 0.033911563, 0.0642687, -0.02208107, 0.0040624263, -0.003194478, -0.082016475, -0.088730805, -0.084694624, -0.03364641, -0.05026475, 0.051665384, 0.058177516, 0.02759865, -0.034461632, 0.0027396793, 0.013807217, 0.040009033, 0.06346369, 0.05832441, -0.07451158, 0.028601868, -0.022494016, 0.04229324, 0.027883757, -0.0673137, -0.07119014, 0.047188714, -0.033077974, -0.028302893, -0.028704679, 0.043902606, -0.05147592, 0.045782477, 0.08077521, -0.01782404, 0.0242885, -0.0711172, -0.023565968, 0.041291755, 0.084907316, -0.101972945, -0.038989857, 0.025122978, -0.014144972, -0.010975231, -0.0357049, -0.09243826, -0.023552464, -0.08525497, -0.018912667, 0.049455214, 0.06532829, -0.031223357, -0.013451132, -0.00037671064, 0.04600707, -0.057603396, 0.08035837, -0.026429964, -0.0962299, 0.022606302, -0.0116137, 0.062264528, 0.033446472, -0.06123555, -0.09909991, -0.07459225, -0.018707436, 0.028753517, 0.06808565, 0.023965191, -0.04717076, 0.026551146, 0.019655682, -0.009233348, 0.10465723, 0.046420176, 0.03295103, 0.053024694, -0.03854051, -0.0058735567, -0.061238136, -0.048678573, -0.05362055, 0.048028357, 0.003013557, -0.06505121, -0.020536456, -0.020093206, 0.014102229, 0.10254222, -0.027084326, -0.061477777, 0.03478813, -0.00029115603, 0.053552967, 0.056773122, 0.048566766, 0.027371235, -0.015398839, 0.0511229, -0.03932426, -0.043879736, -0.03872225, -0.08171432, 0.01703992, -0.04535995, 0.03194781, 0.011413799, 0.036786903, 0.021306055, -0.06722324, 0.034231987, -0.027529748, -0.059552487, 0.050244797, 0.08905617, -0.071323626, 0.05047076, 0.003429174, 0.034673557, 0.009984501, 0.056842286, 0.0683513, 0.023990847, -0.04053898, -0.022724004, 0.026175855, 0.027319307, -0.055451974, -0.053907238, -0.05359307, -0.035025068, -0.03776361, -0.02973751, -0.037610233, -0.051089168, 0.04428633, 0.06276192, -0.03754498, -0.060270913, 0.043127347, 0.016669549, 0.024885416, -0.027190097, -0.011614101, 0.077848606, -0.007924398, -0.061833344, -0.015071012, 0.023127502, -0.07634841, -0.015780756, 0.031652045, 0.0031123296, -0.032643825, 0.05640234, -0.02685534, -0.04942714, 0.048498664, 0.00043902535, -0.043975227, 0.017389799, 0.07734344, -0.090009265, 0.019997133, 0.10055134, -0.05671741, 0.048755262, -0.02514076, -0.011394784, 0.049053214, 0.04264309, -0.06451125, -0.029034287, 0.07762039, 0.06809162, 0.059983794, 0.035379365, -0.007960272, 0.019705113, -0.02518122, -0.05767321, 0.038523413, 0.081652805, -0.032829504, -0.0023197657, -0.018218426, -0.0885769, -0.094963886, 0.057851806, -0.041729856, -0.045802936, 0.0570079, 0.047811687, 0.017810043, 0.09373594]}]})"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "client.inference.inference(\n",
+ " inference_id='my_hf_endpoint_object',\n",
+ " input=\"this is the raw text of my document!\"\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1f2e48b7",
+ "metadata": {},
+ "source": [
+ "**IMPORTANT:** If you use Elasticsearch 8.12, you must change `inference_id` in the snippet above to `model_id`! "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0346151d",
+ "metadata": {},
+ "source": [
+ "#"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1024d070",
+ "metadata": {},
+ "source": [
+ "## Create an ingest pipeline with an inference processor\n",
+ "\n",
+ "Create an ingest pipeline with an inference processor by using the [`put_pipeline`](https://www.elastic.co/guide/en/elasticsearch/reference/master/put-pipeline-api.html) method. Reference the `inference_id` created above as `model_id` to infer on the data that is being ingested by the pipeline."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "6ace9e2e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ObjectApiResponse({'acknowledged': True})"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "client.ingest.put_pipeline(\n",
+ " id=\"hf_pipeline\",\n",
+ " processors=[\n",
+ " {\n",
+ " \"inference\": {\n",
+ " \"model_id\": \"my_hf_endpoint_object\",\n",
+ " \"input_output\": {\n",
+ " \"input_field\": \"text_field\",\n",
+ " \"output_field\": \"text_embedding\",\n",
+ " },\n",
+ " }\n",
+ " }\n",
+ " ],\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "76d07567",
+ "metadata": {},
+ "source": [
+ "Let's note a few important parameters from that API call:\n",
+ "\n",
+ "- `inference`: A processor that performs inference using a machine learning model.\n",
+ "- `model_id`: Specifies the ID of the inference endpoint to be used. In this example, the inference ID is set to `my_hf_endpoint_object`. Use the inference ID you defined when created the inference task.\n",
+ "- `input_output`: Specifies input and output fields.\n",
+ "- `input_field`: Field name from which the `dense_vector` representation is created.\n",
+ "- `output_field`: Field name which contains inference results. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "28e12d7a",
+ "metadata": {},
+ "source": [
+ "## Create index\n",
+ "\n",
+ "The mapping of the destination index - the index that contains the embeddings that the model will create based on your input text - must be created. The destination index must have a field with the [dense_vector](https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html) field type to index the output of the model we deployed in Hugging Face (`multilingual-e5-small`).\n",
+ "\n",
+ "Let's create an index named `hf-endpoint-index` with the mappings we need."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "6ddcbca3",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ObjectApiResponse({'acknowledged': True, 'shards_acknowledged': True, 'index': 'hf-endpoint-index'})"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "client.indices.create(\n",
+ " index=\"hf-endpoint-index\",\n",
+ " settings = {\n",
+ " \"index\": {\n",
+ " \"default_pipeline\": \"hf_pipeline\",\n",
+ " }\n",
+ " },\n",
+ " mappings = {\n",
+ " \"properties\": {\n",
+ " \"text\": {\"type\": \"text\"},\n",
+ " \"text_embedding\": {\n",
+ " \"type\": \"dense_vector\",\n",
+ " \"dims\": 384,\n",
+ " \"similarity\": \"dot_product\",\n",
+ " },\n",
+ " }\n",
+ " }\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "12aa3452",
+ "metadata": {},
+ "source": [
+ "## If you are using Elasticsearch serverless or v8.15+ then you will have access to the new `semantic_text` field\n",
+ "`semantic_text` has significantly faster ingest times and is recommended.\n",
+ "\n",
+ "https://github.com/elastic/elasticsearch/blob/main/docs/reference/mapping/types/semantic-text.asciidoc"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "5eeb3755",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ObjectApiResponse({'acknowledged': True, 'shards_acknowledged': True, 'index': 'hf-semantic-text-index'})"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "client.indices.create(\n",
+ " index=\"hf-semantic-text-index\",\n",
+ " mappings={\n",
+ " \"properties\": {\n",
+ " \"infer_field\": {\n",
+ " \"type\": \"semantic_text\",\n",
+ " \"inference_id\": \"my_hf_endpoint_object\"\n",
+ " },\n",
+ " \"text_field\": {\n",
+ " \"type\": \"text\",\n",
+ " \"copy_to\": \"infer_field\"\n",
+ " }\n",
+ " }\n",
+ " }\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "07c187a9",
+ "metadata": {},
+ "source": [
+ "## Insert Documents\n",
+ "\n",
+ "In this example, we want to show the power of using GPUs in Hugging Face's Inference Endpoint service by indexing millions of multilingual documents from the miracl corpus. The speed at which these documents ingest will depend on whether you use a semantic text field (faster) or an ingest pipeline (slower) and will also depend on how much hardware your rent for your Hugging Face inference endpoint. Using a semantic_text field with a single T4 GPU, it may take about 3 hours to index 1 million documents. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "d68737cb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "langs = ['ar', 'bn', 'en', 'es', 'fa', 'fi', 'fr', 'hi', 'id', 'ja', 'ko', 'ru', 'sw', 'te', 'th', 'zh']\n",
+ "\n",
+ "\n",
+ "all_langs_datasets = [iter(datasets.load_dataset('miracl/miracl-corpus', lang)['train']) for lang in langs]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "id": "7ccf9835",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Docs uplaoded: 0\n",
+ "Docs uplaoded: 1000\n",
+ "Docs uplaoded: 2000\n",
+ "Docs uplaoded: 3000\n",
+ "Docs uplaoded: 4000\n",
+ "Docs uplaoded: 5000\n",
+ "Docs uplaoded: 6000\n",
+ "Docs uplaoded: 7000\n",
+ "Docs uplaoded: 8000\n",
+ "Docs uplaoded: 9000\n",
+ "Docs uplaoded: 10000\n",
+ "Docs uplaoded: 11000\n",
+ "Docs uplaoded: 12000\n",
+ "Docs uplaoded: 13000\n",
+ "Docs uplaoded: 14000\n",
+ "Docs uplaoded: 15000\n",
+ "Docs uplaoded: 16000\n",
+ "Docs uplaoded: 17000\n",
+ "Docs uplaoded: 18000\n",
+ "Docs uplaoded: 19000\n",
+ "Docs uplaoded: 20000\n",
+ "Docs uplaoded: 21000\n",
+ "Docs uplaoded: 22000\n",
+ "Docs uplaoded: 23000\n",
+ "Docs uplaoded: 24000\n",
+ "Docs uplaoded: 25000\n",
+ "Docs uplaoded: 26000\n",
+ "Docs uplaoded: 27000\n",
+ "Docs uplaoded: 28000\n",
+ "Docs uplaoded: 29000\n",
+ "Docs uplaoded: 30000\n",
+ "Docs uplaoded: 31000\n",
+ "Docs uplaoded: 32000\n",
+ "Docs uplaoded: 33000\n",
+ "Docs uplaoded: 34000\n",
+ "Docs uplaoded: 35000\n",
+ "Docs uplaoded: 36000\n",
+ "Docs uplaoded: 37000\n",
+ "Docs uplaoded: 38000\n",
+ "Docs uplaoded: 39000\n",
+ "Docs uplaoded: 40000\n",
+ "Docs uplaoded: 41000\n",
+ "Docs uplaoded: 42000\n",
+ "Docs uplaoded: 43000\n",
+ "Docs uplaoded: 44000\n",
+ "Docs uplaoded: 45000\n",
+ "Docs uplaoded: 46000\n",
+ "Docs uplaoded: 47000\n",
+ "Docs uplaoded: 48000\n",
+ "Docs uplaoded: 49000\n",
+ "Docs uplaoded: 50000\n",
+ "Docs uplaoded: 51000\n",
+ "Docs uplaoded: 52000\n",
+ "Docs uplaoded: 53000\n",
+ "Docs uplaoded: 54000\n",
+ "Docs uplaoded: 55000\n",
+ "Docs uplaoded: 56000\n",
+ "Docs uplaoded: 57000\n",
+ "Docs uplaoded: 58000\n",
+ "Docs uplaoded: 59000\n",
+ "Docs uplaoded: 60000\n",
+ "Docs uplaoded: 61000\n",
+ "Docs uplaoded: 62000\n",
+ "Docs uplaoded: 63000\n",
+ "Docs uplaoded: 64000\n",
+ "Docs uplaoded: 65000\n",
+ "Docs uplaoded: 66000\n",
+ "Docs uplaoded: 67000\n",
+ "Docs uplaoded: 68000\n",
+ "Docs uplaoded: 69000\n",
+ "Docs uplaoded: 70000\n",
+ "Docs uplaoded: 71000\n",
+ "Docs uplaoded: 72000\n",
+ "Docs uplaoded: 73000\n",
+ "Docs uplaoded: 74000\n",
+ "Docs uplaoded: 75000\n",
+ "Docs uplaoded: 76000\n",
+ "Docs uplaoded: 77000\n",
+ "Docs uplaoded: 78000\n",
+ "Docs uplaoded: 79000\n",
+ "Docs uplaoded: 80000\n",
+ "Docs uplaoded: 81000\n",
+ "Docs uplaoded: 82000\n",
+ "Docs uplaoded: 83000\n",
+ "Docs uplaoded: 84000\n",
+ "Docs uplaoded: 85000\n",
+ "Docs uplaoded: 86000\n",
+ "Docs uplaoded: 87000\n",
+ "Docs uplaoded: 88000\n",
+ "Docs uplaoded: 89000\n",
+ "Docs uplaoded: 90000\n",
+ "Docs uplaoded: 91000\n",
+ "Docs uplaoded: 92000\n",
+ "Docs uplaoded: 93000\n",
+ "Docs uplaoded: 94000\n",
+ "Docs uplaoded: 95000\n",
+ "Docs uplaoded: 96000\n",
+ "Docs uplaoded: 97000\n",
+ "Docs uplaoded: 98000\n",
+ "Docs uplaoded: 99000\n",
+ "Docs uplaoded: 100000\n",
+ "Docs uplaoded: 101000\n",
+ "Docs uplaoded: 102000\n",
+ "Docs uplaoded: 103000\n",
+ "Docs uplaoded: 104000\n",
+ "Docs uplaoded: 105000\n",
+ "Docs uplaoded: 106000\n",
+ "Docs uplaoded: 107000\n",
+ "Docs uplaoded: 108000\n",
+ "Docs uplaoded: 109000\n",
+ "Docs uplaoded: 110000\n",
+ "Docs uplaoded: 111000\n",
+ "Docs uplaoded: 112000\n",
+ "Docs uplaoded: 113000\n"
+ ]
+ },
+ {
+ "ename": "KeyboardInterrupt",
+ "evalue": "",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[48], line 25\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[38;5;66;03m# if you are using an ingest pipeline instead of a \u001b[39;00m\n\u001b[1;32m 16\u001b[0m \u001b[38;5;66;03m# semantic text field, use this instead:\u001b[39;00m\n\u001b[1;32m 17\u001b[0m \u001b[38;5;66;03m# documents.append(\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[38;5;66;03m# }\u001b[39;00m\n\u001b[1;32m 22\u001b[0m \u001b[38;5;66;03m# )\u001b[39;00m\n\u001b[1;32m 24\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 25\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mhelpers\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbulk\u001b[49m\u001b[43m(\u001b[49m\u001b[43mclient\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdocuments\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mraise_on_error\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m60s\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDocs uplaoded:\u001b[39m\u001b[38;5;124m\"\u001b[39m, j\u001b[38;5;241m*\u001b[39mMAX_BULK_SIZE)\n\u001b[1;32m 28\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/helpers/actions.py:521\u001b[0m, in \u001b[0;36mbulk\u001b[0;34m(client, actions, stats_only, ignore_status, *args, **kwargs)\u001b[0m\n\u001b[1;32m 519\u001b[0m \u001b[38;5;66;03m# make streaming_bulk yield successful results so we can count them\u001b[39;00m\n\u001b[1;32m 520\u001b[0m kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124myield_ok\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m--> 521\u001b[0m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mok\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mitem\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstreaming_bulk\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 522\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mactions\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mignore_status\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mignore_status\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[misc]\u001b[39;49;00m\n\u001b[1;32m 523\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 524\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# go through request-response pairs and detect failures\u001b[39;49;00m\n\u001b[1;32m 525\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mok\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 526\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstats_only\u001b[49m\u001b[43m:\u001b[49m\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/helpers/actions.py:436\u001b[0m, in \u001b[0;36mstreaming_bulk\u001b[0;34m(client, actions, chunk_size, max_chunk_bytes, raise_on_error, expand_action_callback, raise_on_exception, max_retries, initial_backoff, max_backoff, yield_ok, ignore_status, *args, **kwargs)\u001b[0m\n\u001b[1;32m 433\u001b[0m time\u001b[38;5;241m.\u001b[39msleep(\u001b[38;5;28mmin\u001b[39m(max_backoff, initial_backoff \u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m (attempt \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m)))\n\u001b[1;32m 435\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 436\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[43mok\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minfo\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mzip\u001b[39;49m\u001b[43m(\u001b[49m\n\u001b[1;32m 437\u001b[0m \u001b[43m \u001b[49m\u001b[43mbulk_data\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 438\u001b[0m \u001b[43m \u001b[49m\u001b[43m_process_bulk_chunk\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 439\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 440\u001b[0m \u001b[43m \u001b[49m\u001b[43mbulk_actions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 441\u001b[0m \u001b[43m \u001b[49m\u001b[43mbulk_data\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 442\u001b[0m \u001b[43m \u001b[49m\u001b[43mraise_on_exception\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 443\u001b[0m \u001b[43m \u001b[49m\u001b[43mraise_on_error\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 444\u001b[0m \u001b[43m \u001b[49m\u001b[43mignore_status\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 445\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 446\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 447\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 448\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 449\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mok\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 450\u001b[0m \u001b[43m \u001b[49m\u001b[43maction\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minfo\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43minfo\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpopitem\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/helpers/actions.py:339\u001b[0m, in \u001b[0;36m_process_bulk_chunk\u001b[0;34m(client, bulk_actions, bulk_data, raise_on_exception, raise_on_error, ignore_status, *args, **kwargs)\u001b[0m\n\u001b[1;32m 335\u001b[0m ignore_status \u001b[38;5;241m=\u001b[39m (ignore_status,)\n\u001b[1;32m 337\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 338\u001b[0m \u001b[38;5;66;03m# send the actual request\u001b[39;00m\n\u001b[0;32m--> 339\u001b[0m resp \u001b[38;5;241m=\u001b[39m \u001b[43mclient\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbulk\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moperations\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbulk_actions\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n\u001b[1;32m 340\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m ApiError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 341\u001b[0m gen \u001b[38;5;241m=\u001b[39m _process_bulk_chunk_error(\n\u001b[1;32m 342\u001b[0m error\u001b[38;5;241m=\u001b[39me,\n\u001b[1;32m 343\u001b[0m bulk_data\u001b[38;5;241m=\u001b[39mbulk_data,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 346\u001b[0m raise_on_error\u001b[38;5;241m=\u001b[39mraise_on_error,\n\u001b[1;32m 347\u001b[0m )\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/_sync/client/utils.py:446\u001b[0m, in \u001b[0;36m_rewrite_parameters..wrapper..wrapped\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 443\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n\u001b[1;32m 444\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[0;32m--> 446\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mapi\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/_sync/client/__init__.py:714\u001b[0m, in \u001b[0;36mElasticsearch.bulk\u001b[0;34m(self, operations, body, index, error_trace, filter_path, human, pipeline, pretty, refresh, require_alias, routing, source, source_excludes, source_includes, timeout, wait_for_active_shards)\u001b[0m\n\u001b[1;32m 709\u001b[0m __body \u001b[38;5;241m=\u001b[39m operations \u001b[38;5;28;01mif\u001b[39;00m operations \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m body\n\u001b[1;32m 710\u001b[0m __headers \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 711\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124maccept\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mapplication/json\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 712\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent-type\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mapplication/x-ndjson\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 713\u001b[0m }\n\u001b[0;32m--> 714\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mperform_request\u001b[49m\u001b[43m(\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[return-value]\u001b[39;49;00m\n\u001b[1;32m 715\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mPUT\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 716\u001b[0m \u001b[43m \u001b[49m\u001b[43m__path\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 717\u001b[0m \u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__query\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 718\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 719\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__body\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 720\u001b[0m \u001b[43m \u001b[49m\u001b[43mendpoint_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mbulk\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 721\u001b[0m \u001b[43m \u001b[49m\u001b[43mpath_parts\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__path_parts\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 722\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/_sync/client/_base.py:271\u001b[0m, in \u001b[0;36mBaseClient.perform_request\u001b[0;34m(self, method, path, params, headers, body, endpoint_id, path_parts)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mperform_request\u001b[39m(\n\u001b[1;32m 256\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 257\u001b[0m method: \u001b[38;5;28mstr\u001b[39m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 264\u001b[0m path_parts: Optional[Mapping[\u001b[38;5;28mstr\u001b[39m, Any]] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 265\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ApiResponse[Any]:\n\u001b[1;32m 266\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_otel\u001b[38;5;241m.\u001b[39mspan(\n\u001b[1;32m 267\u001b[0m method,\n\u001b[1;32m 268\u001b[0m endpoint_id\u001b[38;5;241m=\u001b[39mendpoint_id,\n\u001b[1;32m 269\u001b[0m path_parts\u001b[38;5;241m=\u001b[39mpath_parts \u001b[38;5;129;01mor\u001b[39;00m {},\n\u001b[1;32m 270\u001b[0m ) \u001b[38;5;28;01mas\u001b[39;00m otel_span:\n\u001b[0;32m--> 271\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_perform_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 272\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 273\u001b[0m \u001b[43m \u001b[49m\u001b[43mpath\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 274\u001b[0m \u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 275\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 276\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbody\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 277\u001b[0m \u001b[43m \u001b[49m\u001b[43motel_span\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43motel_span\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 278\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 279\u001b[0m otel_span\u001b[38;5;241m.\u001b[39mset_elastic_cloud_metadata(response\u001b[38;5;241m.\u001b[39mmeta\u001b[38;5;241m.\u001b[39mheaders)\n\u001b[1;32m 280\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m response\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/_sync/client/_base.py:316\u001b[0m, in \u001b[0;36mBaseClient._perform_request\u001b[0;34m(self, method, path, params, headers, body, otel_span)\u001b[0m\n\u001b[1;32m 313\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 314\u001b[0m target \u001b[38;5;241m=\u001b[39m path\n\u001b[0;32m--> 316\u001b[0m meta, resp_body \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtransport\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mperform_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 317\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 318\u001b[0m \u001b[43m \u001b[49m\u001b[43mtarget\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 319\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 320\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbody\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 321\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_timeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_request_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 322\u001b[0m \u001b[43m \u001b[49m\u001b[43mmax_retries\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_max_retries\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 323\u001b[0m \u001b[43m \u001b[49m\u001b[43mretry_on_status\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_retry_on_status\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 324\u001b[0m \u001b[43m \u001b[49m\u001b[43mretry_on_timeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_retry_on_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 325\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient_meta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_client_meta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 326\u001b[0m \u001b[43m \u001b[49m\u001b[43motel_span\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43motel_span\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 327\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 329\u001b[0m \u001b[38;5;66;03m# HEAD with a 404 is returned as a normal response\u001b[39;00m\n\u001b[1;32m 330\u001b[0m \u001b[38;5;66;03m# since this is used as an 'exists' functionality.\u001b[39;00m\n\u001b[1;32m 331\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (method \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHEAD\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m meta\u001b[38;5;241m.\u001b[39mstatus \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m404\u001b[39m) \u001b[38;5;129;01mand\u001b[39;00m (\n\u001b[1;32m 332\u001b[0m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;241m200\u001b[39m \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m meta\u001b[38;5;241m.\u001b[39mstatus \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m299\u001b[39m\n\u001b[1;32m 333\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m (\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 337\u001b[0m )\n\u001b[1;32m 338\u001b[0m ):\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elastic_transport/_transport.py:342\u001b[0m, in \u001b[0;36mTransport.perform_request\u001b[0;34m(self, method, target, body, headers, max_retries, retry_on_status, retry_on_timeout, request_timeout, client_meta, otel_span)\u001b[0m\n\u001b[1;32m 340\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 341\u001b[0m otel_span\u001b[38;5;241m.\u001b[39mset_node_metadata(node\u001b[38;5;241m.\u001b[39mhost, node\u001b[38;5;241m.\u001b[39mport, node\u001b[38;5;241m.\u001b[39mbase_url, target)\n\u001b[0;32m--> 342\u001b[0m resp \u001b[38;5;241m=\u001b[39m \u001b[43mnode\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mperform_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 343\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 344\u001b[0m \u001b[43m \u001b[49m\u001b[43mtarget\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 345\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_body\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 346\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 347\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_timeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 348\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 349\u001b[0m _logger\u001b[38;5;241m.\u001b[39minfo(\n\u001b[1;32m 350\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m [status:\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m duration:\u001b[39m\u001b[38;5;132;01m%.3f\u001b[39;00m\u001b[38;5;124ms]\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 351\u001b[0m \u001b[38;5;241m%\u001b[39m (\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 357\u001b[0m )\n\u001b[1;32m 358\u001b[0m )\n\u001b[1;32m 360\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m method \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHEAD\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elastic_transport/_node/_http_urllib3.py:167\u001b[0m, in \u001b[0;36mUrllib3HttpNode.perform_request\u001b[0;34m(self, method, target, body, headers, request_timeout)\u001b[0m\n\u001b[1;32m 164\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 165\u001b[0m body_to_send \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m--> 167\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpool\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43murlopen\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 168\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 169\u001b[0m \u001b[43m \u001b[49m\u001b[43mtarget\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 170\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbody_to_send\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 171\u001b[0m \u001b[43m \u001b[49m\u001b[43mretries\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mRetry\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 172\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 173\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkw\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 174\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 175\u001b[0m response_headers \u001b[38;5;241m=\u001b[39m HttpHeaders(response\u001b[38;5;241m.\u001b[39mheaders)\n\u001b[1;32m 176\u001b[0m data \u001b[38;5;241m=\u001b[39m response\u001b[38;5;241m.\u001b[39mdata\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/connectionpool.py:790\u001b[0m, in \u001b[0;36mHTTPConnectionPool.urlopen\u001b[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001b[0m\n\u001b[1;32m 787\u001b[0m response_conn \u001b[38;5;241m=\u001b[39m conn \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m release_conn \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 789\u001b[0m \u001b[38;5;66;03m# Make the request on the HTTPConnection object\u001b[39;00m\n\u001b[0;32m--> 790\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_make_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 791\u001b[0m \u001b[43m \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 792\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 793\u001b[0m \u001b[43m \u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 794\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout_obj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 795\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbody\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 796\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 797\u001b[0m \u001b[43m \u001b[49m\u001b[43mchunked\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mchunked\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 798\u001b[0m \u001b[43m \u001b[49m\u001b[43mretries\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 799\u001b[0m \u001b[43m \u001b[49m\u001b[43mresponse_conn\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresponse_conn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 800\u001b[0m \u001b[43m \u001b[49m\u001b[43mpreload_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpreload_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 801\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 802\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mresponse_kw\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 803\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 805\u001b[0m \u001b[38;5;66;03m# Everything went great!\u001b[39;00m\n\u001b[1;32m 806\u001b[0m clean_exit \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/connectionpool.py:536\u001b[0m, in \u001b[0;36mHTTPConnectionPool._make_request\u001b[0;34m(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)\u001b[0m\n\u001b[1;32m 534\u001b[0m \u001b[38;5;66;03m# Receive the response from the server\u001b[39;00m\n\u001b[1;32m 535\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 536\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mconn\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgetresponse\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 537\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (BaseSSLError, \u001b[38;5;167;01mOSError\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 538\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_raise_timeout(err\u001b[38;5;241m=\u001b[39me, url\u001b[38;5;241m=\u001b[39murl, timeout_value\u001b[38;5;241m=\u001b[39mread_timeout)\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/connection.py:475\u001b[0m, in \u001b[0;36mHTTPConnection.getresponse\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 466\u001b[0m log\u001b[38;5;241m.\u001b[39mwarning(\n\u001b[1;32m 467\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFailed to parse headers (url=\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m): \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 468\u001b[0m _url_from_connection(\u001b[38;5;28mself\u001b[39m, resp_options\u001b[38;5;241m.\u001b[39mrequest_url),\n\u001b[1;32m 469\u001b[0m hpe,\n\u001b[1;32m 470\u001b[0m exc_info\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 471\u001b[0m )\n\u001b[1;32m 473\u001b[0m headers \u001b[38;5;241m=\u001b[39m HTTPHeaderDict(httplib_response\u001b[38;5;241m.\u001b[39mmsg\u001b[38;5;241m.\u001b[39mitems())\n\u001b[0;32m--> 475\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mHTTPResponse\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 476\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 477\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 478\u001b[0m \u001b[43m \u001b[49m\u001b[43mstatus\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstatus\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 479\u001b[0m \u001b[43m \u001b[49m\u001b[43mversion\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mversion\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 480\u001b[0m \u001b[43m \u001b[49m\u001b[43mreason\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mreason\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 481\u001b[0m \u001b[43m \u001b[49m\u001b[43mpreload_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpreload_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 482\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 483\u001b[0m \u001b[43m \u001b[49m\u001b[43moriginal_response\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 484\u001b[0m \u001b[43m \u001b[49m\u001b[43menforce_content_length\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43menforce_content_length\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 485\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_method\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest_method\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 486\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_url\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest_url\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 487\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 488\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m response\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/response.py:597\u001b[0m, in \u001b[0;36mHTTPResponse.__init__\u001b[0;34m(self, body, headers, status, version, reason, preload_content, decode_content, original_response, pool, connection, msg, retries, enforce_content_length, request_method, request_url, auto_close)\u001b[0m\n\u001b[1;32m 595\u001b[0m \u001b[38;5;66;03m# If requested, preload the body.\u001b[39;00m\n\u001b[1;32m 596\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m preload_content \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_body:\n\u001b[0;32m--> 597\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_body \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_content\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/response.py:877\u001b[0m, in \u001b[0;36mHTTPResponse.read\u001b[0;34m(self, amt, decode_content, cache_content)\u001b[0m\n\u001b[1;32m 874\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_decoded_buffer) \u001b[38;5;241m>\u001b[39m\u001b[38;5;241m=\u001b[39m amt:\n\u001b[1;32m 875\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_decoded_buffer\u001b[38;5;241m.\u001b[39mget(amt)\n\u001b[0;32m--> 877\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_raw_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 879\u001b[0m flush_decoder \u001b[38;5;241m=\u001b[39m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mor\u001b[39;00m (amt \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data)\n\u001b[1;32m 881\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_decoded_buffer) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/response.py:812\u001b[0m, in \u001b[0;36mHTTPResponse._raw_read\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 809\u001b[0m fp_closed \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_fp, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mclosed\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[1;32m 811\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_error_catcher():\n\u001b[0;32m--> 812\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_fp_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m fp_closed \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;124mb\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 813\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m amt \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data:\n\u001b[1;32m 814\u001b[0m \u001b[38;5;66;03m# Platform-specific: Buggy versions of Python.\u001b[39;00m\n\u001b[1;32m 815\u001b[0m \u001b[38;5;66;03m# Close the connection when no data is returned\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 820\u001b[0m \u001b[38;5;66;03m# not properly close the connection in all cases. There is\u001b[39;00m\n\u001b[1;32m 821\u001b[0m \u001b[38;5;66;03m# no harm in redundantly calling close.\u001b[39;00m\n\u001b[1;32m 822\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_fp\u001b[38;5;241m.\u001b[39mclose()\n",
+ "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/response.py:797\u001b[0m, in \u001b[0;36mHTTPResponse._fp_read\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 794\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m buffer\u001b[38;5;241m.\u001b[39mgetvalue()\n\u001b[1;32m 795\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 796\u001b[0m \u001b[38;5;66;03m# StringIO doesn't like amt=None\u001b[39;00m\n\u001b[0;32m--> 797\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_fp\u001b[38;5;241m.\u001b[39mread(amt) \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_fp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/http/client.py:460\u001b[0m, in \u001b[0;36mHTTPResponse.read\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 457\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;124mb\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 459\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mchunked:\n\u001b[0;32m--> 460\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_read_chunked\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 462\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 463\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlength \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m amt \u001b[38;5;241m>\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlength:\n\u001b[1;32m 464\u001b[0m \u001b[38;5;66;03m# clip the read to the \"end of response\"\u001b[39;00m\n",
+ "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/http/client.py:592\u001b[0m, in \u001b[0;36mHTTPResponse._read_chunked\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 589\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mchunk_left \u001b[38;5;241m=\u001b[39m chunk_left \u001b[38;5;241m-\u001b[39m amt\n\u001b[1;32m 590\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[0;32m--> 592\u001b[0m value\u001b[38;5;241m.\u001b[39mappend(\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_safe_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mchunk_left\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 593\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 594\u001b[0m amt \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m=\u001b[39m chunk_left\n",
+ "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/http/client.py:631\u001b[0m, in \u001b[0;36mHTTPResponse._safe_read\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 624\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_safe_read\u001b[39m(\u001b[38;5;28mself\u001b[39m, amt):\n\u001b[1;32m 625\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Read the number of bytes requested.\u001b[39;00m\n\u001b[1;32m 626\u001b[0m \n\u001b[1;32m 627\u001b[0m \u001b[38;5;124;03m This function should be used when bytes \"should\" be present for\u001b[39;00m\n\u001b[1;32m 628\u001b[0m \u001b[38;5;124;03m reading. If the bytes are truly not available (due to EOF), then the\u001b[39;00m\n\u001b[1;32m 629\u001b[0m \u001b[38;5;124;03m IncompleteRead exception can be used to detect the problem.\u001b[39;00m\n\u001b[1;32m 630\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 631\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfp\u001b[38;5;241m.\u001b[39mread(amt)\n\u001b[1;32m 632\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(data) \u001b[38;5;241m<\u001b[39m amt:\n\u001b[1;32m 633\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m IncompleteRead(data, amt\u001b[38;5;241m-\u001b[39m\u001b[38;5;28mlen\u001b[39m(data))\n",
+ "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/socket.py:706\u001b[0m, in \u001b[0;36mSocketIO.readinto\u001b[0;34m(self, b)\u001b[0m\n\u001b[1;32m 704\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[1;32m 705\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 706\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_sock\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrecv_into\u001b[49m\u001b[43m(\u001b[49m\u001b[43mb\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 707\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m timeout:\n\u001b[1;32m 708\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_timeout_occurred \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n",
+ "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/ssl.py:1278\u001b[0m, in \u001b[0;36mSSLSocket.recv_into\u001b[0;34m(self, buffer, nbytes, flags)\u001b[0m\n\u001b[1;32m 1274\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m flags \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 1275\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 1276\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnon-zero flags not allowed in calls to recv_into() on \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m%\u001b[39m\n\u001b[1;32m 1277\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__class__\u001b[39m)\n\u001b[0;32m-> 1278\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnbytes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbuffer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1279\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1280\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39mrecv_into(buffer, nbytes, flags)\n",
+ "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/ssl.py:1134\u001b[0m, in \u001b[0;36mSSLSocket.read\u001b[0;34m(self, len, buffer)\u001b[0m\n\u001b[1;32m 1132\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1133\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m buffer \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1134\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_sslobj\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbuffer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1135\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1136\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_sslobj\u001b[38;5;241m.\u001b[39mread(\u001b[38;5;28mlen\u001b[39m)\n",
+ "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
+ ]
+ }
+ ],
+ "source": [
+ "MAX_BULK_SIZE = 1000\n",
+ "MAX_BULK_UPLOADS = 1000\n",
+ "\n",
+ "sentinel = object()\n",
+ "for j in range(MAX_BULK_UPLOADS):\n",
+ " documents = []\n",
+ " while len(documents) < MAX_BULK_SIZE - len(all_langs_datasets):\n",
+ " for ds in all_langs_datasets:\n",
+ " text = next(ds, sentinel)\n",
+ " if text is not sentinel:\n",
+ " documents.append({\n",
+ " \"_index\": \"hf-semantic-text-index\",\n",
+ " \"_source\": {\"text_field\": text['text']},\n",
+ " })\n",
+ " # if you are using an ingest pipeline instead of a \n",
+ " # semantic text field, use this instead:\n",
+ " # documents.append(\n",
+ " # {\n",
+ " # \"_index\": \"hf-endpoint-index\",\n",
+ " # \"_source\": {\"text\": text['text']},\n",
+ " # }\n",
+ " # )\n",
+ "\n",
+ " try:\n",
+ " response = helpers.bulk(client, documents, raise_on_error=False, timeout=\"60s\")\n",
+ " print(\"Docs uplaoded:\", (j+1)*MAX_BULK_SIZE)\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(\"exception:\", str(e))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cf0f6df7",
+ "metadata": {},
+ "source": [
+ "## Semantic search\n",
+ "\n",
+ "After the dataset has been enriched with the embeddings, you can query the data using [semantic search](https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-semantic-search). Pass a `query_vector_builder` to the k-nearest neighbor (kNN) vector search API, and provide the query text and the model you have used to create the embeddings."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "id": "d9b21b71",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "query = \"English speaking countries\"\n",
+ "semantic_search_results = client.search(\n",
+ " index=\"hf-semantic-text-index\",\n",
+ " query={\"semantic\": {\"field\": \"infer_field\", \"query\": query}},\n",
+ ")\n",
+ "\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "id": "8ef79d16",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "ID: lnBvR5EBptSvxeXARnIB\n",
+ "Score: 0.9464458\n",
+ "Text: English-\n",
+ "\n",
+ "ID: LHF6R5EBptSvxeXA2LN6\n",
+ "Score: 0.93809533\n",
+ "Text: अंग्रेजी\n",
+ "\n",
+ "ID: ynF1R5EBptSvxeXAqSO4\n",
+ "Score: 0.93671393\n",
+ "Text: en anglais\n",
+ "\n",
+ "ID: e288R5EBptSvxeXANHxf\n",
+ "Score: 0.9351928\n",
+ "Text: المملكة المتحدة\n",
+ "\n",
+ "ID: hHBxR5EBptSvxeXAdq3W\n",
+ "Score: 0.9330108\n",
+ "Text: Argos - United Kingdom\n",
+ "\n",
+ "ID: Z3BzR5EBptSvxeXAwfCg\n",
+ "Score: 0.9316524\n",
+ "Text: Alemania\n",
+ "\n",
+ "ID: GHBxR5EBptSvxeXAubW9\n",
+ "Score: 0.9309325\n",
+ "Text: انگلیسی\n",
+ "\n",
+ "ID: Cm87R5EBptSvxeXA_HgZ\n",
+ "Score: 0.9307623\n",
+ "Text: . 🇹🇯\n",
+ "\n",
+ "ID: VnBxR5EBptSvxeXAqbSI\n",
+ "Score: 0.92829084\n",
+ "Text: Specific\n",
+ "\n",
+ "ID: G3F8R5EBptSvxeXACNSX\n",
+ "Score: 0.9281881\n",
+ "Text: Transports\n"
+ ]
+ }
+ ],
+ "source": [
+ "pretty_search_response(semantic_search_results)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "id": "a7053b1e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "ObjectApiResponse({'model_id': 'my_cohere_rerank_endpoint', 'inference_id': 'my_cohere_rerank_endpoint', 'task_type': 'rerank', 'service': 'cohere', 'service_settings': {'model_id': 'rerank-english-v3.0', 'rate_limit': {'requests_per_minute': 10000}}, 'task_settings': {'top_n': 100, 'return_documents': True}})"
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "try:\n",
+ " client.inference.delete_model(inference_id=\"my_cohere_rerank_endpoint\")\n",
+ "except Exception:\n",
+ " pass\n",
+ "client.inference.put_model(\n",
+ " task_type=\"rerank\",\n",
+ " inference_id=\"my_cohere_rerank_endpoint\",\n",
+ " body={\n",
+ " \"service\": \"cohere\",\n",
+ " \"service_settings\": {\n",
+ " \"api_key\": \"h2OzeuORCdvJ8eidGYbHmjfeWcecRQN8MYGDHxK1\",\n",
+ " \"model_id\": \"rerank-english-v3.0\"\n",
+ " },\n",
+ " \"task_settings\": {\n",
+ " \"top_n\": 100,\n",
+ " \"return_documents\": True\n",
+ " }\n",
+ " }\n",
+ ")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "id": "6fba4f22",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "reranked_search_results = client.search(\n",
+ " index=\"hf-semantic-text-index\",\n",
+ " retriever= {\n",
+ " \"text_similarity_reranker\": {\n",
+ " \"retriever\": {\n",
+ " \"standard\": {\n",
+ " \"query\": {\n",
+ " \"semantic\": {\n",
+ " \"field\": \"infer_field\",\n",
+ " \"query\": query \n",
+ " }\n",
+ " }\n",
+ " }\n",
+ " },\n",
+ " \"field\": \"text_field\",\n",
+ " \"inference_id\": \"my_cohere_rerank_endpoint\",\n",
+ " \"inference_text\": query,\n",
+ " \"rank_window_size\": 100,\n",
+ " }\n",
+ " }\n",
+ ")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "id": "fd4ef932",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "ID: 8HBuR5EBptSvxeXAPVTR\n",
+ "Score: 0.018546565\n",
+ "Text: Kiingereza ni lugha rasmi pekee katika nchi za Uingereza, Marekani, Australia, Nyuzilandi, Jamaica, na nchi nyingine.\n",
+ "\n",
+ "ID: lnBvR5EBptSvxeXARnIB\n",
+ "Score: 0.014728614\n",
+ "Text: English-\n",
+ "\n",
+ "ID: 13F5R5EBptSvxeXAhY5q\n",
+ "Score: 0.0085443305\n",
+ "Text: The questions are worded in Canadian-English, with Canadian terminology and spelling, and are not localized for the American, UK or Australian markets.\n",
+ "\n",
+ "ID: 7G9CR5EBptSvxeXAwPtI\n",
+ "Score: 0.004451041\n",
+ "Text: باللغة العربية\n",
+ "باللغة الإنجليزية\n",
+ "\n",
+ "ID: 13F4R5EBptSvxeXAV23K\n",
+ "Score: 0.0031480705\n",
+ "Text: (en inglés):\n",
+ "\n",
+ "ID: 4HF4R5EBptSvxeXA2Xpb\n",
+ "Score: 0.0030634168\n",
+ "Text: Greenland, *Iceland, Britania (Uingereza), Ueire (Ireland), Kuba, Newfoundland\n",
+ "\n",
+ "ID: Z3BzR5EBptSvxeXAwfCg\n",
+ "Score: 0.0025311112\n",
+ "Text: Alemania\n",
+ "\n",
+ "ID: tnBvR5EBptSvxeXARnIB\n",
+ "Score: 0.0018031665\n",
+ "Text: Italian-\n",
+ "\n",
+ "ID: 128-R5EBptSvxeXAw603\n",
+ "Score: 0.0016809417\n",
+ "Text: بالعربي الاختصار بالإنكليزي\n",
+ "\n",
+ "ID: ynF1R5EBptSvxeXAqSO4\n",
+ "Score: 0.0016040893\n",
+ "Text: en anglais\n"
+ ]
+ }
+ ],
+ "source": [
+ "pretty_search_response(reranked_search_results)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7e4055ba",
+ "metadata": {},
+ "source": [
+ "**NOTE:** The value of `model_id` in the `query_vector_builder` must match the value of `inference_id` you created in the [first step](#create-the-inference-endpoint)."
+ ]
+ }
+ ],
+ "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.11.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents.ipynb b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents.ipynb
deleted file mode 100644
index 627fa779..00000000
--- a/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents.ipynb
+++ /dev/null
@@ -1,554 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "7a765629",
- "metadata": {},
- "source": [
- "# Semantic Search using the Inference API with the Hugging Face Inference Endpoints Service\n",
- "\n",
- "TODO [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/elastic/elasticsearch-labs/blob/main/notebooks/search/TODO)\n",
- "\n",
- "\n",
- "Learn how to use the [Inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-apis.html) with the Hugging Face Inference Endpoint service for semantic search."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f9101eb9",
- "metadata": {},
- "source": [
- "# 🧰 Requirements\n",
- "\n",
- "For this example, you will need:\n",
- "\n",
- "- An Elastic deployment:\n",
- " - We'll be using [Elastic Cloud](https://www.elastic.co/guide/en/cloud/current/ec-getting-started.html) for this example (available with a [free trial](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook))\n",
- "\n",
- "- Elasticsearch 8.14 or above.\n",
- " \n",
- "- A paid [Hugging Face Inference Endpoint](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) is required to use the Inference API with \n",
- "the Hugging Face Inference Endpoint service."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "4cd69cc0",
- "metadata": {},
- "source": [
- "# Create Elastic Cloud deployment or serverless project\n",
- "\n",
- "If you don't have an Elastic Cloud deployment, sign up [here](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook) for a free trial."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f27dffbf",
- "metadata": {},
- "source": [
- "# Install packages and connect with Elasticsearch Client\n",
- "\n",
- "To get started, we'll need to connect to our Elastic deployment using the Python client (version 8.12.0 or above).\n",
- "Because we're using an Elastic Cloud deployment, we'll use the **Cloud ID** to identify our deployment.\n",
- "\n",
- "First we need to `pip` install the following packages:\n",
- "\n",
- "- `elasticsearch`"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "8c4b16bc",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Requirement already satisfied: elasticsearch in /Users/mh/.venv/lib/python3.11/site-packages (8.14.0)\n",
- "Requirement already satisfied: elastic-transport<9,>=8.13 in /Users/mh/.venv/lib/python3.11/site-packages (from elasticsearch) (8.13.1)\n",
- "Requirement already satisfied: urllib3<3,>=1.26.2 in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch) (2.1.0)\n",
- "Requirement already satisfied: certifi in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch) (2023.11.17)\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.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.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",
- "Requirement already satisfied: datasets in /Users/mh/.venv/lib/python3.11/site-packages (2.20.0)\n",
- "Requirement already satisfied: filelock in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.13.1)\n",
- "Requirement already satisfied: numpy>=1.17 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (1.23.5)\n",
- "Requirement already satisfied: pyarrow>=15.0.0 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (17.0.0)\n",
- "Requirement already satisfied: pyarrow-hotfix in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.6)\n",
- "Requirement already satisfied: dill<0.3.9,>=0.3.0 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.3.8)\n",
- "Requirement already satisfied: pandas in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (1.5.3)\n",
- "Requirement already satisfied: requests>=2.32.2 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (2.32.3)\n",
- "Requirement already satisfied: tqdm>=4.66.3 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (4.66.4)\n",
- "Requirement already satisfied: xxhash in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.4.1)\n",
- "Requirement already satisfied: multiprocess in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.70.16)\n",
- "Requirement already satisfied: fsspec<=2024.5.0,>=2023.1.0 in /Users/mh/.venv/lib/python3.11/site-packages (from fsspec[http]<=2024.5.0,>=2023.1.0->datasets) (2024.5.0)\n",
- "Requirement already satisfied: aiohttp in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.9.5)\n",
- "Requirement already satisfied: huggingface-hub>=0.21.2 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.24.2)\n",
- "Requirement already satisfied: packaging in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (23.2)\n",
- "Requirement already satisfied: pyyaml>=5.1 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (6.0.1)\n",
- "Requirement already satisfied: aiosignal>=1.1.2 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.3.1)\n",
- "Requirement already satisfied: attrs>=17.3.0 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (23.2.0)\n",
- "Requirement already satisfied: frozenlist>=1.1.1 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.4.1)\n",
- "Requirement already satisfied: multidict<7.0,>=4.5 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (6.0.5)\n",
- "Requirement already satisfied: yarl<2.0,>=1.0 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.9.4)\n",
- "Requirement already satisfied: typing-extensions>=3.7.4.3 in /Users/mh/.venv/lib/python3.11/site-packages (from huggingface-hub>=0.21.2->datasets) (4.12.2)\n",
- "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (3.3.2)\n",
- "Requirement already satisfied: idna<4,>=2.5 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (3.6)\n",
- "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (2.1.0)\n",
- "Requirement already satisfied: certifi>=2017.4.17 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (2023.11.17)\n",
- "Requirement already satisfied: python-dateutil>=2.8.1 in /Users/mh/.venv/lib/python3.11/site-packages (from pandas->datasets) (2.8.2)\n",
- "Requirement already satisfied: pytz>=2020.1 in /Users/mh/.venv/lib/python3.11/site-packages (from pandas->datasets) (2023.3.post1)\n",
- "Requirement already satisfied: six>=1.5 in /Users/mh/.venv/lib/python3.11/site-packages (from python-dateutil>=2.8.1->pandas->datasets) (1.16.0)\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.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.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",
- "Note: you may need to restart the kernel to use updated packages.\n"
- ]
- }
- ],
- "source": [
- "!pip install elasticsearch\n",
- "%pip install datasets"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "41ef96b3",
- "metadata": {},
- "source": [
- "Next, we need to import the modules we need. 🔐 NOTE: getpass enables us to securely prompt the user for credentials without echoing them to the terminal, or storing it in memory."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "690ff9af",
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "/Users/mh/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
- " from .autonotebook import tqdm as notebook_tqdm\n"
- ]
- }
- ],
- "source": [
- "from elasticsearch import Elasticsearch, helpers, exceptions\n",
- "from urllib.request import urlopen\n",
- "from getpass import getpass\n",
- "import json\n",
- "import time\n",
- "from timeit import default_timer as timer\n",
- "import datasets\n",
- "import csv"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "23fa2b6c",
- "metadata": {},
- "source": [
- "Now we can instantiate the Python Elasticsearch client.\n",
- "\n",
- "First we prompt the user for their password and Cloud ID.\n",
- "Then we create a `client` object that instantiates an instance of the `Elasticsearch` class."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "195cc597",
- "metadata": {},
- "outputs": [],
- "source": [
- "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id\n",
- "ELASTIC_CLOUD_ID = getpass(\"Elastic Cloud ID: \")\n",
- "\n",
- "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key\n",
- "ELASTIC_API_KEY = getpass(\"Elastic Api Key: \")\n",
- "\n",
- "# Create the client instance\n",
- "client = Elasticsearch(\n",
- " # For local development\n",
- " # hosts=[\"http://localhost:9200\"]\n",
- " cloud_id=ELASTIC_CLOUD_ID,\n",
- " api_key=ELASTIC_API_KEY,\n",
- " request_timeout=120, \n",
- " max_retries=10,\n",
- " retry_on_timeout=True\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b1115ffb",
- "metadata": {},
- "source": [
- "### Test the Client\n",
- "Before you continue, confirm that the client has connected with this test."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "cc0de5ea",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'name': 'instance-0000000001', 'cluster_name': 'dd945ac31a6c456686ee5117f1ec38a2', 'cluster_uuid': 'puJnzmQtThKzbfH1e3_bVw', 'version': {'number': '8.14.3', 'build_flavor': 'default', 'build_type': 'docker', 'build_hash': 'd55f984299e0e88dee72ebd8255f7ff130859ad0', 'build_date': '2024-07-07T22:04:49.882652950Z', 'build_snapshot': False, 'lucene_version': '9.10.0', 'minimum_wire_compatibility_version': '7.17.0', 'minimum_index_compatibility_version': '7.0.0'}, 'tagline': 'You Know, for Search'}\n"
- ]
- }
- ],
- "source": [
- "print(client.info())"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "659c5890",
- "metadata": {},
- "source": [
- "Refer to [the documentation](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect to a self-managed deployment.\n",
- "\n",
- "Read [this page](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect using API keys."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "840d92f0",
- "metadata": {},
- "source": [
- "\n",
- "## Create the inference endpoint object\n",
- "\n",
- "Let's create the inference endpoint by using the [Create inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html).\n",
- "\n",
- "You'll need an Hugging Face API key (access token) for this that you can find in your Hugging Face account under the [Access Tokens](https://huggingface.co/settings/tokens).\n",
- "\n",
- "You will also need to have created a [Hugging Face Inference Endpoint service instance](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) and noted the `url` of your instance. For this notebook, we deployed the `multilingual-e5-small` model."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "0d007737",
- "metadata": {},
- "outputs": [],
- "source": [
- "API_KEY = getpass(\"Huggingface API key: \")\n",
- "try:\n",
- " client.inference.delete_model(inference_id=\"my_hf_endpoint_object\")\n",
- "except Exception as e:\n",
- " print(e)\n",
- "client.inference.put_model(\n",
- " inference_id='my_hf_endpoint_object',\n",
- " body={\n",
- " \"service\": \"hugging_face\",\n",
- " \"service_settings\": {\"api_key\": API_KEY, \n",
- " \"url\": \"\",\n",
- " \"similarity\": \"dot_product\"\n",
- " },\n",
- " },\n",
- " task_type=\"text_embedding\"\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "67f4201d",
- "metadata": {},
- "outputs": [],
- "source": [
- "client.inference.inference(\n",
- " inference_id='my_hf_endpoint_object',\n",
- " input=\"this is the raw text of my document!\"\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "1f2e48b7",
- "metadata": {},
- "source": [
- "**IMPORTANT:** If you use Elasticsearch 8.12, you must change `inference_id` in the snippet above to `model_id`! "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0346151d",
- "metadata": {},
- "source": [
- "#"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "1024d070",
- "metadata": {},
- "source": [
- "## Create an ingest pipeline with an inference processor\n",
- "\n",
- "Create an ingest pipeline with an inference processor by using the [`put_pipeline`](https://www.elastic.co/guide/en/elasticsearch/reference/master/put-pipeline-api.html) method. Reference the `inference_id` created above as `model_id` to infer on the data that is being ingested by the pipeline."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "6ace9e2e",
- "metadata": {},
- "outputs": [],
- "source": [
- "client.ingest.put_pipeline(\n",
- " id=\"hf_pipeline\",\n",
- " processors=[\n",
- " {\n",
- " \"inference\": {\n",
- " \"model_id\": \"my_hf_endpoint_object\",\n",
- " \"input_output\": {\n",
- " \"input_field\": \"text\",\n",
- " \"output_field\": \"text_embedding\",\n",
- " },\n",
- " }\n",
- " }\n",
- " ],\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "76d07567",
- "metadata": {},
- "source": [
- "Let's note a few important parameters from that API call:\n",
- "\n",
- "- `inference`: A processor that performs inference using a machine learning model.\n",
- "- `model_id`: Specifies the ID of the inference endpoint to be used. In this example, the inference ID is set to `my_hf_endpoint_object`. Use the inference ID you defined when created the inference task.\n",
- "- `input_output`: Specifies input and output fields.\n",
- "- `input_field`: Field name from which the `dense_vector` representation is created.\n",
- "- `output_field`: Field name which contains inference results. "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "28e12d7a",
- "metadata": {},
- "source": [
- "## Create index\n",
- "\n",
- "The mapping of the destination index - the index that contains the embeddings that the model will create based on your input text - must be created. The destination index must have a field with the [dense_vector](https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html) field type to index the output of the model we deployed in Hugging Face (`multilingual-e5-small`).\n",
- "\n",
- "Let's create an index named `hf-endpoint-index` with the mappings we need."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "6ddcbca3",
- "metadata": {},
- "outputs": [],
- "source": [
- "client.indices.create(\n",
- " index=\"hf-endpoint-index\",\n",
- " settings = {\n",
- " \"index\": {\n",
- " \"number_of_replicas\": \"1\",\n",
- " \"number_of_shards\": \"1\",\n",
- " \"default_pipeline\": \"hf_pipeline\",\n",
- " }\n",
- " },\n",
- " mappings = {\n",
- " \"properties\": {\n",
- " \"text\": {\"type\": \"text\"},\n",
- " \"text_embedding\": {\n",
- " \"type\": \"dense_vector\",\n",
- " \"dims\": 384,\n",
- " \"similarity\": \"dot_product\",\n",
- " },\n",
- " },\n",
- " \"_source\": {\"excludes\": [\"passage_embedding.predicted_value\"]},\n",
- " }\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "12aa3452",
- "metadata": {},
- "source": [
- "## If you are using Elasticsearch serverless or v8.15+ then you will have access to the new `semantic_text` field\n",
- "`semantic_text` has significantly faster ingest times and is recommended.\n",
- "\n",
- "https://github.com/elastic/elasticsearch/blob/main/docs/reference/mapping/types/semantic-text.asciidoc"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "5eeb3755",
- "metadata": {},
- "outputs": [],
- "source": [
- "client.indices.create(\n",
- " index=\"hf-semantic-text-index\",\n",
- " mappings={\n",
- " \"properties\": {\n",
- " \"infer_field\": {\n",
- " \"type\": \"semantic_text\",\n",
- " \"inference_id\": \"my_hf_endpoint_object\"\n",
- " }\n",
- " }\n",
- " },\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "07c187a9",
- "metadata": {},
- "source": [
- "## Insert Documents\n",
- "\n",
- "In this example, we want to show the power of using GPUs in Hugging Face's Inference Endpoint service by indexing millions of multilingual documents from the miracl corpus. The speed at which these documents ingest will depend on whether you use a semantic text field (faster) or an ingest pipeline (slower) and will also depend on how much hardware your rent for your Hugging Face inference endpoint. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "d68737cb",
- "metadata": {},
- "outputs": [],
- "source": [
- "langs = ['ar', 'bn', 'en', 'es', 'fa', 'fi', 'fr', 'hi', 'id', 'ja', 'ko', 'ru', 'sw', 'te', 'th', 'zh']\n",
- "\n",
- "\n",
- "all_langs_datasets = [iter(datasets.load_dataset('miracl/miracl-corpus', lang)['train']) for lang in langs]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "7ccf9835",
- "metadata": {},
- "outputs": [],
- "source": [
- "MAX_BULK_SIZE = 1000\n",
- "MAX_BULK_UPLOADS = 1000\n",
- "\n",
- "sentinel = object()\n",
- "for j in range():\n",
- " start = timer()\n",
- " try:\n",
- " time.sleep(1)\n",
- " documents = []\n",
- " while len(documents) < MAX_BULK_SIZE - len(all_langs_datasets):\n",
- " for ds in all_langs_datasets:\n",
- " text = next(ds, sentinel)\n",
- " if text is not sentinel:\n",
- " documents.append(\n",
- " {\n",
- " \"_index\": \"hf-endpoint-index\",\n",
- " \"_source\": {\"text\": text['text']},\n",
- " }\n",
- " )\n",
- " # if you are using semantic text, use this append instead:\n",
- " # documents.append(\n",
- " # {\n",
- " # \"_index\": \"hf-semantic-text-index\",\n",
- " # \"_source\": {\"infer_field\": text['text']},\n",
- " # }\n",
- " # )\n",
- "\n",
- " try:\n",
- " response = helpers.bulk(client, documents, raise_on_error=False, timeout=\"60s\")\n",
- "\n",
- " print(\"Doc set:\", j, end=\"; \")\n",
- " end=timer()\n",
- "\n",
- " except Exception as e:\n",
- " print(\"exception:\", str(e))\n",
- " time.sleep(30)\n",
- " except Exception as e:\n",
- " print(j, e)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "cf0f6df7",
- "metadata": {},
- "source": [
- "## Semantic search\n",
- "\n",
- "After the dataset has been enriched with the embeddings, you can query the data using [semantic search](https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-semantic-search). Pass a `query_vector_builder` to the k-nearest neighbor (kNN) vector search API, and provide the query text and the model you have used to create the embeddings."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "d9b21b71",
- "metadata": {},
- "outputs": [],
- "source": [
- "response = client.search(\n",
- " index=\"hf-endpoint-index\",\n",
- " size=3,\n",
- " knn={\n",
- " \"field\": \"text_embedding\",\n",
- " \"query_vector_builder\": {\n",
- " \"text_embedding\": {\n",
- " \"model_id\": \"my_hf_endpoint_object\",\n",
- " \"model_text\": \"Fighting movie\",\n",
- " }\n",
- " },\n",
- " \"k\": 10,\n",
- " \"num_candidates\": 100,\n",
- " },\n",
- ")\n",
- "\n",
- "for hit in response[\"hits\"][\"hits\"]:\n",
- " doc_id = hit[\"_id\"]\n",
- " score = hit[\"_score\"]\n",
- " text = hit[\"_source\"][\"text\"]\n",
- " print(f\"Score: {score}\\nText: {text}\\n\")\n",
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "7e4055ba",
- "metadata": {},
- "source": [
- "**NOTE:** The value of `model_id` in the `query_vector_builder` must match the value of `inference_id` you created in the [first step](#create-the-inference-endpoint)."
- ]
- }
- ],
- "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.11.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
From 3cd988ff98be72f917c117b4d0d87b0b3a55d1ba Mon Sep 17 00:00:00 2001
From: Max Hniebergall
Date: Mon, 12 Aug 2024 14:14:47 -0400
Subject: [PATCH 3/5] precommit
---
...s-of-documents-with-cohere-reranking.ipynb | 131 ++++++++++--------
1 file changed, 70 insertions(+), 61 deletions(-)
diff --git a/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb
index 1109a69c..1b38484a 100644
--- a/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb
+++ b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb
@@ -173,9 +173,9 @@
" # hosts=[\"http://localhost:9200\"]\n",
" cloud_id=ELASTIC_CLOUD_ID,\n",
" api_key=ELASTIC_API_KEY,\n",
- " request_timeout=120, \n",
+ " request_timeout=120,\n",
" max_retries=10,\n",
- " retry_on_timeout=True\n",
+ " retry_on_timeout=True,\n",
")"
]
},
@@ -205,6 +205,7 @@
"source": [
"print(client.info())\n",
"\n",
+ "\n",
"# define this now so we can use it later\n",
"def pretty_search_response(response):\n",
" if len(response[\"hits\"][\"hits\"]) == 0:\n",
@@ -265,15 +266,16 @@
"source": [
"API_KEY = getpass(\"Huggingface API key: \")\n",
"client.inference.put_model(\n",
- " inference_id='my_hf_endpoint_object',\n",
+ " inference_id=\"my_hf_endpoint_object\",\n",
" body={\n",
" \"service\": \"hugging_face\",\n",
- " \"service_settings\": {\"api_key\": API_KEY, \n",
- " \"url\": \"https://yb0j0ol2xzvro0oc.us-east-1.aws.endpoints.huggingface.cloud\",\n",
- " \"similarity\": \"dot_product\"\n",
- " },\n",
+ " \"service_settings\": {\n",
+ " \"api_key\": API_KEY,\n",
+ " \"url\": \"https://yb0j0ol2xzvro0oc.us-east-1.aws.endpoints.huggingface.cloud\",\n",
+ " \"similarity\": \"dot_product\",\n",
+ " },\n",
" },\n",
- " task_type=\"text_embedding\"\n",
+ " task_type=\"text_embedding\",\n",
")"
]
},
@@ -296,9 +298,8 @@
],
"source": [
"client.inference.inference(\n",
- " inference_id='my_hf_endpoint_object',\n",
- " input=\"this is the raw text of my document!\"\n",
- " )"
+ " inference_id=\"my_hf_endpoint_object\", input=\"this is the raw text of my document!\"\n",
+ ")"
]
},
{
@@ -407,12 +408,12 @@
"source": [
"client.indices.create(\n",
" index=\"hf-endpoint-index\",\n",
- " settings = {\n",
+ " settings={\n",
" \"index\": {\n",
" \"default_pipeline\": \"hf_pipeline\",\n",
" }\n",
" },\n",
- " mappings = {\n",
+ " mappings={\n",
" \"properties\": {\n",
" \"text\": {\"type\": \"text\"},\n",
" \"text_embedding\": {\n",
@@ -421,7 +422,7 @@
" \"similarity\": \"dot_product\",\n",
" },\n",
" }\n",
- " }\n",
+ " },\n",
")"
]
},
@@ -455,19 +456,16 @@
],
"source": [
"client.indices.create(\n",
- " index=\"hf-semantic-text-index\",\n",
- " mappings={\n",
+ " index=\"hf-semantic-text-index\",\n",
+ " mappings={\n",
" \"properties\": {\n",
" \"infer_field\": {\n",
" \"type\": \"semantic_text\",\n",
- " \"inference_id\": \"my_hf_endpoint_object\"\n",
+ " \"inference_id\": \"my_hf_endpoint_object\",\n",
" },\n",
- " \"text_field\": {\n",
- " \"type\": \"text\",\n",
- " \"copy_to\": \"infer_field\"\n",
- " }\n",
+ " \"text_field\": {\"type\": \"text\", \"copy_to\": \"infer_field\"},\n",
" }\n",
- " }\n",
+ " },\n",
")"
]
},
@@ -488,10 +486,29 @@
"metadata": {},
"outputs": [],
"source": [
- "langs = ['ar', 'bn', 'en', 'es', 'fa', 'fi', 'fr', 'hi', 'id', 'ja', 'ko', 'ru', 'sw', 'te', 'th', 'zh']\n",
+ "langs = [\n",
+ " \"ar\",\n",
+ " \"bn\",\n",
+ " \"en\",\n",
+ " \"es\",\n",
+ " \"fa\",\n",
+ " \"fi\",\n",
+ " \"fr\",\n",
+ " \"hi\",\n",
+ " \"id\",\n",
+ " \"ja\",\n",
+ " \"ko\",\n",
+ " \"ru\",\n",
+ " \"sw\",\n",
+ " \"te\",\n",
+ " \"th\",\n",
+ " \"zh\",\n",
+ "]\n",
"\n",
"\n",
- "all_langs_datasets = [iter(datasets.load_dataset('miracl/miracl-corpus', lang)['train']) for lang in langs]"
+ "all_langs_datasets = [\n",
+ " iter(datasets.load_dataset(\"miracl/miracl-corpus\", lang)[\"train\"]) for lang in langs\n",
+ "]"
]
},
{
@@ -665,11 +682,13 @@
" for ds in all_langs_datasets:\n",
" text = next(ds, sentinel)\n",
" if text is not sentinel:\n",
- " documents.append({\n",
- " \"_index\": \"hf-semantic-text-index\",\n",
- " \"_source\": {\"text_field\": text['text']},\n",
- " })\n",
- " # if you are using an ingest pipeline instead of a \n",
+ " documents.append(\n",
+ " {\n",
+ " \"_index\": \"hf-semantic-text-index\",\n",
+ " \"_source\": {\"text_field\": text[\"text\"]},\n",
+ " }\n",
+ " )\n",
+ " # if you are using an ingest pipeline instead of a\n",
" # semantic text field, use this instead:\n",
" # documents.append(\n",
" # {\n",
@@ -680,7 +699,7 @@
"\n",
" try:\n",
" response = helpers.bulk(client, documents, raise_on_error=False, timeout=\"60s\")\n",
- " print(\"Docs uplaoded:\", (j+1)*MAX_BULK_SIZE)\n",
+ " print(\"Docs uplaoded:\", (j + 1) * MAX_BULK_SIZE)\n",
"\n",
" except Exception as e:\n",
" print(\"exception:\", str(e))"
@@ -705,11 +724,9 @@
"source": [
"query = \"English speaking countries\"\n",
"semantic_search_results = client.search(\n",
- " index=\"hf-semantic-text-index\",\n",
- " query={\"semantic\": {\"field\": \"infer_field\", \"query\": query}},\n",
- ")\n",
- "\n",
- " "
+ " index=\"hf-semantic-text-index\",\n",
+ " query={\"semantic\": {\"field\": \"infer_field\", \"query\": query}},\n",
+ ")"
]
},
{
@@ -795,17 +812,14 @@
" task_type=\"rerank\",\n",
" inference_id=\"my_cohere_rerank_endpoint\",\n",
" body={\n",
- " \"service\": \"cohere\",\n",
- " \"service_settings\": {\n",
+ " \"service\": \"cohere\",\n",
+ " \"service_settings\": {\n",
" \"api_key\": \"h2OzeuORCdvJ8eidGYbHmjfeWcecRQN8MYGDHxK1\",\n",
- " \"model_id\": \"rerank-english-v3.0\"\n",
- " },\n",
- " \"task_settings\": {\n",
- " \"top_n\": 100,\n",
- " \"return_documents\": True\n",
- " }\n",
- " }\n",
- ")\n"
+ " \"model_id\": \"rerank-english-v3.0\",\n",
+ " },\n",
+ " \"task_settings\": {\"top_n\": 100, \"return_documents\": True},\n",
+ " },\n",
+ ")"
]
},
{
@@ -817,25 +831,20 @@
"source": [
"reranked_search_results = client.search(\n",
" index=\"hf-semantic-text-index\",\n",
- " retriever= {\n",
+ " retriever={\n",
" \"text_similarity_reranker\": {\n",
- " \"retriever\": {\n",
- " \"standard\": {\n",
- " \"query\": {\n",
- " \"semantic\": {\n",
- " \"field\": \"infer_field\",\n",
- " \"query\": query \n",
- " }\n",
+ " \"retriever\": {\n",
+ " \"standard\": {\n",
+ " \"query\": {\"semantic\": {\"field\": \"infer_field\", \"query\": query}}\n",
" }\n",
- " }\n",
- " },\n",
- " \"field\": \"text_field\",\n",
- " \"inference_id\": \"my_cohere_rerank_endpoint\",\n",
- " \"inference_text\": query,\n",
- " \"rank_window_size\": 100,\n",
+ " },\n",
+ " \"field\": \"text_field\",\n",
+ " \"inference_id\": \"my_cohere_rerank_endpoint\",\n",
+ " \"inference_text\": query,\n",
+ " \"rank_window_size\": 100,\n",
" }\n",
- " }\n",
- ")\n"
+ " },\n",
+ ")"
]
},
{
From 0473f0538ed1f9defe4736ef921dc0979250605a Mon Sep 17 00:00:00 2001
From: Max Hniebergall
Date: Wed, 14 Aug 2024 16:47:23 -0400
Subject: [PATCH 4/5] Updated cohere notebook for video
---
...d-cohere-elasticsearch-inference-api.ipynb | 495 ++++++++++++++++++
1 file changed, 495 insertions(+)
create mode 100644 notebooks/integrations/cohere/updated-cohere-elasticsearch-inference-api.ipynb
diff --git a/notebooks/integrations/cohere/updated-cohere-elasticsearch-inference-api.ipynb b/notebooks/integrations/cohere/updated-cohere-elasticsearch-inference-api.ipynb
new file mode 100644
index 00000000..9c7fbd5c
--- /dev/null
+++ b/notebooks/integrations/cohere/updated-cohere-elasticsearch-inference-api.ipynb
@@ -0,0 +1,495 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "68a13ecd",
+ "metadata": {},
+ "source": [
+ "# Tutorial: Using Cohere with the elastic open inference API\n",
+ "\n",
+ "## TODO [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)]()\n",
+ "\n",
+ "This tutorial shows you how to compute embeddings with\n",
+ "Cohere using the elastic open inference API and store them for efficient vector or hybrid\n",
+ "search in Elasticsearch. This tutorial uses the Python Elasticsearch client\n",
+ "to perform the operations.\n",
+ "\n",
+ "You'll learn how to:\n",
+ "* create an inference endpoint for text embedding using the Cohere service,\n",
+ "* create the necessary index mapping for the Elasticsearch index using semantic search,\n",
+ "* rerank with retrievers using Cohere's rerank model,\n",
+ "* design a RAG system with Cohere's Chat API.\n",
+ "\n",
+ "The tutorial uses the [gqd/fictional-characters](https://huggingface.co/datasets/gqd/fictional-characters/blob/main/heros.json) data set.\n",
+ "\n",
+ "Refer to [Cohere's tutorial](https://docs.cohere.com/docs/elasticsearch-and-cohere) for an example using a different data set."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7f068b1b",
+ "metadata": {},
+ "source": [
+ "## 🧰 Requirements\n",
+ "\n",
+ "For this example, you will need:\n",
+ "\n",
+ "- An Elastic deployment:\n",
+ " - We'll be using [Elastic serverless](https://www.elastic.co/docs/current/serverless) for this example (available with a [free trial](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook))\n",
+ " \n",
+ "- A paid [Cohere account](https://cohere.com/) is required to use the Open Inference API with \n",
+ "the Cohere service as the Cohere free trial API usage is limited.\n",
+ "\n",
+ "- Python 3.7+.\n",
+ "\n",
+ "- the 8.15+ python elasticsearch client"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "aac02d37",
+ "metadata": {},
+ "source": [
+ "## Install and import required packages\n",
+ "\n",
+ "Install Elasticsearch and Cohere:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "50940b4d",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: elasticsearch==8.15 in /Users/mh/.venv/lib/python3.11/site-packages (8.15.0)\n",
+ "Requirement already satisfied: elastic-transport<9,>=8.13 in /Users/mh/.venv/lib/python3.11/site-packages (from elasticsearch==8.15) (8.13.1)\n",
+ "Requirement already satisfied: urllib3<3,>=1.26.2 in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch==8.15) (2.1.0)\n",
+ "Requirement already satisfied: certifi in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch==8.15) (2023.11.17)\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.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.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 elasticsearch==8.15"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3b24d95b",
+ "metadata": {},
+ "source": [
+ "Import the required packages:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "1a5a5eeb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from elasticsearch import Elasticsearch, helpers\n",
+ "import json\n",
+ "import requests\n",
+ "from getpass import getpass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3d0dacc5",
+ "metadata": {},
+ "source": [
+ "## Create an Elasticsearch client"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "74841401",
+ "metadata": {},
+ "source": [
+ "Now you can instantiate the Python Elasticsearch client.\n",
+ "\n",
+ "First provide your password and Cloud ID.\n",
+ "Then create a `client` object that instantiates an instance of the `Elasticsearch` class."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "76394974",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "TypeError",
+ "evalue": "Elasticsearch.__init__() got an unexpected keyword argument 'endpoint'",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[13], line 6\u001b[0m\n\u001b[1;32m 3\u001b[0m ELASTIC_API_KEY \u001b[38;5;241m=\u001b[39m getpass(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mElastic API key: \u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5\u001b[0m \u001b[38;5;66;03m# Create the client instance\u001b[39;00m\n\u001b[0;32m----> 6\u001b[0m client \u001b[38;5;241m=\u001b[39m \u001b[43mElasticsearch\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 7\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# For local development\u001b[39;49;00m\n\u001b[1;32m 8\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# hosts=[\"http://localhost:9200\"]\u001b[39;49;00m\n\u001b[1;32m 9\u001b[0m \u001b[43m \u001b[49m\u001b[43mendpoint\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mELASTIC_SERVERLESS_ENDPOINT\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 10\u001b[0m \u001b[43m \u001b[49m\u001b[43mapi_key\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mELASTIC_API_KEY\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 11\u001b[0m \u001b[43m)\u001b[49m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;66;03m# Confirm the client has connected\u001b[39;00m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28mprint\u001b[39m(client\u001b[38;5;241m.\u001b[39minfo())\n",
+ "\u001b[0;31mTypeError\u001b[0m: Elasticsearch.__init__() got an unexpected keyword argument 'endpoint'"
+ ]
+ }
+ ],
+ "source": [
+ "ELASTIC_SERVERLESS_ENDPOINT = getpass(\"Elastic serverless endpoint: \")\n",
+ "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key\n",
+ "ELASTIC_API_KEY = getpass(\"Elastic API key: \")\n",
+ "\n",
+ "# Create the client instance\n",
+ "client = Elasticsearch(\n",
+ " # For local development\n",
+ " # hosts=[\"http://localhost:9200\"]\n",
+ " endpoint=ELASTIC_SERVERLESS_ENDPOINT,\n",
+ " api_key=ELASTIC_API_KEY,\n",
+ ")\n",
+ "\n",
+ "# Confirm the client has connected\n",
+ "print(client.info())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3d29b809",
+ "metadata": {},
+ "source": [
+ "## Create the inference endpoints\n",
+ "\n",
+ "Create the inference endpoint first. In this example, the inference endpoint \n",
+ "uses Cohere's `embed-english-v3.0` model and the `embedding_type` is set to\n",
+ "`byte`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ae8ae88c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "COHERE_API_KEY = getpass(\"Cohere API key: \")\n",
+ "co = cohere.Client(api_key=COHERE_API_KEY)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6e524ce4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.inference.put_model(\n",
+ " task_type=\"text_embedding\",\n",
+ " inference_id=\"cohere_embeddings\",\n",
+ " body={\n",
+ " \"service\": \"cohere\",\n",
+ " \"service_settings\": {\n",
+ " \"api_key\": COHERE_API_KEY,\n",
+ " \"model_id\": \"embed-english-v3.0\",\n",
+ " \"embedding_type\": \"byte\",\n",
+ " },\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "884e424d",
+ "metadata": {},
+ "source": [
+ "You can find your API keys in your Cohere dashboard under the\n",
+ "[API keys section](https://dashboard.cohere.com/api-keys)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "178c32db",
+ "metadata": {},
+ "source": [
+ "## Create the index mapping\n",
+ "\n",
+ "Create the index mapping for the index that will contain the embeddings."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "35ab26b2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.indices.create(\n",
+ " index=\"characters-index\",\n",
+ " mappings={\n",
+ " \"properties\": {\n",
+ " \"infer_field\": {\n",
+ " \"type\": \"semantic_text\",\n",
+ " \"inference_id\": \"cohere_embeddings\",\n",
+ " },\n",
+ " \"title\": {\"type\": \"text\", \"copy_to\": \"infer_field\"},\n",
+ " }\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9c5e1e7b",
+ "metadata": {},
+ "source": [
+ "## Prepare data and insert documents\n",
+ "\n",
+ "This example uses the [SciFact](https://huggingface.co/datasets/mteb/scifact) data\n",
+ "set that you can find on HuggingFace."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "c71b8367",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'title': '\"Eat\" Owner', 'parsed': {'image': 'Vlcsnap-2016-09-06-05h31m34s50.png', 'origin': \"''Wander Over Yonder''\", 'fullname': 'Unknown', 'alias': '\"Eat\" Owner', 'occupation': 'Restaurant owner', 'skills': 'Management skills', 'goals': 'Manage his restaurant.
\\nHelp the rebels defeat Lord Dominator.
', 'hobby': '', 'family': 'Unknown', 'friends': 'Michelle', 'enemies': 'Lord Dominator', 'type of hero': 'Alien'}, 'origin': \"''Wander Over Yonder''\"}\n"
+ ]
+ },
+ {
+ "ename": "NameError",
+ "evalue": "name 'client' is not defined",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[19], line 24\u001b[0m\n\u001b[1;32m 16\u001b[0m documents\u001b[38;5;241m.\u001b[39mappend(\n\u001b[1;32m 17\u001b[0m {\n\u001b[1;32m 18\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m_index\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcohere-embeddings\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 19\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m_source\u001b[39m\u001b[38;5;124m\"\u001b[39m: data_dict,\n\u001b[1;32m 20\u001b[0m }\n\u001b[1;32m 21\u001b[0m )\n\u001b[1;32m 23\u001b[0m \u001b[38;5;66;03m# Use the bulk endpoint to index\u001b[39;00m\n\u001b[0;32m---> 24\u001b[0m helpers\u001b[38;5;241m.\u001b[39mbulk(\u001b[43mclient\u001b[49m, documents)\n\u001b[1;32m 26\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mData ingestion completed, text embeddings generated!\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
+ "\u001b[0;31mNameError\u001b[0m: name 'client' is not defined"
+ ]
+ }
+ ],
+ "source": [
+ "url = \"https://huggingface.co/datasets/gqd/fictional-characters/resolve/main/heros.json\"\n",
+ "\n",
+ "# Fetch the JSONL data from the URL\n",
+ "response = requests.get(url)\n",
+ "response.raise_for_status() # Ensure we notice bad responses\n",
+ "\n",
+ "# Split the content by new lines and parse each line as JSON\n",
+ "data = json.loads(response.text)\n",
+ "\n",
+ "print(data[0])\n",
+ "\n",
+ "# Prepare the documents to be indexed\n",
+ "documents = []\n",
+ "for line in data:\n",
+ " data_dict = line\n",
+ " documents.append(\n",
+ " {\n",
+ " \"_index\": \"characters-index\",\n",
+ " \"_source\": data_dict,\n",
+ " }\n",
+ " )\n",
+ "\n",
+ "# Use the bulk endpoint to index\n",
+ "helpers.bulk(client, documents)\n",
+ "\n",
+ "print(\"Data ingestion completed, text embeddings generated!\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9332078a",
+ "metadata": {},
+ "source": [
+ "Your index is populated with the SciFact data and text embeddings for the text\n",
+ "field."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "153ce2d5",
+ "metadata": {},
+ "source": [
+ "## Add the rerank inference endpoint\n",
+ "\n",
+ "To combine the results more effectively, use \n",
+ "[Cohere's Rerank v3](https://docs.cohere.com/docs/rerank-2) model through the\n",
+ "inference API to provide a more precise semantic reranking of the results.\n",
+ "\n",
+ "Create an inference endpoint with your Cohere API key and the used model name as\n",
+ "the `model_id` (`rerank-english-v3.0` in this example)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "886c4d15",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.inference.put_model(\n",
+ " task_type=\"rerank\",\n",
+ " inference_id=\"cohere_rerank\",\n",
+ " body={\n",
+ " \"service\": \"cohere\",\n",
+ " \"service_settings\": {\n",
+ " \"api_key\": COHERE_API_KEY,\n",
+ " \"model_id\": \"rerank-english-v3.0\",\n",
+ " },\n",
+ " \"task_settings\": {\n",
+ " \"top_n\": 100,\n",
+ " },\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ebf82d4a",
+ "metadata": {},
+ "source": [
+ "Rerank the results using the new inference endpoint."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "610035af",
+ "metadata": {},
+ "source": [
+ "## Semantic search with reranking\n",
+ "\n",
+ "Let's start querying the index!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ab735ef0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def semantic_search_with_reranking(query):\n",
+ " return client.search(\n",
+ " index=\"characters-index\",\n",
+ " retriever={\n",
+ " \"text_similarity_reranker\": {\n",
+ " \"retriever\": {\n",
+ " \"standard\": {\n",
+ " \"query\": {\"semantic\": {\"field\": \"infer_field\", \"query\": query}}\n",
+ " }\n",
+ " },\n",
+ " \"field\": \"titles\",\n",
+ " \"inference_id\": \"my_cohere_rerank_endpoint\",\n",
+ " \"inference_text\": query,\n",
+ " \"rank_window_size\": 100,\n",
+ " }\n",
+ " },\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3f0fc7e1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pretty_print_characters(\n",
+ " semantic_search_with_reranking(\"Who are the stars of the Harry Potter series?\")\n",
+ ")\n",
+ "pretty_print_characters(\n",
+ " semantic_search_with_reranking(\"Who are the hobbits in the lord of the rings?\")\n",
+ ")\n",
+ "pretty_print_characters(\n",
+ " semantic_search_with_reranking(\"Who are the hobbits in the hobbit?\")\n",
+ ")\n",
+ "pretty_print_characters(\n",
+ " semantic_search_with_reranking(\"Who are the stars of star wars\")\n",
+ ")\n",
+ "pretty_print_characters(semantic_search_with_reranking(\"Who is the coolest character?\"))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9539ff47",
+ "metadata": {},
+ "source": [
+ "## Retrieval Augmented Generation (RAG) with Cohere and Elasticsearch\n",
+ "\n",
+ "RAG is a method for generating text using additional information fetched from an\n",
+ "external data source. With the ranked results, you can build a RAG system on\n",
+ "top of what you created with \n",
+ "[Cohere's Chat API](https://docs.cohere.com/docs/chat-api).\n",
+ "\n",
+ "Pass in the retrieved documents and the query to receive a grounded response\n",
+ "using Cohere's newest generative model \n",
+ "[Command R+](https://docs.cohere.com/docs/command-r-plus).\n",
+ "\n",
+ "Then pass in the query and the documents to the Chat API, and print out the\n",
+ "response."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "id": "39badebf",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Query: What is biosimilarity?\n",
+ "Response: Biosimilarity is based on the comparability concept, which has been used successfully for several decades to ensure close similarity of a biological product before and after a manufacturing change. Over the last 10 years, experience with biosimilars has shown that even complex biotechnology-derived proteins can be copied successfully.\n",
+ "Sources:\n",
+ "Interchangeability of Biosimilars: A European Perspective: Many of the best-selling ‘blockbuster’ biological medicinal products are, or will soon be, facing competition from similar biological medicinal products (biosimilars) in the EU. Biosimilarity is based on the comparability concept, which has been used successfully for several decades to ensure close similarity of a biological product before and after a manufacturing change. Over the last 10 years, experience with biosimilars has shown that even complex biotechnology-derived proteins can be copied successfully. Most best-selling biologicals are used for chronic treatment. This has triggered intensive discussion on the interchangeability of a biosimilar with its reference product, with the main concern being immunogenicity. We explore the theoretical basis of the presumed risks of switching between a biosimilar and its reference product and the available data on switches. Our conclusion is that a switch between comparable versions of the same active substance approved in accordance with EU legislation is not expected to trigger or enhance immunogenicity. On the basis of current knowledge, it is unlikely and very difficult to substantiate that two products, comparable on a population level, would have different safety or efficacy in individual patients upon a switch. Our conclusion is that biosimilars licensed in the EU are interchangeable.\n"
+ ]
+ }
+ ],
+ "source": [
+ "response = co.chat(message=query, documents=ranked_documents, model=\"command-r-plus\")\n",
+ "\n",
+ "source_documents = []\n",
+ "for citation in response.citations:\n",
+ " for document_id in citation.document_ids:\n",
+ " if document_id not in source_documents:\n",
+ " source_documents.append(document_id)\n",
+ "\n",
+ "print(f\"Query: {query}\")\n",
+ "print(f\"Response: {response.text}\")\n",
+ "print(\"Sources:\")\n",
+ "for document in response.documents:\n",
+ " if document[\"id\"] in source_documents:\n",
+ " print(f\"{document['title']}: {document['text']}\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": ".venv",
+ "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"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
From 7ea3971cfda4cd3ae8753b7bfed0e01f5553adf3 Mon Sep 17 00:00:00 2001
From: Max Hniebergall
Date: Tue, 20 Aug 2024 16:06:10 -0400
Subject: [PATCH 5/5] Update notebook to use in-cluster reranking instead of
cohere
---
...s-of-documents-with-cohere-reranking.ipynb | 938 ------------------
...millions-of-documents-with-reranking.ipynb | 579 +++++++++++
2 files changed, 579 insertions(+), 938 deletions(-)
delete mode 100644 notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb
create mode 100644 notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-reranking.ipynb
diff --git a/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb
deleted file mode 100644
index 1b38484a..00000000
--- a/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-cohere-reranking.ipynb
+++ /dev/null
@@ -1,938 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "7a765629",
- "metadata": {},
- "source": [
- "# Semantic Search using the Inference API with the Hugging Face Inference Endpoints Service\n",
- "\n",
- "TODO [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/elastic/elasticsearch-labs/blob/main/notebooks/search/TODO)\n",
- "\n",
- "\n",
- "Learn how to use the [Inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-apis.html) with the Hugging Face Inference Endpoint service for semantic search."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f9101eb9",
- "metadata": {},
- "source": [
- "# 🧰 Requirements\n",
- "\n",
- "For this example, you will need:\n",
- "\n",
- "- An Elastic deployment:\n",
- " - We'll be using [Elastic serverless](https://www.elastic.co/docs/current/serverless) for this example (available with a [free trial](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook))\n",
- "\n",
- "- Elasticsearch 8.14 or above.\n",
- " \n",
- "- A paid [Hugging Face Inference Endpoint](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) is required to use the Inference API with \n",
- "the Hugging Face Inference Endpoint service."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "4cd69cc0",
- "metadata": {},
- "source": [
- "# Create Elastic Cloud deployment or serverless project\n",
- "\n",
- "If you don't have an Elastic Cloud deployment, sign up [here](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook) for a free trial."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f27dffbf",
- "metadata": {},
- "source": [
- "# Install packages and connect with Elasticsearch Client\n",
- "\n",
- "To get started, we'll need to connect to our Elastic deployment using the Python client (version 8.12.0 or above).\n",
- "Because we're using an Elastic Cloud deployment, we'll use the **Cloud ID** to identify our deployment.\n",
- "\n",
- "First we need to `pip` install the following packages:\n",
- "\n",
- "- `elasticsearch`"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "8c4b16bc",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Requirement already satisfied: elasticsearch in /Users/mh/.venv/lib/python3.11/site-packages (8.14.0)\n",
- "Requirement already satisfied: elastic-transport<9,>=8.13 in /Users/mh/.venv/lib/python3.11/site-packages (from elasticsearch) (8.13.1)\n",
- "Requirement already satisfied: urllib3<3,>=1.26.2 in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch) (2.1.0)\n",
- "Requirement already satisfied: certifi in /Users/mh/.venv/lib/python3.11/site-packages (from elastic-transport<9,>=8.13->elasticsearch) (2023.11.17)\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.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.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",
- "Requirement already satisfied: datasets in /Users/mh/.venv/lib/python3.11/site-packages (2.20.0)\n",
- "Requirement already satisfied: filelock in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.13.1)\n",
- "Requirement already satisfied: numpy>=1.17 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (1.23.5)\n",
- "Requirement already satisfied: pyarrow>=15.0.0 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (17.0.0)\n",
- "Requirement already satisfied: pyarrow-hotfix in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.6)\n",
- "Requirement already satisfied: dill<0.3.9,>=0.3.0 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.3.8)\n",
- "Requirement already satisfied: pandas in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (1.5.3)\n",
- "Requirement already satisfied: requests>=2.32.2 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (2.32.3)\n",
- "Requirement already satisfied: tqdm>=4.66.3 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (4.66.4)\n",
- "Requirement already satisfied: xxhash in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.4.1)\n",
- "Requirement already satisfied: multiprocess in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.70.16)\n",
- "Requirement already satisfied: fsspec<=2024.5.0,>=2023.1.0 in /Users/mh/.venv/lib/python3.11/site-packages (from fsspec[http]<=2024.5.0,>=2023.1.0->datasets) (2024.5.0)\n",
- "Requirement already satisfied: aiohttp in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (3.9.5)\n",
- "Requirement already satisfied: huggingface-hub>=0.21.2 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (0.24.2)\n",
- "Requirement already satisfied: packaging in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (23.2)\n",
- "Requirement already satisfied: pyyaml>=5.1 in /Users/mh/.venv/lib/python3.11/site-packages (from datasets) (6.0.1)\n",
- "Requirement already satisfied: aiosignal>=1.1.2 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.3.1)\n",
- "Requirement already satisfied: attrs>=17.3.0 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (23.2.0)\n",
- "Requirement already satisfied: frozenlist>=1.1.1 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.4.1)\n",
- "Requirement already satisfied: multidict<7.0,>=4.5 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (6.0.5)\n",
- "Requirement already satisfied: yarl<2.0,>=1.0 in /Users/mh/.venv/lib/python3.11/site-packages (from aiohttp->datasets) (1.9.4)\n",
- "Requirement already satisfied: typing-extensions>=3.7.4.3 in /Users/mh/.venv/lib/python3.11/site-packages (from huggingface-hub>=0.21.2->datasets) (4.12.2)\n",
- "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (3.3.2)\n",
- "Requirement already satisfied: idna<4,>=2.5 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (3.6)\n",
- "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (2.1.0)\n",
- "Requirement already satisfied: certifi>=2017.4.17 in /Users/mh/.venv/lib/python3.11/site-packages (from requests>=2.32.2->datasets) (2023.11.17)\n",
- "Requirement already satisfied: python-dateutil>=2.8.1 in /Users/mh/.venv/lib/python3.11/site-packages (from pandas->datasets) (2.8.2)\n",
- "Requirement already satisfied: pytz>=2020.1 in /Users/mh/.venv/lib/python3.11/site-packages (from pandas->datasets) (2023.3.post1)\n",
- "Requirement already satisfied: six>=1.5 in /Users/mh/.venv/lib/python3.11/site-packages (from python-dateutil>=2.8.1->pandas->datasets) (1.16.0)\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.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.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",
- "Note: you may need to restart the kernel to use updated packages.\n"
- ]
- }
- ],
- "source": [
- "!pip install elasticsearch\n",
- "%pip install datasets"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "41ef96b3",
- "metadata": {},
- "source": [
- "Next, we need to import the modules we need. 🔐 NOTE: getpass enables us to securely prompt the user for credentials without echoing them to the terminal, or storing it in memory."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "690ff9af",
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "/Users/mh/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
- " from .autonotebook import tqdm as notebook_tqdm\n"
- ]
- }
- ],
- "source": [
- "from elasticsearch import Elasticsearch, helpers\n",
- "from getpass import getpass\n",
- "import datasets"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "23fa2b6c",
- "metadata": {},
- "source": [
- "Now we can instantiate the Python Elasticsearch client.\n",
- "\n",
- "First we prompt the user for their password and Cloud ID.\n",
- "Then we create a `client` object that instantiates an instance of the `Elasticsearch` class."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "195cc597",
- "metadata": {},
- "outputs": [],
- "source": [
- "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id\n",
- "ELASTIC_CLOUD_ID = getpass(\"Elastic Cloud ID: \")\n",
- "\n",
- "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key\n",
- "ELASTIC_API_KEY = getpass(\"Elastic Api Key: \")\n",
- "\n",
- "# Create the client instance\n",
- "client = Elasticsearch(\n",
- " # For local development\n",
- " # hosts=[\"http://localhost:9200\"]\n",
- " cloud_id=ELASTIC_CLOUD_ID,\n",
- " api_key=ELASTIC_API_KEY,\n",
- " request_timeout=120,\n",
- " max_retries=10,\n",
- " retry_on_timeout=True,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b1115ffb",
- "metadata": {},
- "source": [
- "### Test the Client\n",
- "Before you continue, confirm that the client has connected with this test."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "id": "cc0de5ea",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'name': 'serverless', 'cluster_name': 'eccdee94244a41ad8c9e7788576116e0', 'cluster_uuid': 'nNzsd294QbWzcXy9m0kkcQ', 'version': {'number': '8.11.0', 'build_flavor': 'serverless', 'build_type': 'docker', 'build_hash': '00000000', 'build_date': '2023-10-31', 'build_snapshot': False, 'lucene_version': '9.7.0', 'minimum_wire_compatibility_version': '8.11.0', 'minimum_index_compatibility_version': '8.11.0'}, 'tagline': 'You Know, for Search'}\n"
- ]
- }
- ],
- "source": [
- "print(client.info())\n",
- "\n",
- "\n",
- "# define this now so we can use it later\n",
- "def pretty_search_response(response):\n",
- " if len(response[\"hits\"][\"hits\"]) == 0:\n",
- " print(\"Your search returned no results.\")\n",
- " else:\n",
- " for hit in response[\"hits\"][\"hits\"]:\n",
- " id = hit[\"_id\"]\n",
- " score = hit[\"_score\"]\n",
- " text = hit[\"_source\"][\"text_field\"]\n",
- "\n",
- " pretty_output = f\"\\nID: {id}\\nScore: {score}\\nText: {text}\"\n",
- "\n",
- " print(pretty_output)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "659c5890",
- "metadata": {},
- "source": [
- "Refer to [the documentation](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect to a self-managed deployment.\n",
- "\n",
- "Read [this page](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect using API keys."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "840d92f0",
- "metadata": {},
- "source": [
- "\n",
- "## Create the inference endpoint object\n",
- "\n",
- "Let's create the inference endpoint by using the [Create inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html).\n",
- "\n",
- "You'll need an Hugging Face API key (access token) for this that you can find in your Hugging Face account under the [Access Tokens](https://huggingface.co/settings/tokens).\n",
- "\n",
- "You will also need to have created a [Hugging Face Inference Endpoint service instance](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) and noted the `url` of your instance. For this notebook, we deployed the `multilingual-e5-small` model."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "0d007737",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "ObjectApiResponse({'model_id': 'my_hf_endpoint_object', 'inference_id': 'my_hf_endpoint_object', 'task_type': 'text_embedding', 'service': 'hugging_face', 'service_settings': {'url': 'https://yb0j0ol2xzvro0oc.us-east-1.aws.endpoints.huggingface.cloud', 'similarity': 'dot_product', 'dimensions': 384, 'rate_limit': {'requests_per_minute': 3000}}, 'task_settings': {}})"
- ]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "API_KEY = getpass(\"Huggingface API key: \")\n",
- "client.inference.put_model(\n",
- " inference_id=\"my_hf_endpoint_object\",\n",
- " body={\n",
- " \"service\": \"hugging_face\",\n",
- " \"service_settings\": {\n",
- " \"api_key\": API_KEY,\n",
- " \"url\": \"https://yb0j0ol2xzvro0oc.us-east-1.aws.endpoints.huggingface.cloud\",\n",
- " \"similarity\": \"dot_product\",\n",
- " },\n",
- " },\n",
- " task_type=\"text_embedding\",\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "67f4201d",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "ObjectApiResponse({'text_embedding': [{'embedding': [0.026027203, -0.011120652, -0.048804738, -0.108695105, 0.06134937, -0.003066093, 0.053232085, 0.103629395, 0.046043355, 0.0055427994, 0.036174323, 0.022110537, 0.084891565, -0.008215214, -0.017915571, 0.041923355, 0.048264034, -0.0404355, -0.02609504, -0.023076748, 0.0077286777, 0.023034474, 0.010379155, 0.06257496, 0.025658935, 0.040398516, -0.059809092, 0.032451782, 0.020798752, -0.053219322, -0.0447653, -0.033474423, 0.085040554, -0.051343303, 0.081006914, 0.026895791, -0.031822708, -0.06217641, 0.069435075, -0.055062667, -0.014967285, -0.0040517864, 0.03874908, 0.07854211, 0.017526977, 0.040629108, -0.023190023, 0.056913305, -0.06422566, -0.009403182, -0.06666503, 0.035270344, 0.004515737, 0.07347306, 0.011125566, -0.07184689, -0.08095445, -0.04214626, -0.108447045, -0.019494658, 0.06303337, 0.019757038, -0.014584281, 0.060923614, 0.06465893, 0.108431116, 0.04072316, 0.03705652, -0.06975359, -0.050562095, -0.058487326, 0.05989619, 0.008454561, -0.02706363, -0.017974045, 0.030698266, 0.046484154, -0.06212431, 0.009513307, -0.056369964, -0.052940592, -0.05834985, -0.02096531, 0.03910419, -0.054484386, 0.06231919, 0.044607673, -0.064030685, 0.067746714, -0.0291515, 0.06992093, 0.06300958, -0.07530936, -0.06167211, -0.0681666, -0.042375665, -0.05200085, 0.058336657, 0.039630838, -0.03444309, 0.030615594, -0.042388055, 0.03127304, -0.059075136, -0.05925558, 0.019864058, 0.0311022, -0.11285156, 0.02264027, -0.0676216, 0.011842404, -0.0157365, 0.06580391, 0.023665493, -0.05072435, -0.039492164, -0.06390325, -0.067074455, 0.032680944, -0.05243909, 0.06721114, -0.005195616, -0.0458316, -0.046202496, -0.07942237, -0.011754681, 0.026515028, 0.04761297, 0.08130492, 0.0118014645, 0.025956452, 0.039976373, 0.050196614, 0.052609406, 0.063223615, 0.06121741, -0.028745022, 0.0008677591, 0.038760003, -0.021240402, -0.073974326, 0.0548761, -0.047403768, 0.025582938, 0.0585596, 0.056284837, 0.08381001, -0.02149303, 0.09447917, -0.04940235, 0.018470071, -0.044996567, 0.08062048, 0.05162519, 0.053831138, -0.052980945, -0.08226773, -0.068137355, 0.028439872, 0.049932946, -0.07633764, -0.08649836, -0.07108301, 0.017650153, -0.065348, -0.038191773, 0.040068675, 0.05870959, -0.04707911, -0.04340612, -0.044621766, 0.030800574, -0.042227603, 0.0604754, 0.010891958, 0.057460006, -0.046362966, 0.046009373, 0.07293652, 0.09398854, -0.017035728, -0.010618687, -0.09326647, -0.03877647, -0.026517635, -0.047411792, -0.073266074, 0.033911563, 0.0642687, -0.02208107, 0.0040624263, -0.003194478, -0.082016475, -0.088730805, -0.084694624, -0.03364641, -0.05026475, 0.051665384, 0.058177516, 0.02759865, -0.034461632, 0.0027396793, 0.013807217, 0.040009033, 0.06346369, 0.05832441, -0.07451158, 0.028601868, -0.022494016, 0.04229324, 0.027883757, -0.0673137, -0.07119014, 0.047188714, -0.033077974, -0.028302893, -0.028704679, 0.043902606, -0.05147592, 0.045782477, 0.08077521, -0.01782404, 0.0242885, -0.0711172, -0.023565968, 0.041291755, 0.084907316, -0.101972945, -0.038989857, 0.025122978, -0.014144972, -0.010975231, -0.0357049, -0.09243826, -0.023552464, -0.08525497, -0.018912667, 0.049455214, 0.06532829, -0.031223357, -0.013451132, -0.00037671064, 0.04600707, -0.057603396, 0.08035837, -0.026429964, -0.0962299, 0.022606302, -0.0116137, 0.062264528, 0.033446472, -0.06123555, -0.09909991, -0.07459225, -0.018707436, 0.028753517, 0.06808565, 0.023965191, -0.04717076, 0.026551146, 0.019655682, -0.009233348, 0.10465723, 0.046420176, 0.03295103, 0.053024694, -0.03854051, -0.0058735567, -0.061238136, -0.048678573, -0.05362055, 0.048028357, 0.003013557, -0.06505121, -0.020536456, -0.020093206, 0.014102229, 0.10254222, -0.027084326, -0.061477777, 0.03478813, -0.00029115603, 0.053552967, 0.056773122, 0.048566766, 0.027371235, -0.015398839, 0.0511229, -0.03932426, -0.043879736, -0.03872225, -0.08171432, 0.01703992, -0.04535995, 0.03194781, 0.011413799, 0.036786903, 0.021306055, -0.06722324, 0.034231987, -0.027529748, -0.059552487, 0.050244797, 0.08905617, -0.071323626, 0.05047076, 0.003429174, 0.034673557, 0.009984501, 0.056842286, 0.0683513, 0.023990847, -0.04053898, -0.022724004, 0.026175855, 0.027319307, -0.055451974, -0.053907238, -0.05359307, -0.035025068, -0.03776361, -0.02973751, -0.037610233, -0.051089168, 0.04428633, 0.06276192, -0.03754498, -0.060270913, 0.043127347, 0.016669549, 0.024885416, -0.027190097, -0.011614101, 0.077848606, -0.007924398, -0.061833344, -0.015071012, 0.023127502, -0.07634841, -0.015780756, 0.031652045, 0.0031123296, -0.032643825, 0.05640234, -0.02685534, -0.04942714, 0.048498664, 0.00043902535, -0.043975227, 0.017389799, 0.07734344, -0.090009265, 0.019997133, 0.10055134, -0.05671741, 0.048755262, -0.02514076, -0.011394784, 0.049053214, 0.04264309, -0.06451125, -0.029034287, 0.07762039, 0.06809162, 0.059983794, 0.035379365, -0.007960272, 0.019705113, -0.02518122, -0.05767321, 0.038523413, 0.081652805, -0.032829504, -0.0023197657, -0.018218426, -0.0885769, -0.094963886, 0.057851806, -0.041729856, -0.045802936, 0.0570079, 0.047811687, 0.017810043, 0.09373594]}]})"
- ]
- },
- "execution_count": 6,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "client.inference.inference(\n",
- " inference_id=\"my_hf_endpoint_object\", input=\"this is the raw text of my document!\"\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "1f2e48b7",
- "metadata": {},
- "source": [
- "**IMPORTANT:** If you use Elasticsearch 8.12, you must change `inference_id` in the snippet above to `model_id`! "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0346151d",
- "metadata": {},
- "source": [
- "#"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "1024d070",
- "metadata": {},
- "source": [
- "## Create an ingest pipeline with an inference processor\n",
- "\n",
- "Create an ingest pipeline with an inference processor by using the [`put_pipeline`](https://www.elastic.co/guide/en/elasticsearch/reference/master/put-pipeline-api.html) method. Reference the `inference_id` created above as `model_id` to infer on the data that is being ingested by the pipeline."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "6ace9e2e",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "ObjectApiResponse({'acknowledged': True})"
- ]
- },
- "execution_count": 7,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "client.ingest.put_pipeline(\n",
- " id=\"hf_pipeline\",\n",
- " processors=[\n",
- " {\n",
- " \"inference\": {\n",
- " \"model_id\": \"my_hf_endpoint_object\",\n",
- " \"input_output\": {\n",
- " \"input_field\": \"text_field\",\n",
- " \"output_field\": \"text_embedding\",\n",
- " },\n",
- " }\n",
- " }\n",
- " ],\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "76d07567",
- "metadata": {},
- "source": [
- "Let's note a few important parameters from that API call:\n",
- "\n",
- "- `inference`: A processor that performs inference using a machine learning model.\n",
- "- `model_id`: Specifies the ID of the inference endpoint to be used. In this example, the inference ID is set to `my_hf_endpoint_object`. Use the inference ID you defined when created the inference task.\n",
- "- `input_output`: Specifies input and output fields.\n",
- "- `input_field`: Field name from which the `dense_vector` representation is created.\n",
- "- `output_field`: Field name which contains inference results. "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "28e12d7a",
- "metadata": {},
- "source": [
- "## Create index\n",
- "\n",
- "The mapping of the destination index - the index that contains the embeddings that the model will create based on your input text - must be created. The destination index must have a field with the [dense_vector](https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html) field type to index the output of the model we deployed in Hugging Face (`multilingual-e5-small`).\n",
- "\n",
- "Let's create an index named `hf-endpoint-index` with the mappings we need."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "id": "6ddcbca3",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "ObjectApiResponse({'acknowledged': True, 'shards_acknowledged': True, 'index': 'hf-endpoint-index'})"
- ]
- },
- "execution_count": 10,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "client.indices.create(\n",
- " index=\"hf-endpoint-index\",\n",
- " settings={\n",
- " \"index\": {\n",
- " \"default_pipeline\": \"hf_pipeline\",\n",
- " }\n",
- " },\n",
- " mappings={\n",
- " \"properties\": {\n",
- " \"text\": {\"type\": \"text\"},\n",
- " \"text_embedding\": {\n",
- " \"type\": \"dense_vector\",\n",
- " \"dims\": 384,\n",
- " \"similarity\": \"dot_product\",\n",
- " },\n",
- " }\n",
- " },\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "12aa3452",
- "metadata": {},
- "source": [
- "## If you are using Elasticsearch serverless or v8.15+ then you will have access to the new `semantic_text` field\n",
- "`semantic_text` has significantly faster ingest times and is recommended.\n",
- "\n",
- "https://github.com/elastic/elasticsearch/blob/main/docs/reference/mapping/types/semantic-text.asciidoc"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "id": "5eeb3755",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "ObjectApiResponse({'acknowledged': True, 'shards_acknowledged': True, 'index': 'hf-semantic-text-index'})"
- ]
- },
- "execution_count": 12,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "client.indices.create(\n",
- " index=\"hf-semantic-text-index\",\n",
- " mappings={\n",
- " \"properties\": {\n",
- " \"infer_field\": {\n",
- " \"type\": \"semantic_text\",\n",
- " \"inference_id\": \"my_hf_endpoint_object\",\n",
- " },\n",
- " \"text_field\": {\"type\": \"text\", \"copy_to\": \"infer_field\"},\n",
- " }\n",
- " },\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "07c187a9",
- "metadata": {},
- "source": [
- "## Insert Documents\n",
- "\n",
- "In this example, we want to show the power of using GPUs in Hugging Face's Inference Endpoint service by indexing millions of multilingual documents from the miracl corpus. The speed at which these documents ingest will depend on whether you use a semantic text field (faster) or an ingest pipeline (slower) and will also depend on how much hardware your rent for your Hugging Face inference endpoint. Using a semantic_text field with a single T4 GPU, it may take about 3 hours to index 1 million documents. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "id": "d68737cb",
- "metadata": {},
- "outputs": [],
- "source": [
- "langs = [\n",
- " \"ar\",\n",
- " \"bn\",\n",
- " \"en\",\n",
- " \"es\",\n",
- " \"fa\",\n",
- " \"fi\",\n",
- " \"fr\",\n",
- " \"hi\",\n",
- " \"id\",\n",
- " \"ja\",\n",
- " \"ko\",\n",
- " \"ru\",\n",
- " \"sw\",\n",
- " \"te\",\n",
- " \"th\",\n",
- " \"zh\",\n",
- "]\n",
- "\n",
- "\n",
- "all_langs_datasets = [\n",
- " iter(datasets.load_dataset(\"miracl/miracl-corpus\", lang)[\"train\"]) for lang in langs\n",
- "]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 48,
- "id": "7ccf9835",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Docs uplaoded: 0\n",
- "Docs uplaoded: 1000\n",
- "Docs uplaoded: 2000\n",
- "Docs uplaoded: 3000\n",
- "Docs uplaoded: 4000\n",
- "Docs uplaoded: 5000\n",
- "Docs uplaoded: 6000\n",
- "Docs uplaoded: 7000\n",
- "Docs uplaoded: 8000\n",
- "Docs uplaoded: 9000\n",
- "Docs uplaoded: 10000\n",
- "Docs uplaoded: 11000\n",
- "Docs uplaoded: 12000\n",
- "Docs uplaoded: 13000\n",
- "Docs uplaoded: 14000\n",
- "Docs uplaoded: 15000\n",
- "Docs uplaoded: 16000\n",
- "Docs uplaoded: 17000\n",
- "Docs uplaoded: 18000\n",
- "Docs uplaoded: 19000\n",
- "Docs uplaoded: 20000\n",
- "Docs uplaoded: 21000\n",
- "Docs uplaoded: 22000\n",
- "Docs uplaoded: 23000\n",
- "Docs uplaoded: 24000\n",
- "Docs uplaoded: 25000\n",
- "Docs uplaoded: 26000\n",
- "Docs uplaoded: 27000\n",
- "Docs uplaoded: 28000\n",
- "Docs uplaoded: 29000\n",
- "Docs uplaoded: 30000\n",
- "Docs uplaoded: 31000\n",
- "Docs uplaoded: 32000\n",
- "Docs uplaoded: 33000\n",
- "Docs uplaoded: 34000\n",
- "Docs uplaoded: 35000\n",
- "Docs uplaoded: 36000\n",
- "Docs uplaoded: 37000\n",
- "Docs uplaoded: 38000\n",
- "Docs uplaoded: 39000\n",
- "Docs uplaoded: 40000\n",
- "Docs uplaoded: 41000\n",
- "Docs uplaoded: 42000\n",
- "Docs uplaoded: 43000\n",
- "Docs uplaoded: 44000\n",
- "Docs uplaoded: 45000\n",
- "Docs uplaoded: 46000\n",
- "Docs uplaoded: 47000\n",
- "Docs uplaoded: 48000\n",
- "Docs uplaoded: 49000\n",
- "Docs uplaoded: 50000\n",
- "Docs uplaoded: 51000\n",
- "Docs uplaoded: 52000\n",
- "Docs uplaoded: 53000\n",
- "Docs uplaoded: 54000\n",
- "Docs uplaoded: 55000\n",
- "Docs uplaoded: 56000\n",
- "Docs uplaoded: 57000\n",
- "Docs uplaoded: 58000\n",
- "Docs uplaoded: 59000\n",
- "Docs uplaoded: 60000\n",
- "Docs uplaoded: 61000\n",
- "Docs uplaoded: 62000\n",
- "Docs uplaoded: 63000\n",
- "Docs uplaoded: 64000\n",
- "Docs uplaoded: 65000\n",
- "Docs uplaoded: 66000\n",
- "Docs uplaoded: 67000\n",
- "Docs uplaoded: 68000\n",
- "Docs uplaoded: 69000\n",
- "Docs uplaoded: 70000\n",
- "Docs uplaoded: 71000\n",
- "Docs uplaoded: 72000\n",
- "Docs uplaoded: 73000\n",
- "Docs uplaoded: 74000\n",
- "Docs uplaoded: 75000\n",
- "Docs uplaoded: 76000\n",
- "Docs uplaoded: 77000\n",
- "Docs uplaoded: 78000\n",
- "Docs uplaoded: 79000\n",
- "Docs uplaoded: 80000\n",
- "Docs uplaoded: 81000\n",
- "Docs uplaoded: 82000\n",
- "Docs uplaoded: 83000\n",
- "Docs uplaoded: 84000\n",
- "Docs uplaoded: 85000\n",
- "Docs uplaoded: 86000\n",
- "Docs uplaoded: 87000\n",
- "Docs uplaoded: 88000\n",
- "Docs uplaoded: 89000\n",
- "Docs uplaoded: 90000\n",
- "Docs uplaoded: 91000\n",
- "Docs uplaoded: 92000\n",
- "Docs uplaoded: 93000\n",
- "Docs uplaoded: 94000\n",
- "Docs uplaoded: 95000\n",
- "Docs uplaoded: 96000\n",
- "Docs uplaoded: 97000\n",
- "Docs uplaoded: 98000\n",
- "Docs uplaoded: 99000\n",
- "Docs uplaoded: 100000\n",
- "Docs uplaoded: 101000\n",
- "Docs uplaoded: 102000\n",
- "Docs uplaoded: 103000\n",
- "Docs uplaoded: 104000\n",
- "Docs uplaoded: 105000\n",
- "Docs uplaoded: 106000\n",
- "Docs uplaoded: 107000\n",
- "Docs uplaoded: 108000\n",
- "Docs uplaoded: 109000\n",
- "Docs uplaoded: 110000\n",
- "Docs uplaoded: 111000\n",
- "Docs uplaoded: 112000\n",
- "Docs uplaoded: 113000\n"
- ]
- },
- {
- "ename": "KeyboardInterrupt",
- "evalue": "",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
- "Cell \u001b[0;32mIn[48], line 25\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[38;5;66;03m# if you are using an ingest pipeline instead of a \u001b[39;00m\n\u001b[1;32m 16\u001b[0m \u001b[38;5;66;03m# semantic text field, use this instead:\u001b[39;00m\n\u001b[1;32m 17\u001b[0m \u001b[38;5;66;03m# documents.append(\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[38;5;66;03m# }\u001b[39;00m\n\u001b[1;32m 22\u001b[0m \u001b[38;5;66;03m# )\u001b[39;00m\n\u001b[1;32m 24\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 25\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mhelpers\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbulk\u001b[49m\u001b[43m(\u001b[49m\u001b[43mclient\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdocuments\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mraise_on_error\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m60s\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDocs uplaoded:\u001b[39m\u001b[38;5;124m\"\u001b[39m, j\u001b[38;5;241m*\u001b[39mMAX_BULK_SIZE)\n\u001b[1;32m 28\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/helpers/actions.py:521\u001b[0m, in \u001b[0;36mbulk\u001b[0;34m(client, actions, stats_only, ignore_status, *args, **kwargs)\u001b[0m\n\u001b[1;32m 519\u001b[0m \u001b[38;5;66;03m# make streaming_bulk yield successful results so we can count them\u001b[39;00m\n\u001b[1;32m 520\u001b[0m kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124myield_ok\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m--> 521\u001b[0m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mok\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mitem\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstreaming_bulk\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 522\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mactions\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mignore_status\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mignore_status\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[misc]\u001b[39;49;00m\n\u001b[1;32m 523\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 524\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# go through request-response pairs and detect failures\u001b[39;49;00m\n\u001b[1;32m 525\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mok\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 526\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstats_only\u001b[49m\u001b[43m:\u001b[49m\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/helpers/actions.py:436\u001b[0m, in \u001b[0;36mstreaming_bulk\u001b[0;34m(client, actions, chunk_size, max_chunk_bytes, raise_on_error, expand_action_callback, raise_on_exception, max_retries, initial_backoff, max_backoff, yield_ok, ignore_status, *args, **kwargs)\u001b[0m\n\u001b[1;32m 433\u001b[0m time\u001b[38;5;241m.\u001b[39msleep(\u001b[38;5;28mmin\u001b[39m(max_backoff, initial_backoff \u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m (attempt \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m)))\n\u001b[1;32m 435\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 436\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[43mok\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minfo\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mzip\u001b[39;49m\u001b[43m(\u001b[49m\n\u001b[1;32m 437\u001b[0m \u001b[43m \u001b[49m\u001b[43mbulk_data\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 438\u001b[0m \u001b[43m \u001b[49m\u001b[43m_process_bulk_chunk\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 439\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 440\u001b[0m \u001b[43m \u001b[49m\u001b[43mbulk_actions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 441\u001b[0m \u001b[43m \u001b[49m\u001b[43mbulk_data\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 442\u001b[0m \u001b[43m \u001b[49m\u001b[43mraise_on_exception\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 443\u001b[0m \u001b[43m \u001b[49m\u001b[43mraise_on_error\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 444\u001b[0m \u001b[43m \u001b[49m\u001b[43mignore_status\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 445\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 446\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 447\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 448\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 449\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mok\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 450\u001b[0m \u001b[43m \u001b[49m\u001b[43maction\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minfo\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43minfo\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpopitem\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/helpers/actions.py:339\u001b[0m, in \u001b[0;36m_process_bulk_chunk\u001b[0;34m(client, bulk_actions, bulk_data, raise_on_exception, raise_on_error, ignore_status, *args, **kwargs)\u001b[0m\n\u001b[1;32m 335\u001b[0m ignore_status \u001b[38;5;241m=\u001b[39m (ignore_status,)\n\u001b[1;32m 337\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 338\u001b[0m \u001b[38;5;66;03m# send the actual request\u001b[39;00m\n\u001b[0;32m--> 339\u001b[0m resp \u001b[38;5;241m=\u001b[39m \u001b[43mclient\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbulk\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moperations\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbulk_actions\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n\u001b[1;32m 340\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m ApiError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 341\u001b[0m gen \u001b[38;5;241m=\u001b[39m _process_bulk_chunk_error(\n\u001b[1;32m 342\u001b[0m error\u001b[38;5;241m=\u001b[39me,\n\u001b[1;32m 343\u001b[0m bulk_data\u001b[38;5;241m=\u001b[39mbulk_data,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 346\u001b[0m raise_on_error\u001b[38;5;241m=\u001b[39mraise_on_error,\n\u001b[1;32m 347\u001b[0m )\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/_sync/client/utils.py:446\u001b[0m, in \u001b[0;36m_rewrite_parameters..wrapper..wrapped\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 443\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n\u001b[1;32m 444\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[0;32m--> 446\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mapi\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/_sync/client/__init__.py:714\u001b[0m, in \u001b[0;36mElasticsearch.bulk\u001b[0;34m(self, operations, body, index, error_trace, filter_path, human, pipeline, pretty, refresh, require_alias, routing, source, source_excludes, source_includes, timeout, wait_for_active_shards)\u001b[0m\n\u001b[1;32m 709\u001b[0m __body \u001b[38;5;241m=\u001b[39m operations \u001b[38;5;28;01mif\u001b[39;00m operations \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m body\n\u001b[1;32m 710\u001b[0m __headers \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 711\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124maccept\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mapplication/json\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 712\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent-type\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mapplication/x-ndjson\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 713\u001b[0m }\n\u001b[0;32m--> 714\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mperform_request\u001b[49m\u001b[43m(\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[return-value]\u001b[39;49;00m\n\u001b[1;32m 715\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mPUT\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 716\u001b[0m \u001b[43m \u001b[49m\u001b[43m__path\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 717\u001b[0m \u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__query\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 718\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 719\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__body\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 720\u001b[0m \u001b[43m \u001b[49m\u001b[43mendpoint_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mbulk\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 721\u001b[0m \u001b[43m \u001b[49m\u001b[43mpath_parts\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m__path_parts\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 722\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/_sync/client/_base.py:271\u001b[0m, in \u001b[0;36mBaseClient.perform_request\u001b[0;34m(self, method, path, params, headers, body, endpoint_id, path_parts)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mperform_request\u001b[39m(\n\u001b[1;32m 256\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 257\u001b[0m method: \u001b[38;5;28mstr\u001b[39m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 264\u001b[0m path_parts: Optional[Mapping[\u001b[38;5;28mstr\u001b[39m, Any]] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 265\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ApiResponse[Any]:\n\u001b[1;32m 266\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_otel\u001b[38;5;241m.\u001b[39mspan(\n\u001b[1;32m 267\u001b[0m method,\n\u001b[1;32m 268\u001b[0m endpoint_id\u001b[38;5;241m=\u001b[39mendpoint_id,\n\u001b[1;32m 269\u001b[0m path_parts\u001b[38;5;241m=\u001b[39mpath_parts \u001b[38;5;129;01mor\u001b[39;00m {},\n\u001b[1;32m 270\u001b[0m ) \u001b[38;5;28;01mas\u001b[39;00m otel_span:\n\u001b[0;32m--> 271\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_perform_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 272\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 273\u001b[0m \u001b[43m \u001b[49m\u001b[43mpath\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 274\u001b[0m \u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 275\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 276\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbody\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 277\u001b[0m \u001b[43m \u001b[49m\u001b[43motel_span\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43motel_span\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 278\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 279\u001b[0m otel_span\u001b[38;5;241m.\u001b[39mset_elastic_cloud_metadata(response\u001b[38;5;241m.\u001b[39mmeta\u001b[38;5;241m.\u001b[39mheaders)\n\u001b[1;32m 280\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m response\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elasticsearch/_sync/client/_base.py:316\u001b[0m, in \u001b[0;36mBaseClient._perform_request\u001b[0;34m(self, method, path, params, headers, body, otel_span)\u001b[0m\n\u001b[1;32m 313\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 314\u001b[0m target \u001b[38;5;241m=\u001b[39m path\n\u001b[0;32m--> 316\u001b[0m meta, resp_body \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtransport\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mperform_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 317\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 318\u001b[0m \u001b[43m \u001b[49m\u001b[43mtarget\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 319\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 320\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbody\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 321\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_timeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_request_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 322\u001b[0m \u001b[43m \u001b[49m\u001b[43mmax_retries\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_max_retries\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 323\u001b[0m \u001b[43m \u001b[49m\u001b[43mretry_on_status\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_retry_on_status\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 324\u001b[0m \u001b[43m \u001b[49m\u001b[43mretry_on_timeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_retry_on_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 325\u001b[0m \u001b[43m \u001b[49m\u001b[43mclient_meta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_client_meta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 326\u001b[0m \u001b[43m \u001b[49m\u001b[43motel_span\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43motel_span\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 327\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 329\u001b[0m \u001b[38;5;66;03m# HEAD with a 404 is returned as a normal response\u001b[39;00m\n\u001b[1;32m 330\u001b[0m \u001b[38;5;66;03m# since this is used as an 'exists' functionality.\u001b[39;00m\n\u001b[1;32m 331\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (method \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHEAD\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m meta\u001b[38;5;241m.\u001b[39mstatus \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m404\u001b[39m) \u001b[38;5;129;01mand\u001b[39;00m (\n\u001b[1;32m 332\u001b[0m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;241m200\u001b[39m \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m meta\u001b[38;5;241m.\u001b[39mstatus \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m299\u001b[39m\n\u001b[1;32m 333\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m (\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 337\u001b[0m )\n\u001b[1;32m 338\u001b[0m ):\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elastic_transport/_transport.py:342\u001b[0m, in \u001b[0;36mTransport.perform_request\u001b[0;34m(self, method, target, body, headers, max_retries, retry_on_status, retry_on_timeout, request_timeout, client_meta, otel_span)\u001b[0m\n\u001b[1;32m 340\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 341\u001b[0m otel_span\u001b[38;5;241m.\u001b[39mset_node_metadata(node\u001b[38;5;241m.\u001b[39mhost, node\u001b[38;5;241m.\u001b[39mport, node\u001b[38;5;241m.\u001b[39mbase_url, target)\n\u001b[0;32m--> 342\u001b[0m resp \u001b[38;5;241m=\u001b[39m \u001b[43mnode\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mperform_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 343\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 344\u001b[0m \u001b[43m \u001b[49m\u001b[43mtarget\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 345\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_body\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 346\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 347\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_timeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 348\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 349\u001b[0m _logger\u001b[38;5;241m.\u001b[39minfo(\n\u001b[1;32m 350\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m [status:\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m duration:\u001b[39m\u001b[38;5;132;01m%.3f\u001b[39;00m\u001b[38;5;124ms]\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 351\u001b[0m \u001b[38;5;241m%\u001b[39m (\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 357\u001b[0m )\n\u001b[1;32m 358\u001b[0m )\n\u001b[1;32m 360\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m method \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHEAD\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/elastic_transport/_node/_http_urllib3.py:167\u001b[0m, in \u001b[0;36mUrllib3HttpNode.perform_request\u001b[0;34m(self, method, target, body, headers, request_timeout)\u001b[0m\n\u001b[1;32m 164\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 165\u001b[0m body_to_send \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m--> 167\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpool\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43murlopen\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 168\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 169\u001b[0m \u001b[43m \u001b[49m\u001b[43mtarget\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 170\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbody_to_send\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 171\u001b[0m \u001b[43m \u001b[49m\u001b[43mretries\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mRetry\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 172\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrequest_headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 173\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkw\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 174\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 175\u001b[0m response_headers \u001b[38;5;241m=\u001b[39m HttpHeaders(response\u001b[38;5;241m.\u001b[39mheaders)\n\u001b[1;32m 176\u001b[0m data \u001b[38;5;241m=\u001b[39m response\u001b[38;5;241m.\u001b[39mdata\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/connectionpool.py:790\u001b[0m, in \u001b[0;36mHTTPConnectionPool.urlopen\u001b[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001b[0m\n\u001b[1;32m 787\u001b[0m response_conn \u001b[38;5;241m=\u001b[39m conn \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m release_conn \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 789\u001b[0m \u001b[38;5;66;03m# Make the request on the HTTPConnection object\u001b[39;00m\n\u001b[0;32m--> 790\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_make_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 791\u001b[0m \u001b[43m \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 792\u001b[0m \u001b[43m \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 793\u001b[0m \u001b[43m \u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 794\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout_obj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 795\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbody\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 796\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 797\u001b[0m \u001b[43m \u001b[49m\u001b[43mchunked\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mchunked\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 798\u001b[0m \u001b[43m \u001b[49m\u001b[43mretries\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 799\u001b[0m \u001b[43m \u001b[49m\u001b[43mresponse_conn\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresponse_conn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 800\u001b[0m \u001b[43m \u001b[49m\u001b[43mpreload_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpreload_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 801\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 802\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mresponse_kw\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 803\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 805\u001b[0m \u001b[38;5;66;03m# Everything went great!\u001b[39;00m\n\u001b[1;32m 806\u001b[0m clean_exit \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/connectionpool.py:536\u001b[0m, in \u001b[0;36mHTTPConnectionPool._make_request\u001b[0;34m(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)\u001b[0m\n\u001b[1;32m 534\u001b[0m \u001b[38;5;66;03m# Receive the response from the server\u001b[39;00m\n\u001b[1;32m 535\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 536\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mconn\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgetresponse\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 537\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (BaseSSLError, \u001b[38;5;167;01mOSError\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 538\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_raise_timeout(err\u001b[38;5;241m=\u001b[39me, url\u001b[38;5;241m=\u001b[39murl, timeout_value\u001b[38;5;241m=\u001b[39mread_timeout)\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/connection.py:475\u001b[0m, in \u001b[0;36mHTTPConnection.getresponse\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 466\u001b[0m log\u001b[38;5;241m.\u001b[39mwarning(\n\u001b[1;32m 467\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFailed to parse headers (url=\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m): \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 468\u001b[0m _url_from_connection(\u001b[38;5;28mself\u001b[39m, resp_options\u001b[38;5;241m.\u001b[39mrequest_url),\n\u001b[1;32m 469\u001b[0m hpe,\n\u001b[1;32m 470\u001b[0m exc_info\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 471\u001b[0m )\n\u001b[1;32m 473\u001b[0m headers \u001b[38;5;241m=\u001b[39m HTTPHeaderDict(httplib_response\u001b[38;5;241m.\u001b[39mmsg\u001b[38;5;241m.\u001b[39mitems())\n\u001b[0;32m--> 475\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mHTTPResponse\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 476\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 477\u001b[0m \u001b[43m \u001b[49m\u001b[43mheaders\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 478\u001b[0m \u001b[43m \u001b[49m\u001b[43mstatus\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstatus\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 479\u001b[0m \u001b[43m \u001b[49m\u001b[43mversion\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mversion\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 480\u001b[0m \u001b[43m \u001b[49m\u001b[43mreason\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mreason\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 481\u001b[0m \u001b[43m \u001b[49m\u001b[43mpreload_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpreload_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 482\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 483\u001b[0m \u001b[43m \u001b[49m\u001b[43moriginal_response\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mhttplib_response\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 484\u001b[0m \u001b[43m \u001b[49m\u001b[43menforce_content_length\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43menforce_content_length\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 485\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_method\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest_method\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 486\u001b[0m \u001b[43m \u001b[49m\u001b[43mrequest_url\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mresp_options\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest_url\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 487\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 488\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m response\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/response.py:597\u001b[0m, in \u001b[0;36mHTTPResponse.__init__\u001b[0;34m(self, body, headers, status, version, reason, preload_content, decode_content, original_response, pool, connection, msg, retries, enforce_content_length, request_method, request_url, auto_close)\u001b[0m\n\u001b[1;32m 595\u001b[0m \u001b[38;5;66;03m# If requested, preload the body.\u001b[39;00m\n\u001b[1;32m 596\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m preload_content \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_body:\n\u001b[0;32m--> 597\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_body \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_content\u001b[49m\u001b[43m)\u001b[49m\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/response.py:877\u001b[0m, in \u001b[0;36mHTTPResponse.read\u001b[0;34m(self, amt, decode_content, cache_content)\u001b[0m\n\u001b[1;32m 874\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_decoded_buffer) \u001b[38;5;241m>\u001b[39m\u001b[38;5;241m=\u001b[39m amt:\n\u001b[1;32m 875\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_decoded_buffer\u001b[38;5;241m.\u001b[39mget(amt)\n\u001b[0;32m--> 877\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_raw_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 879\u001b[0m flush_decoder \u001b[38;5;241m=\u001b[39m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mor\u001b[39;00m (amt \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data)\n\u001b[1;32m 881\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_decoded_buffer) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/response.py:812\u001b[0m, in \u001b[0;36mHTTPResponse._raw_read\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 809\u001b[0m fp_closed \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_fp, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mclosed\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[1;32m 811\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_error_catcher():\n\u001b[0;32m--> 812\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_fp_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m fp_closed \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;124mb\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 813\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m amt \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data:\n\u001b[1;32m 814\u001b[0m \u001b[38;5;66;03m# Platform-specific: Buggy versions of Python.\u001b[39;00m\n\u001b[1;32m 815\u001b[0m \u001b[38;5;66;03m# Close the connection when no data is returned\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 820\u001b[0m \u001b[38;5;66;03m# not properly close the connection in all cases. There is\u001b[39;00m\n\u001b[1;32m 821\u001b[0m \u001b[38;5;66;03m# no harm in redundantly calling close.\u001b[39;00m\n\u001b[1;32m 822\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_fp\u001b[38;5;241m.\u001b[39mclose()\n",
- "File \u001b[0;32m~/.venv/lib/python3.11/site-packages/urllib3/response.py:797\u001b[0m, in \u001b[0;36mHTTPResponse._fp_read\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 794\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m buffer\u001b[38;5;241m.\u001b[39mgetvalue()\n\u001b[1;32m 795\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 796\u001b[0m \u001b[38;5;66;03m# StringIO doesn't like amt=None\u001b[39;00m\n\u001b[0;32m--> 797\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_fp\u001b[38;5;241m.\u001b[39mread(amt) \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_fp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
- "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/http/client.py:460\u001b[0m, in \u001b[0;36mHTTPResponse.read\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 457\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;124mb\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 459\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mchunked:\n\u001b[0;32m--> 460\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_read_chunked\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 462\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 463\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlength \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m amt \u001b[38;5;241m>\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlength:\n\u001b[1;32m 464\u001b[0m \u001b[38;5;66;03m# clip the read to the \"end of response\"\u001b[39;00m\n",
- "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/http/client.py:592\u001b[0m, in \u001b[0;36mHTTPResponse._read_chunked\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 589\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mchunk_left \u001b[38;5;241m=\u001b[39m chunk_left \u001b[38;5;241m-\u001b[39m amt\n\u001b[1;32m 590\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[0;32m--> 592\u001b[0m value\u001b[38;5;241m.\u001b[39mappend(\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_safe_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mchunk_left\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 593\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 594\u001b[0m amt \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m=\u001b[39m chunk_left\n",
- "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/http/client.py:631\u001b[0m, in \u001b[0;36mHTTPResponse._safe_read\u001b[0;34m(self, amt)\u001b[0m\n\u001b[1;32m 624\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_safe_read\u001b[39m(\u001b[38;5;28mself\u001b[39m, amt):\n\u001b[1;32m 625\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Read the number of bytes requested.\u001b[39;00m\n\u001b[1;32m 626\u001b[0m \n\u001b[1;32m 627\u001b[0m \u001b[38;5;124;03m This function should be used when bytes \"should\" be present for\u001b[39;00m\n\u001b[1;32m 628\u001b[0m \u001b[38;5;124;03m reading. If the bytes are truly not available (due to EOF), then the\u001b[39;00m\n\u001b[1;32m 629\u001b[0m \u001b[38;5;124;03m IncompleteRead exception can be used to detect the problem.\u001b[39;00m\n\u001b[1;32m 630\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 631\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfp\u001b[38;5;241m.\u001b[39mread(amt)\n\u001b[1;32m 632\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(data) \u001b[38;5;241m<\u001b[39m amt:\n\u001b[1;32m 633\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m IncompleteRead(data, amt\u001b[38;5;241m-\u001b[39m\u001b[38;5;28mlen\u001b[39m(data))\n",
- "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/socket.py:706\u001b[0m, in \u001b[0;36mSocketIO.readinto\u001b[0;34m(self, b)\u001b[0m\n\u001b[1;32m 704\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[1;32m 705\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 706\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_sock\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrecv_into\u001b[49m\u001b[43m(\u001b[49m\u001b[43mb\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 707\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m timeout:\n\u001b[1;32m 708\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_timeout_occurred \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n",
- "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/ssl.py:1278\u001b[0m, in \u001b[0;36mSSLSocket.recv_into\u001b[0;34m(self, buffer, nbytes, flags)\u001b[0m\n\u001b[1;32m 1274\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m flags \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 1275\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 1276\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnon-zero flags not allowed in calls to recv_into() on \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m%\u001b[39m\n\u001b[1;32m 1277\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__class__\u001b[39m)\n\u001b[0;32m-> 1278\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnbytes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbuffer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1279\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1280\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39mrecv_into(buffer, nbytes, flags)\n",
- "File \u001b[0;32m~/.pyenv/versions/3.11.4/lib/python3.11/ssl.py:1134\u001b[0m, in \u001b[0;36mSSLSocket.read\u001b[0;34m(self, len, buffer)\u001b[0m\n\u001b[1;32m 1132\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1133\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m buffer \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1134\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_sslobj\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbuffer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1135\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1136\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_sslobj\u001b[38;5;241m.\u001b[39mread(\u001b[38;5;28mlen\u001b[39m)\n",
- "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
- ]
- }
- ],
- "source": [
- "MAX_BULK_SIZE = 1000\n",
- "MAX_BULK_UPLOADS = 1000\n",
- "\n",
- "sentinel = object()\n",
- "for j in range(MAX_BULK_UPLOADS):\n",
- " documents = []\n",
- " while len(documents) < MAX_BULK_SIZE - len(all_langs_datasets):\n",
- " for ds in all_langs_datasets:\n",
- " text = next(ds, sentinel)\n",
- " if text is not sentinel:\n",
- " documents.append(\n",
- " {\n",
- " \"_index\": \"hf-semantic-text-index\",\n",
- " \"_source\": {\"text_field\": text[\"text\"]},\n",
- " }\n",
- " )\n",
- " # if you are using an ingest pipeline instead of a\n",
- " # semantic text field, use this instead:\n",
- " # documents.append(\n",
- " # {\n",
- " # \"_index\": \"hf-endpoint-index\",\n",
- " # \"_source\": {\"text\": text['text']},\n",
- " # }\n",
- " # )\n",
- "\n",
- " try:\n",
- " response = helpers.bulk(client, documents, raise_on_error=False, timeout=\"60s\")\n",
- " print(\"Docs uplaoded:\", (j + 1) * MAX_BULK_SIZE)\n",
- "\n",
- " except Exception as e:\n",
- " print(\"exception:\", str(e))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "cf0f6df7",
- "metadata": {},
- "source": [
- "## Semantic search\n",
- "\n",
- "After the dataset has been enriched with the embeddings, you can query the data using [semantic search](https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-semantic-search). Pass a `query_vector_builder` to the k-nearest neighbor (kNN) vector search API, and provide the query text and the model you have used to create the embeddings."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 65,
- "id": "d9b21b71",
- "metadata": {},
- "outputs": [],
- "source": [
- "query = \"English speaking countries\"\n",
- "semantic_search_results = client.search(\n",
- " index=\"hf-semantic-text-index\",\n",
- " query={\"semantic\": {\"field\": \"infer_field\", \"query\": query}},\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 66,
- "id": "8ef79d16",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\n",
- "ID: lnBvR5EBptSvxeXARnIB\n",
- "Score: 0.9464458\n",
- "Text: English-\n",
- "\n",
- "ID: LHF6R5EBptSvxeXA2LN6\n",
- "Score: 0.93809533\n",
- "Text: अंग्रेजी\n",
- "\n",
- "ID: ynF1R5EBptSvxeXAqSO4\n",
- "Score: 0.93671393\n",
- "Text: en anglais\n",
- "\n",
- "ID: e288R5EBptSvxeXANHxf\n",
- "Score: 0.9351928\n",
- "Text: المملكة المتحدة\n",
- "\n",
- "ID: hHBxR5EBptSvxeXAdq3W\n",
- "Score: 0.9330108\n",
- "Text: Argos - United Kingdom\n",
- "\n",
- "ID: Z3BzR5EBptSvxeXAwfCg\n",
- "Score: 0.9316524\n",
- "Text: Alemania\n",
- "\n",
- "ID: GHBxR5EBptSvxeXAubW9\n",
- "Score: 0.9309325\n",
- "Text: انگلیسی\n",
- "\n",
- "ID: Cm87R5EBptSvxeXA_HgZ\n",
- "Score: 0.9307623\n",
- "Text: . 🇹🇯\n",
- "\n",
- "ID: VnBxR5EBptSvxeXAqbSI\n",
- "Score: 0.92829084\n",
- "Text: Specific\n",
- "\n",
- "ID: G3F8R5EBptSvxeXACNSX\n",
- "Score: 0.9281881\n",
- "Text: Transports\n"
- ]
- }
- ],
- "source": [
- "pretty_search_response(semantic_search_results)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 44,
- "id": "a7053b1e",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "ObjectApiResponse({'model_id': 'my_cohere_rerank_endpoint', 'inference_id': 'my_cohere_rerank_endpoint', 'task_type': 'rerank', 'service': 'cohere', 'service_settings': {'model_id': 'rerank-english-v3.0', 'rate_limit': {'requests_per_minute': 10000}}, 'task_settings': {'top_n': 100, 'return_documents': True}})"
- ]
- },
- "execution_count": 44,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "try:\n",
- " client.inference.delete_model(inference_id=\"my_cohere_rerank_endpoint\")\n",
- "except Exception:\n",
- " pass\n",
- "client.inference.put_model(\n",
- " task_type=\"rerank\",\n",
- " inference_id=\"my_cohere_rerank_endpoint\",\n",
- " body={\n",
- " \"service\": \"cohere\",\n",
- " \"service_settings\": {\n",
- " \"api_key\": \"h2OzeuORCdvJ8eidGYbHmjfeWcecRQN8MYGDHxK1\",\n",
- " \"model_id\": \"rerank-english-v3.0\",\n",
- " },\n",
- " \"task_settings\": {\"top_n\": 100, \"return_documents\": True},\n",
- " },\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 67,
- "id": "6fba4f22",
- "metadata": {},
- "outputs": [],
- "source": [
- "reranked_search_results = client.search(\n",
- " index=\"hf-semantic-text-index\",\n",
- " retriever={\n",
- " \"text_similarity_reranker\": {\n",
- " \"retriever\": {\n",
- " \"standard\": {\n",
- " \"query\": {\"semantic\": {\"field\": \"infer_field\", \"query\": query}}\n",
- " }\n",
- " },\n",
- " \"field\": \"text_field\",\n",
- " \"inference_id\": \"my_cohere_rerank_endpoint\",\n",
- " \"inference_text\": query,\n",
- " \"rank_window_size\": 100,\n",
- " }\n",
- " },\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 68,
- "id": "fd4ef932",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\n",
- "ID: 8HBuR5EBptSvxeXAPVTR\n",
- "Score: 0.018546565\n",
- "Text: Kiingereza ni lugha rasmi pekee katika nchi za Uingereza, Marekani, Australia, Nyuzilandi, Jamaica, na nchi nyingine.\n",
- "\n",
- "ID: lnBvR5EBptSvxeXARnIB\n",
- "Score: 0.014728614\n",
- "Text: English-\n",
- "\n",
- "ID: 13F5R5EBptSvxeXAhY5q\n",
- "Score: 0.0085443305\n",
- "Text: The questions are worded in Canadian-English, with Canadian terminology and spelling, and are not localized for the American, UK or Australian markets.\n",
- "\n",
- "ID: 7G9CR5EBptSvxeXAwPtI\n",
- "Score: 0.004451041\n",
- "Text: باللغة العربية\n",
- "باللغة الإنجليزية\n",
- "\n",
- "ID: 13F4R5EBptSvxeXAV23K\n",
- "Score: 0.0031480705\n",
- "Text: (en inglés):\n",
- "\n",
- "ID: 4HF4R5EBptSvxeXA2Xpb\n",
- "Score: 0.0030634168\n",
- "Text: Greenland, *Iceland, Britania (Uingereza), Ueire (Ireland), Kuba, Newfoundland\n",
- "\n",
- "ID: Z3BzR5EBptSvxeXAwfCg\n",
- "Score: 0.0025311112\n",
- "Text: Alemania\n",
- "\n",
- "ID: tnBvR5EBptSvxeXARnIB\n",
- "Score: 0.0018031665\n",
- "Text: Italian-\n",
- "\n",
- "ID: 128-R5EBptSvxeXAw603\n",
- "Score: 0.0016809417\n",
- "Text: بالعربي الاختصار بالإنكليزي\n",
- "\n",
- "ID: ynF1R5EBptSvxeXAqSO4\n",
- "Score: 0.0016040893\n",
- "Text: en anglais\n"
- ]
- }
- ],
- "source": [
- "pretty_search_response(reranked_search_results)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "7e4055ba",
- "metadata": {},
- "source": [
- "**NOTE:** The value of `model_id` in the `query_vector_builder` must match the value of `inference_id` you created in the [first step](#create-the-inference-endpoint)."
- ]
- }
- ],
- "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.11.4"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-reranking.ipynb b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-reranking.ipynb
new file mode 100644
index 00000000..14a0d4f2
--- /dev/null
+++ b/notebooks/integrations/hugging-face/huggingface-integration-millions-of-documents-with-reranking.ipynb
@@ -0,0 +1,579 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "7a765629",
+ "metadata": {},
+ "source": [
+ "# Semantic Search using the Inference API with the Hugging Face Inference Endpoints Service\n",
+ "\n",
+ "TODO [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/elastic/elasticsearch-labs/blob/main/notebooks/search/TODO)\n",
+ "\n",
+ "\n",
+ "Learn how to use the [Inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-apis.html) with the Hugging Face Inference Endpoint service for semantic search."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f9101eb9",
+ "metadata": {},
+ "source": [
+ "# 🧰 Requirements\n",
+ "\n",
+ "For this example, you will need:\n",
+ "\n",
+ "- An Elastic deployment:\n",
+ " - We'll be using [Elastic serverless](https://www.elastic.co/docs/current/serverless) for this example (available with a [free trial](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook))\n",
+ "\n",
+ "- Elasticsearch 8.14 or above.\n",
+ " \n",
+ "- A paid [Hugging Face Inference Endpoint](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) is required to use the Inference API with \n",
+ "the Hugging Face Inference Endpoint service."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4cd69cc0",
+ "metadata": {},
+ "source": [
+ "# Create Elastic Cloud deployment or serverless project\n",
+ "\n",
+ "If you don't have an Elastic Cloud deployment, sign up [here](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook) for a free trial."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f27dffbf",
+ "metadata": {},
+ "source": [
+ "# Install packages and connect with Elasticsearch Client\n",
+ "\n",
+ "To get started, we'll need to connect to our Elastic deployment using the Python client (version 8.12.0 or above).\n",
+ "Because we're using an Elastic Cloud deployment, we'll use the **Cloud ID** to identify our deployment.\n",
+ "\n",
+ "First we need to `pip` install the following packages:\n",
+ "\n",
+ "- `elasticsearch`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8c4b16bc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "!pip install elasticsearch\n",
+ "!pip install datasets\n",
+ "!pip install 'eland[pytorch]'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "41ef96b3",
+ "metadata": {},
+ "source": [
+ "Next, we need to import the modules we need. 🔐 NOTE: getpass enables us to securely prompt the user for credentials without echoing them to the terminal, or storing it in memory."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "690ff9af",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from elasticsearch import Elasticsearch, helpers\n",
+ "from getpass import getpass\n",
+ "import datasets\n",
+ "from eland.ml.pytorch import PyTorchModel\n",
+ "from eland.ml.pytorch.transformers import TransformerModel\n",
+ "from pathlib import Path"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "23fa2b6c",
+ "metadata": {},
+ "source": [
+ "Now we can instantiate the Python Elasticsearch client.\n",
+ "\n",
+ "First we prompt the user for their password and Cloud ID.\n",
+ "Then we create a `client` object that instantiates an instance of the `Elasticsearch` class."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "195cc597",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id\n",
+ "ELASTIC_CLOUD_ID = getpass(\"Elastic Cloud ID: \")\n",
+ "\n",
+ "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key\n",
+ "ELASTIC_API_KEY = getpass(\"Elastic Api Key: \")\n",
+ "\n",
+ "# Create the client instance\n",
+ "client = Elasticsearch(\n",
+ " # For local development\n",
+ " # hosts=[\"http://localhost:9200\"]\n",
+ " cloud_id=ELASTIC_CLOUD_ID,\n",
+ " api_key=ELASTIC_API_KEY,\n",
+ " request_timeout=120,\n",
+ " max_retries=10,\n",
+ " retry_on_timeout=True,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b1115ffb",
+ "metadata": {},
+ "source": [
+ "### Test the Client\n",
+ "Before you continue, confirm that the client has connected with this test."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "cc0de5ea",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(client.info())\n",
+ "\n",
+ "\n",
+ "# define this now so we can use it later\n",
+ "def pretty_search_response(response):\n",
+ " if len(response[\"hits\"][\"hits\"]) == 0:\n",
+ " print(\"Your search returned no results.\")\n",
+ " else:\n",
+ " for hit in response[\"hits\"][\"hits\"]:\n",
+ " id = hit[\"_id\"]\n",
+ " score = hit[\"_score\"]\n",
+ " text = hit[\"_source\"][\"text_field\"]\n",
+ "\n",
+ " pretty_output = f\"\\nID: {id}\\nScore: {score}\\nText: {text}\"\n",
+ "\n",
+ " print(pretty_output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "659c5890",
+ "metadata": {},
+ "source": [
+ "Refer to [the documentation](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect to a self-managed deployment.\n",
+ "\n",
+ "Read [this page](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect using API keys."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "840d92f0",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Create the inference endpoint object\n",
+ "\n",
+ "Let's create the inference endpoint by using the [Create inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html).\n",
+ "\n",
+ "You'll need an Hugging Face API key (access token) for this that you can find in your Hugging Face account under the [Access Tokens](https://huggingface.co/settings/tokens).\n",
+ "\n",
+ "You will also need to have created a [Hugging Face Inference Endpoint service instance](https://huggingface.co/docs/inference-endpoints/guides/create_endpoint) and noted the `url` of your instance. For this notebook, we deployed the `multilingual-e5-small` model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0d007737",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "API_KEY = getpass(\"Huggingface API key: \")\n",
+ "client.inference.put_model(\n",
+ " inference_id=\"my_hf_endpoint_object\",\n",
+ " body={\n",
+ " \"service\": \"hugging_face\",\n",
+ " \"service_settings\": {\n",
+ " \"api_key\": API_KEY,\n",
+ " \"url\": \"\",\n",
+ " \"similarity\": \"dot_product\",\n",
+ " },\n",
+ " },\n",
+ " task_type=\"text_embedding\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "67f4201d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.inference.inference(\n",
+ " inference_id=\"my_hf_endpoint_object\", input=\"this is the raw text of my document!\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1f2e48b7",
+ "metadata": {},
+ "source": [
+ "**IMPORTANT:** If you use Elasticsearch 8.12, you must change `inference_id` in the snippet above to `model_id`! "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0346151d",
+ "metadata": {},
+ "source": [
+ "#"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1024d070",
+ "metadata": {},
+ "source": [
+ "## Create an ingest pipeline with an inference processor\n",
+ "\n",
+ "Create an ingest pipeline with an inference processor by using the [`put_pipeline`](https://www.elastic.co/guide/en/elasticsearch/reference/master/put-pipeline-api.html) method. Reference the `inference_id` created above as `model_id` to infer on the data that is being ingested by the pipeline."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6ace9e2e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.ingest.put_pipeline(\n",
+ " id=\"hf_pipeline\",\n",
+ " processors=[\n",
+ " {\n",
+ " \"inference\": {\n",
+ " \"model_id\": \"my_hf_endpoint_object\",\n",
+ " \"input_output\": {\n",
+ " \"input_field\": \"text_field\",\n",
+ " \"output_field\": \"text_embedding\",\n",
+ " },\n",
+ " }\n",
+ " }\n",
+ " ],\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "76d07567",
+ "metadata": {},
+ "source": [
+ "Let's note a few important parameters from that API call:\n",
+ "\n",
+ "- `inference`: A processor that performs inference using a machine learning model.\n",
+ "- `model_id`: Specifies the ID of the inference endpoint to be used. In this example, the inference ID is set to `my_hf_endpoint_object`. Use the inference ID you defined when created the inference task.\n",
+ "- `input_output`: Specifies input and output fields.\n",
+ "- `input_field`: Field name from which the `dense_vector` representation is created.\n",
+ "- `output_field`: Field name which contains inference results. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "28e12d7a",
+ "metadata": {},
+ "source": [
+ "## Create index\n",
+ "\n",
+ "The mapping of the destination index - the index that contains the embeddings that the model will create based on your input text - must be created. The destination index must have a field with the [dense_vector](https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html) field type to index the output of the model we deployed in Hugging Face (`multilingual-e5-small`).\n",
+ "\n",
+ "Let's create an index named `hf-endpoint-index` with the mappings we need."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6ddcbca3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.indices.create(\n",
+ " index=\"hf-endpoint-index\",\n",
+ " settings={\n",
+ " \"index\": {\n",
+ " \"default_pipeline\": \"hf_pipeline\",\n",
+ " }\n",
+ " },\n",
+ " mappings={\n",
+ " \"properties\": {\n",
+ " \"text\": {\"type\": \"text\"},\n",
+ " \"text_embedding\": {\n",
+ " \"type\": \"dense_vector\",\n",
+ " \"dims\": 384,\n",
+ " \"similarity\": \"dot_product\",\n",
+ " },\n",
+ " }\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "12aa3452",
+ "metadata": {},
+ "source": [
+ "## If you are using Elasticsearch serverless or v8.15+ then you will have access to the new `semantic_text` field\n",
+ "`semantic_text` has significantly faster ingest times and is recommended.\n",
+ "\n",
+ "https://github.com/elastic/elasticsearch/blob/main/docs/reference/mapping/types/semantic-text.asciidoc"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5eeb3755",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.indices.create(\n",
+ " index=\"hf-semantic-text-index\",\n",
+ " mappings={\n",
+ " \"properties\": {\n",
+ " \"infer_field\": {\n",
+ " \"type\": \"semantic_text\",\n",
+ " \"inference_id\": \"my_hf_endpoint_object\",\n",
+ " },\n",
+ " \"text_field\": {\"type\": \"text\", \"copy_to\": \"infer_field\"},\n",
+ " }\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "07c187a9",
+ "metadata": {},
+ "source": [
+ "## Insert Documents\n",
+ "\n",
+ "In this example, we want to show the power of using GPUs in Hugging Face's Inference Endpoint service by indexing millions of multilingual documents from the miracl corpus. The speed at which these documents ingest will depend on whether you use a semantic text field (faster) or an ingest pipeline (slower) and will also depend on how much hardware your rent for your Hugging Face inference endpoint. Using a semantic_text field with a single T4 GPU, it may take about 3 hours to index 1 million documents. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d68737cb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# langs = [\n",
+ "# \"ar\",\n",
+ "# \"bn\",\n",
+ "# \"en\",\n",
+ "# \"es\",\n",
+ "# \"fa\",\n",
+ "# \"fi\",\n",
+ "# \"fr\",\n",
+ "# \"hi\",\n",
+ "# \"id\",\n",
+ "# \"ja\",\n",
+ "# \"ko\",\n",
+ "# \"ru\",\n",
+ "# \"sw\",\n",
+ "# \"te\",\n",
+ "# \"th\",\n",
+ "# \"zh\",\n",
+ "# ]\n",
+ "\n",
+ "langs = [\n",
+ " \"en\"\n",
+ "] # for this example, we just use english so that the results are also in english\n",
+ "\n",
+ "\n",
+ "all_langs_datasets = [\n",
+ " iter(datasets.load_dataset(\"miracl/miracl-corpus\", lang)[\"train\"]) for lang in langs\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7ccf9835",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "MAX_BULK_SIZE = 1000\n",
+ "MAX_BULK_UPLOADS = 1000\n",
+ "\n",
+ "sentinel = object()\n",
+ "for j in range(MAX_BULK_UPLOADS):\n",
+ " documents = []\n",
+ " while len(documents) < MAX_BULK_SIZE - len(all_langs_datasets):\n",
+ " for ds in all_langs_datasets:\n",
+ " text = next(ds, sentinel)\n",
+ " if text is not sentinel:\n",
+ " documents.append(\n",
+ " {\n",
+ " \"_index\": \"hf-semantic-text-index\",\n",
+ " \"_source\": {\"text_field\": text[\"text\"]},\n",
+ " }\n",
+ " )\n",
+ " # if you are using an ingest pipeline instead of a\n",
+ " # semantic text field, use this instead:\n",
+ " # documents.append(\n",
+ " # {\n",
+ " # \"_index\": \"hf-endpoint-index\",\n",
+ " # \"_source\": {\"text\": text['text']},\n",
+ " # }\n",
+ " # )\n",
+ "\n",
+ " try:\n",
+ " response = helpers.bulk(client, documents, raise_on_error=False, timeout=\"60s\")\n",
+ " print(response)\n",
+ " print(\"Docs uplaoded:\", (j + 1) * MAX_BULK_SIZE)\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(\"exception:\", str(e))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cf0f6df7",
+ "metadata": {},
+ "source": [
+ "## Semantic search\n",
+ "\n",
+ "After the dataset has been enriched with the embeddings, you can query the data using [semantic search](https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-semantic-search). Pass a `query_vector_builder` to the k-nearest neighbor (kNN) vector search API, and provide the query text and the model you have used to create the embeddings."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d9b21b71",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "query = \"motor vehicles\"\n",
+ "semantic_search_results = client.search(\n",
+ " index=\"hf-semantic-text-index\",\n",
+ " query={\"semantic\": {\"field\": \"infer_field\", \"query\": query}},\n",
+ ")\n",
+ "pretty_search_response(semantic_search_results)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "81c3f28a",
+ "metadata": {},
+ "source": [
+ "## Reranking with elasticsearch"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "934c54b4",
+ "metadata": {},
+ "source": [
+ "First, import a reranking model into elasticsearch with eland"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "916745f2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Load a Hugging Face transformers model directly from the model hub\n",
+ "tm = TransformerModel(\n",
+ " model_id=\"cross-encoder/ms-marco-MiniLM-L-6-v2\", task_type=\"text_similarity\"\n",
+ ")\n",
+ "\n",
+ "# Export the model in a TorchScrpt representation which Elasticsearch uses\n",
+ "tmp_path = \"models\"\n",
+ "Path(tmp_path).mkdir(parents=True, exist_ok=True)\n",
+ "model_path, config, vocab_path = tm.save(tmp_path)\n",
+ "\n",
+ "# Import model into Elasticsearch\n",
+ "ptm = PyTorchModel(client, tm.elasticsearch_model_id())\n",
+ "ptm.import_model(\n",
+ " model_path=model_path, config_path=None, vocab_path=vocab_path, config=config\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a7053b1e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "client.inference.put(\n",
+ " task_type=\"rerank\",\n",
+ " inference_id=\"es-rerank-endpoint\",\n",
+ " body={\n",
+ " \"service\": \"elasticsearch\",\n",
+ " \"service_settings\": {\n",
+ " \"model_id\": \"cross-encoder__ms-marco-minilm-l-6-v2\",\n",
+ " \"num_allocations\": 1,\n",
+ " \"num_threads\": 1,\n",
+ " },\n",
+ " \"task_settings\": {\"return_documents\": True},\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6fba4f22",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "reranked_search_results = client.search(\n",
+ " index=\"hf-semantic-text-index\",\n",
+ " retriever={\n",
+ " \"text_similarity_reranker\": {\n",
+ " \"retriever\": {\n",
+ " \"standard\": {\n",
+ " \"query\": {\"semantic\": {\"field\": \"infer_field\", \"query\": query}}\n",
+ " }\n",
+ " },\n",
+ " \"field\": \"text_field\",\n",
+ " \"inference_id\": \"es-rerank-endpoint\",\n",
+ " \"inference_text\": query,\n",
+ " \"rank_window_size\": 100,\n",
+ " }\n",
+ " },\n",
+ ")\n",
+ "pretty_search_response(reranked_search_results)"
+ ]
+ }
+ ],
+ "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.11.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}