Repository: aurelio-labs/semantic-router
Pull request #581: feat: voyage/jina_ai encoder addition
Author: login: shinobi-josh
Created: 2025-04-08T09:00:03Z | Merged: 2025-04-09T20:28:01Z

### **PR Type**
- Enhancement



___

### **Description**
- Migrate encoders to use LiteLLM wrapper

- Add new Voyage and LiteLLM encoders

- Remove deprecated client initialization code

- Update defaults, schema, tests, and documentation notebooks


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>7 files</summary><table>
<tr>
  <td><strong>__init__.py</strong><dd><code>Add LiteLLMEncoder and VoyageEncoder imports; update auto encoder </code><br><code>logic</code></dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-ad09f7d7b788f2facc4d15efb96ad834afb2af0e8d5028bc44488cc1210c2657">+6/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>cohere.py</strong><dd><code>Refactor CohereEncoder to extend LiteLLMEncoder and update embed </code><br><code>methods</code></dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-63f98a7f33cadbad5e7fc0ad2984d0cb0f16535da13c3b99508ea6fe9e3a6ba8">+51/-95</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>litellm.py</strong><dd><code>Introduce LiteLLMEncoder class and helper function for embeddings </code><br><code>conversion</code></dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-d889d6b45bcb301ba87646cbf443aa759645826d27605b7b6de6137a55c7613e">+123/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>mistral.py</strong><dd><code>Migrate MistralEncoder to inherit from LiteLLMEncoder and simplify </code><br><code>initialization</code></dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-a0f7be222fef77a8d121872f4872506b3d622025b16a5ff2bec90ea48d9e7ba7">+31/-67</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>voyage.py</strong><dd><code>Add new VoyageEncoder for Voyage API integration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-b884de055c5959bf4aac86a6d014e8e6410565dc55cc54206306a7b85427b668">+85/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>schema.py</strong><dd><code>Add LITELLM type to encoder schema enumeration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-2745f608339614a7c34abdc5917e375f87cb4c953a07404ed96f4f37180b3232">+1/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>defaults.py</strong><dd><code>Add default settings for VOYAGE encoder integration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-edc2d765344712a611990c95a5d8e155c8516f3266732b60eea4023b92a7b10c">+4/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>2 files</summary><table>
<tr>
  <td><strong>test_cohere.py</strong><dd><code>Remove legacy Cohere encoder tests now replaced by LiteLLM tests</code></dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-5e79eea8775d2be786b481e5757c9b8e66392cdfdd894ee51035ad91ce5944ed">+0/-85</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>test_lite_encoders.py</strong><dd><code>Add tests for LiteLLM, Cohere, and Mistral encoders integration</code></dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-dd2b479970380035ae4a0d62e8c20695a3e96237f8e3eec2b22851d64fb46516">+107/-0</a>&nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>3 files</summary><table>
<tr>
  <td><strong>cohere.ipynb</strong><dd><code>Update notebook for Cohere encoder with new LiteLLM integration</code></dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-8b00a1d7bad521540253291a37cfa1f12dd30fb0e73b665e8d86a8c25a4c57dd">+352/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>mistral-encoder.ipynb</strong><dd><code>Update notebook to demonstrate Mistral encoder migration to LiteLLM</code></dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-8a201b8b1ca4b4b648ae14cd6957139a94c5ff7e96f574e54fbe4ce92939b4e9">+385/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>voyage-encoder.ipynb</strong><dd><code>Add new notebook demonstrating Voyage encoder usage</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-b7035a3795114d3075eb6f11ef45bdc586e9092055b70ca1b17ae5e426584dcb">+400/-0</a>&nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Configuration</strong></td><td><details><summary>1 files</summary><table>
<tr>
  <td><strong>pyproject.toml</strong><dd><code>Update version to 0.1.8 and add litellm dependency</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-50c86b7ed8ac2cf95bd48334961bf0530cdc77b5a56f852c5c61b89d735fd711">+3/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>1 files</summary><table>
<tr>
  <td><strong>test_mistral.py</strong></td>
  <td><a href="https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-87f4826b2c58386dd72dd1f529d3558227b34a5066f958693a9f72fce465cb9e">+0/-138</a>&nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary>  Need help?</summary><li>Type <code>/help how to ...</code> in the comments thread for any questions about PR-Agent usage.</li><li>Check out the <a href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a> for more information.</li></details>


Diff:
diff --git a/docs/encoders/jina-encoder.ipynb b/docs/encoders/jina-encoder.ipynb
new file mode 100644
index 00000000..e09217ec
--- /dev/null
+++ b/docs/encoders/jina-encoder.ipynb
@@ -0,0 +1,417 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/aurelio-labs/semantic-router/blob/main/docs/encoders/jina-encoder.ipynb) [![Open nbviewer](https://raw.githubusercontent.com/pinecone-io/examples/master/assets/nbviewer-shield.svg)](https://nbviewer.org/github/aurelio-labs/semantic-router/blob/main/docs/encoders/jina-encoder.ipynb)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Using Jina Models"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The Voyage embedding model (jina-embeddings-v3) can be used with our LiteLLMEncoder and usage is primarily the same as with other embedding models. The model produces high-quality embeddings for semantic search, classification, and other text similarity tasks."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We start by installing semantic-router. Support for the new `jina` encoder was added in `semantic-router==0.1.8`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!pip install -qU semantic-router==0.1.8"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We start by defining a dictionary mapping routes to example phrases that should trigger those routes."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/Users/joshuabriggs/Documents/GitHub/semantic-router/.venv/lib/python3.13/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",
+      "2025-04-08 11:38:16 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: GET https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json \"HTTP/1.1 200 OK\"\n"
+     ]
+    }
+   ],
+   "source": [
+    "from semantic_router import Route\n",
+    "\n",
+    "politics = Route(\n",
+    "    name=\"politics\",\n",
+    "    utterances=[\n",
+    "        \"isn't politics the best thing ever\",\n",
+    "        \"why don't you tell me about your political opinions\",\n",
+    "        \"don't you just love the president\",\n",
+    "        \"don't you just hate the president\",\n",
+    "        \"they're going to destroy this country!\",\n",
+    "        \"they will save the country!\",\n",
+    "    ],\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Let's define another for good measure:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "chitchat = Route(\n",
+    "    name=\"chitchat\",\n",
+    "    utterances=[\n",
+    "        \"how's the weather today?\",\n",
+    "        \"how are things going?\",\n",
+    "        \"lovely weather today\",\n",
+    "        \"the weather is horrendous\",\n",
+    "        \"let's go to the chippy\",\n",
+    "    ],\n",
+    ")\n",
+    "\n",
+    "routes = [politics, chitchat]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now we initialize our embedding model, we will use the `jina-embeddings-v3` model."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "from getpass import getpass\n",
+    "from semantic_router.encoders import JinaEncoder\n",
+    "\n",
+    "os.environ[\"JINA_AI_API_KEY\"] = os.getenv(\"JINA_AI_API_KEY\") or getpass(\n",
+    "    \"Enter Jina API Key: \"\n",
+    ")\n",
+    "\n",
+    "encoder = JinaEncoder(\n",
+    "    name=\"jina-embeddings-v3\",\n",
+    "    score_threshold=0.4,\n",
+    "    api_key=os.environ[\"JINA_AI_API_KEY\"],\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now we define the `RouteLayer`. When called, the route layer will consume text (a query) and output the category (`Route`) it belongs to — to initialize a `RouteLayer` we need our `encoder` model and a list of `routes`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 11:40:19 - semantic_router.utils.logger - WARNING - base.py:442 - _get_index() - No index provided. Using default LocalIndex.\n",
+      "2025-04-08 11:40:20 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.jina.ai/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m11:40:20 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 11:40:20 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m11:40:20 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:20 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:20 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:20 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:20 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:20 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n",
+      "\u001b[92m11:40:20 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:20 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:21 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.jina.ai/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m11:40:21 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 11:40:21 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m11:40:21 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:21 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:21 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:21 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:21 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:21 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n",
+      "\u001b[92m11:40:21 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:21 - semantic_router.utils.logger - WARNING - local.py:213 - _write_config() - No config is written for LocalIndex.\n",
+      "2025-04-08 11:40:21 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n"
+     ]
+    }
+   ],
+   "source": [
+    "from semantic_router.routers import SemanticRouter\n",
+    "\n",
+    "rl = SemanticRouter(encoder=encoder, routes=routes, auto_sync=\"local\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 11:40:26 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.jina.ai/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m11:40:26 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 11:40:26 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m11:40:26 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:26 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:26 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:26 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n"
+     ]
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "\u001b[92m11:40:26 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:26 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:26 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:26 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n",
+      "\u001b[92m11:40:29 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:29 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:29 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:29 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n",
+      "\u001b[92m11:40:30 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:30 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:30 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:30 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n",
+      "\u001b[92m11:40:33 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:33 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:33 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:33 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n"
+     ]
+    }
+   ],
+   "source": [
+    "result = encoder(\n",
+    "    [\n",
+    "        \"I'm interested in learning about llama 2\",\n",
+    "        \"Don't you love politics?\",\n",
+    "        \"How's the weather today?\",\n",
+    "        \"I love the politics\",\n",
+    "    ]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can check the dimensionality of our vectors by looking at the `index` attribute of the `RouteLayer`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "1024"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "rl.index.dimensions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We do have 256-dimensional vectors. Now let's test them:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 11:40:29 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.jina.ai/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m11:40:29 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 11:40:29 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m11:40:29 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:29 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:29 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:29 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "RouteChoice(name='politics', function_call=None, similarity_score=None)"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "rl(\"don't you love politics?\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 11:40:30 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.jina.ai/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m11:40:30 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 11:40:30 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m11:40:30 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:30 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:30 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:30 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "RouteChoice(name='chitchat', function_call=None, similarity_score=None)"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "rl(\"how's the weather today?\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Both are classified accurately, what if we send a query that is unrelated to our existing `Route` objects?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 11:40:33 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.jina.ai/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m11:40:33 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 11:40:33 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m11:40:33 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "2025-04-08 11:40:33 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina_ai/jina-embeddings-v3\n",
+      "\u001b[92m11:40:33 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: jina-embeddings-v3\n",
+      "2025-04-08 11:40:33 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: jina-embeddings-v3\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "RouteChoice(name='chitchat', function_call=None, similarity_score=None)"
+      ]
+     },
+     "execution_count": 13,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "rl(\"I'm interested in learning about llama 2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In this case, we return `None` because no matches were identified. We always recommend optimizing your `RouteLayer` for optimal performance, you can see how in [this notebook](https://github.com/aurelio-labs/semantic-router/blob/main/docs/06-threshold-optimization.ipynb)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "---"
+   ]
+  }
+ ],
+ "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.13.2"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/encoders/mistral-encoder.ipynb b/docs/encoders/mistral-encoder.ipynb
index 6326caca..f6d85e1b 100644
--- a/docs/encoders/mistral-encoder.ipynb
+++ b/docs/encoders/mistral-encoder.ipynb
@@ -32,7 +32,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "We start by installing semantic-router. Support for the new `mistral` encoder was added in `semantic-router==1.1.6`."
+    "We start by installing semantic-router. Support for the new `mistral` encoder was added in `semantic-router==0.1.8`."
    ]
   },
   {
@@ -41,7 +41,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "!pip install -qU semantic-router==1.1.6"
+    "!pip install -qU semantic-router==0.1.8"
    ]
   },
   {
diff --git a/docs/encoders/voyage-encoder.ipynb b/docs/encoders/voyage-encoder.ipynb
new file mode 100644
index 00000000..d8a2e126
--- /dev/null
+++ b/docs/encoders/voyage-encoder.ipynb
@@ -0,0 +1,393 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/aurelio-labs/semantic-router/blob/main/docs/encoders/voyage-encoder.ipynb) [![Open nbviewer](https://raw.githubusercontent.com/pinecone-io/examples/master/assets/nbviewer-shield.svg)](https://nbviewer.org/github/aurelio-labs/semantic-router/blob/main/docs/encoders/voyage-encoder.ipynb)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Using VoyageAI Models"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The Voyage embedding model (voyage-v3) can be used with our LiteLLMEncoder and usage is primarily the same as with other embedding models. The model produces high-quality embeddings for semantic search, classification, and other text similarity tasks."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Getting Started"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We start by installing semantic-router. Support for the new `voyage` encoder was added in `semantic-router==0.1.8`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!pip install -qU semantic-router==0.1.8"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We start by defining a dictionary mapping routes to example phrases that should trigger those routes."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/Users/joshuabriggs/Documents/GitHub/semantic-router/.venv/lib/python3.13/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",
+      "2025-04-08 09:50:32 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: GET https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json \"HTTP/1.1 200 OK\"\n"
+     ]
+    }
+   ],
+   "source": [
+    "from semantic_router import Route\n",
+    "\n",
+    "politics = Route(\n",
+    "    name=\"politics\",\n",
+    "    utterances=[\n",
+    "        \"isn't politics the best thing ever\",\n",
+    "        \"why don't you tell me about your political opinions\",\n",
+    "        \"don't you just love the president\",\n",
+    "        \"don't you just hate the president\",\n",
+    "        \"they're going to destroy this country!\",\n",
+    "        \"they will save the country!\",\n",
+    "    ],\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Let's define another for good measure:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "chitchat = Route(\n",
+    "    name=\"chitchat\",\n",
+    "    utterances=[\n",
+    "        \"how's the weather today?\",\n",
+    "        \"how are things going?\",\n",
+    "        \"lovely weather today\",\n",
+    "        \"the weather is horrendous\",\n",
+    "        \"let's go to the chippy\",\n",
+    "    ],\n",
+    ")\n",
+    "\n",
+    "routes = [politics, chitchat]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now we initialize our embedding model, we will use the `voyage-3` model."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "from getpass import getpass\n",
+    "from semantic_router.encoders import VoyageEncoder\n",
+    "\n",
+    "os.environ[\"VOYAGE_API_KEY\"] = os.getenv(\"VOYAGE_API_KEY\") or getpass(\n",
+    "    \"Enter Voyage API Key: \"\n",
+    ")\n",
+    "\n",
+    "encoder = VoyageEncoder(\n",
+    "    name=\"voyage-3\",\n",
+    "    score_threshold=0.4,\n",
+    "    api_key=os.environ[\"VOYAGE_API_KEY\"],\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now we define the `RouteLayer`. When called, the route layer will consume text (a query) and output the category (`Route`) it belongs to — to initialize a `RouteLayer` we need our `encoder` model and a list of `routes`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 09:50:58 - semantic_router.utils.logger - WARNING - base.py:442 - _get_index() - No index provided. Using default LocalIndex.\n",
+      "2025-04-08 09:50:58 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.voyageai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m09:50:58 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 09:50:58 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m09:50:58 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:50:58 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n",
+      "\u001b[92m09:50:58 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:50:58 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:50:58 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.voyageai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m09:50:59 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 09:50:59 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m09:50:59 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:50:59 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n",
+      "\u001b[92m09:50:59 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:50:59 - semantic_router.utils.logger - WARNING - local.py:213 - _write_config() - No config is written for LocalIndex.\n",
+      "2025-04-08 09:50:59 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n"
+     ]
+    }
+   ],
+   "source": [
+    "from semantic_router.routers import SemanticRouter\n",
+    "\n",
+    "rl = SemanticRouter(encoder=encoder, routes=routes, auto_sync=\"local\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 09:51:18 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.voyageai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m09:51:18 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 09:51:18 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m09:51:18 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:51:18 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n"
+     ]
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "\u001b[92m09:51:18 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:51:18 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n",
+      "\u001b[92m09:51:22 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:51:22 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n",
+      "\u001b[92m09:51:26 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:51:26 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n",
+      "\u001b[92m09:51:29 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:51:29 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n"
+     ]
+    }
+   ],
+   "source": [
+    "result = encoder(\n",
+    "    [\n",
+    "        \"I'm interested in learning about llama 2\",\n",
+    "        \"Don't you love politics?\",\n",
+    "        \"How's the weather today?\",\n",
+    "        \"I love the politics\",\n",
+    "    ]\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can check the dimensionality of our vectors by looking at the `index` attribute of the `RouteLayer`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "1024"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "rl.index.dimensions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We do have 256-dimensional vectors. Now let's test them:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 09:51:22 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.voyageai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m09:51:22 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 09:51:22 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m09:51:22 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:51:22 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "RouteChoice(name='politics', function_call=None, similarity_score=None)"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "rl(\"don't you love politics?\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 09:51:26 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.voyageai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m09:51:26 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 09:51:26 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m09:51:26 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:51:26 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "RouteChoice(name='chitchat', function_call=None, similarity_score=None)"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "rl(\"how's the weather today?\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Both are classified accurately, what if we send a query that is unrelated to our existing `Route` objects?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "2025-04-08 09:51:29 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.voyageai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
+      "\u001b[92m09:51:29 - LiteLLM:INFO\u001b[0m: utils.py:1177 - Wrapper: Completed Call, calling success_handler\n",
+      "2025-04-08 09:51:29 - LiteLLM - INFO - utils.py:1177 - wrapper() - Wrapper: Completed Call, calling success_handler\n",
+      "\u001b[92m09:51:29 - LiteLLM:INFO\u001b[0m: cost_calculator.py:622 - selected model name for cost calculation: voyage/voyage-3\n",
+      "2025-04-08 09:51:29 - LiteLLM - INFO - cost_calculator.py:622 - completion_cost() - selected model name for cost calculation: voyage/voyage-3\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "RouteChoice(name=None, function_call=None, similarity_score=None)"
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "rl(\"I'm interested in learning about llama 2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In this case, we return `None` because no matches were identified. We always recommend optimizing your `RouteLayer` for optimal performance, you can see how in [this notebook](https://github.com/aurelio-labs/semantic-router/blob/main/docs/06-threshold-optimization.ipynb)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "---"
+   ]
+  }
+ ],
+ "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.13.2"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/semantic_router/encoders/__init__.py b/semantic_router/encoders/__init__.py
index c34138ee..ab051fd9 100644
--- a/semantic_router/encoders/__init__.py
+++ b/semantic_router/encoders/__init__.py
@@ -10,11 +10,13 @@
 from semantic_router.encoders.fastembed import FastEmbedEncoder
 from semantic_router.encoders.google import GoogleEncoder
 from semantic_router.encoders.huggingface import HFEndpointEncoder, HuggingFaceEncoder
+from semantic_router.encoders.jina import JinaEncoder
 from semantic_router.encoders.litellm import LiteLLMEncoder
 from semantic_router.encoders.mistral import MistralEncoder
 from semantic_router.encoders.openai import OpenAIEncoder
 from semantic_router.encoders.tfidf import TfidfEncoder
 from semantic_router.encoders.vit import VitEncoder
+from semantic_router.encoders.voyage import VoyageEncoder
 from semantic_router.schema import EncoderType, SparseEmbedding
 
 __all__ = [
@@ -35,6 +37,8 @@
     "GoogleEncoder",
     "BedrockEncoder",
     "LiteLLMEncoder",
+    "VoyageEncoder",
+    "JinaEncoder",
 ]
 
 
@@ -68,6 +72,10 @@ def __init__(self, type: str, name: Optional[str]):
             self.model = HuggingFaceEncoder(name=name)
         elif self.type == EncoderType.MISTRAL:
             self.model = MistralEncoder(name=name)
+        elif self.type == EncoderType.VOYAGE:
+            self.model = VoyageEncoder(name=name)
+        elif self.type == EncoderType.JINA:
+            self.model = JinaEncoder(name=name)
         elif self.type == EncoderType.VIT:
             self.model = VitEncoder(name=name)
         elif self.type == EncoderType.CLIP:
diff --git a/semantic_router/encoders/jina.py b/semantic_router/encoders/jina.py
new file mode 100644
index 00000000..21b20811
--- /dev/null
+++ b/semantic_router/encoders/jina.py
@@ -0,0 +1,34 @@
+"""This file contains the JinaEncoder class which is used to encode text using Jina"""
+
+from semantic_router.encoders.litellm import LiteLLMEncoder
+from semantic_router.utils.defaults import EncoderDefault
+
+
+class JinaEncoder(LiteLLMEncoder):
+    """Class to encode text using Jina. Requires a Jina API key from
+    https://jina.ai/api-keys/"""
+
+    type: str = "jina"
+
+    def __init__(
+        self,
+        name: str | None = None,
+        api_key: str | None = None,
+        score_threshold: float = 0.4,
+    ):
+        """Initialize the JinaEncoder.
+
+        :param name: The name of the embedding model to use such as "jina-embeddings-v3".
+        :param jina_api_key: The Jina API key.
+        :type jina_api_key: str
+        """
+
+        if name is None:
+            name = f"jina_ai/{EncoderDefault.JINA.value['embedding_model']}"
+        elif not name.startswith("jina_ai/"):
+            name = f"jina_ai/{name}"
+        super().__init__(
+            name=name,
+            score_threshold=score_threshold,
+            api_key=api_key,
+        )
diff --git a/semantic_router/encoders/voyage.py b/semantic_router/encoders/voyage.py
new file mode 100644
index 00000000..b6bbcae5
--- /dev/null
+++ b/semantic_router/encoders/voyage.py
@@ -0,0 +1,85 @@
+"""This file contains the VoyageEncoder class which is used to encode text using Voyage"""
+
+import litellm
+
+from semantic_router.encoders.litellm import LiteLLMEncoder, litellm_to_list
+from semantic_router.utils.defaults import EncoderDefault
+
+
+class VoyageEncoder(LiteLLMEncoder):
+    """Class to encode text using Voyage. Requires a Voyage API key from
+    https://voyageai.com/api-keys/"""
+
+    type: str = "voyage"
+
+    def __init__(
+        self,
+        name: str | None = None,
+        api_key: str | None = None,
+        score_threshold: float = 0.4,
+    ):
+        """Initialize the VoyageEncoder.
+
+        :param name: The name of the embedding model to use such as "voyage-embed".
+        :type name: str
+        :param voyage_api_key: The Voyage API key.
+        :type voyage_api_key: str
+        """
+
+        if name is None:
+            name = f"voyage/{EncoderDefault.VOYAGE.value['embedding_model']}"
+        elif not name.startswith("voyage/"):
+            name = f"voyage/{name}"
+        super().__init__(
+            name=name,
+            score_threshold=score_threshold,
+            api_key=api_key,
+        )
+
+    def encode_queries(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = litellm.embedding(
+                input=docs,
+                input_type="query",
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Voyage API call failed. Error: {e}") from e
+
+    def encode_documents(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = litellm.embedding(
+                input=docs,
+                input_type="document",
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Voyage API call failed. Error: {e}") from e
+
+    async def aencode_queries(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = await litellm.aembedding(
+                input=docs,
+                input_type="query",
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Voyage API call failed. Error: {e}") from e
+
+    async def aencode_documents(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = await litellm.aembedding(
+                input=docs,
+                input_type="document",
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Voyage API call failed. Error: {e}") from e
diff --git a/semantic_router/schema.py b/semantic_router/schema.py
index 3dc1cc36..2fae8bb1 100644
--- a/semantic_router/schema.py
+++ b/semantic_router/schema.py
@@ -28,6 +28,8 @@ class EncoderType(Enum):
     GOOGLE = "google"
     BEDROCK = "bedrock"
     LITELLM = "litellm"
+    JINA = "jina_ai"
+    VOYAGE = "voyage"
 
 
 class EncoderInfo(BaseModel):
@@ -72,6 +74,14 @@ def to_mistral(self):
         """Convert the message to a Mistral-compatible format."""
         return {"role": self.role, "content": self.content}
 
+    def to_voyage(self):
+        """Convert the message to a Voyage-compatible format."""
+        return {"role": self.role, "content": self.content}
+
+    def to_jina(self):
+        """Convert the message to a Jina-compatible format."""
+        return {"role": self.role, "content": self.content}
+
     def __str__(self):
         """Convert the message to a string."""
         return f"{self.role}: {self.content}"
diff --git a/semantic_router/utils/defaults.py b/semantic_router/utils/defaults.py
index 7915ac9d..f1365bed 100644
--- a/semantic_router/utils/defaults.py
+++ b/semantic_router/utils/defaults.py
@@ -21,6 +21,14 @@ class EncoderDefault(Enum):
         "embedding_model": os.getenv("MISTRAL_MODEL_NAME", "mistral-embed"),
         "language_model": os.getenv("MISTRALAI_CHAT_MODEL_NAME", "mistral-tiny"),
     }
+    VOYAGE = {
+        "embedding_model": os.getenv("VOYAGE_MODEL_NAME", "voyage-3-lite"),
+        "language_model": os.getenv("VOYAGE_CHAT_MODEL_NAME", "voyage-3-lite"),
+    }
+    JINA = {
+        "embedding_model": os.getenv("JINA_MODEL_NAME", "jina-embeddings-v3"),
+        "language_model": os.getenv("JINA_CHAT_MODEL_NAME", "ReaderLM-v2"),
+    }
     AZURE = {
         "embedding_model": os.getenv("AZURE_OPENAI_MODEL", "text-embedding-3-small"),
         "language_model": os.getenv("OPENAI_CHAT_MODEL_NAME", "gpt-4o"),
diff --git a/tests/unit/encoders/test_lite_encoders.py b/tests/unit/encoders/test_lite_encoders.py
index 231a9e42..df6a38d6 100644
--- a/tests/unit/encoders/test_lite_encoders.py
+++ b/tests/unit/encoders/test_lite_encoders.py
@@ -4,7 +4,13 @@
 import pytest
 from litellm.types.utils import Embedding
 
-from semantic_router.encoders import CohereEncoder, LiteLLMEncoder, MistralEncoder
+from semantic_router.encoders import (
+    CohereEncoder,
+    JinaEncoder,
+    LiteLLMEncoder,
+    MistralEncoder,
+    VoyageEncoder,
+)
 
 matrix = [
     [
@@ -21,7 +27,27 @@
         "COHERE_API_KEY",
         CohereEncoder,
     ],
-    ["mistral", "mistral-embed", "mistral-embed", "MISTRAL_API_KEY", MistralEncoder],
+    [
+        "mistral",
+        "mistral-embed",
+        "mistral-embed",
+        "MISTRAL_API_KEY",
+        MistralEncoder,
+    ],
+    [
+        "jina_ai",
+        "jina-embeddings-v3",
+        "jina-embeddings-v3",
+        "JINA_AI_API_KEY",
+        JinaEncoder,
+    ],
+    [
+        "voyage",
+        "voyage-3",
+        "voyage-3",
+        "VOYAGE_API_KEY",
+        VoyageEncoder,
+    ],
 ]
 
 


Reviews:
user: login: jamescalam
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T10:46:11Z
user: login: jamescalam
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T10:46:36Z
user: login: jamescalam
body: I think we need a small change to jina encoder — rest looks good
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-09T10:47:18Z
user: login: jamescalam
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T11:36:01Z
user: login: jamescalam
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T20:27:52Z

Review comments:
diff_hunk: @@ -0,0 +1,80 @@
+"""This file contains the JinaEncoder class which is used to encode text using Jina"""
+
+import litellm
+
+from semantic_router.encoders.litellm import LiteLLMEncoder, litellm_to_list
+from semantic_router.utils.defaults import EncoderDefault
+
+
+class JinaEncoder(LiteLLMEncoder):
+    """Class to encode text using Jina. Requires a Jina API key from
+    https://jina.ai/api-keys/"""
+
+    type: str = "jina"
+
+    def __init__(
+        self,
+        name: str | None = None,
+        api_key: str | None = None,
+        score_threshold: float = 0.4,
+    ):
+        """Initialize the JinaEncoder.
+
+        :param name: The name of the embedding model to use such as "jina-embeddings-v3".
+        :param jina_api_key: The Jina API key.
+        :type jina_api_key: str
+        """
+
+        if name is None:
+            name = f"jina_ai/{EncoderDefault.JINA.value['embedding_model']}"
+        elif not name.startswith("jina_ai/"):
+            name = f"jina_ai/{name}"
+        super().__init__(
+            name=name,
+            score_threshold=score_threshold,
+            api_key=api_key,
+        )
+
+    def encode_queries(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = litellm.embedding(
+                input=docs,
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Jina API call failed. Error: {e}") from e
+
+    def encode_documents(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = litellm.embedding(
+                input=docs,
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Jina API call failed. Error: {e}") from e
+
+    async def aencode_queries(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = await litellm.aembedding(
+                input=docs,
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Jina API call failed. Error: {e}") from e
+
+    async def aencode_documents(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = await litellm.aembedding(
+                input=docs,
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Jina API call failed. Error: {e}") from e
path: semantic_router/encoders/jina.py
user: login: jamescalam
body: these methods look the same as they are in `LiteLLMEncoder` — if so they're inherited so no need to add them here
created_at: 2025-04-09T10:46:11Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,85 @@
+"""This file contains the VoyageEncoder class which is used to encode text using Voyage"""
+
+import litellm
+
+from semantic_router.encoders.litellm import LiteLLMEncoder, litellm_to_list
+from semantic_router.utils.defaults import EncoderDefault
+
+
+class VoyageEncoder(LiteLLMEncoder):
+    """Class to encode text using Voyage. Requires a Voyage API key from
+    https://voyageai.com/api-keys/"""
+
+    type: str = "voyage"
+
+    def __init__(
+        self,
+        name: str | None = None,
+        api_key: str | None = None,
+        score_threshold: float = 0.4,
+    ):
+        """Initialize the VoyageEncoder.
+
+        :param name: The name of the embedding model to use such as "voyage-embed".
+        :type name: str
+        :param voyage_api_key: The Voyage API key.
+        :type voyage_api_key: str
+        """
+
+        if name is None:
+            name = f"voyage/{EncoderDefault.VOYAGE.value['embedding_model']}"
+        elif not name.startswith("voyage/"):
+            name = f"voyage/{name}"
+        super().__init__(
+            name=name,
+            score_threshold=score_threshold,
+            api_key=api_key,
+        )
+
+    def encode_queries(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = litellm.embedding(
+                input=docs,
+                input_type="query",
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Voyage API call failed. Error: {e}") from e
+
+    def encode_documents(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = litellm.embedding(
+                input=docs,
+                input_type="document",
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Voyage API call failed. Error: {e}") from e
+
+    async def aencode_queries(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = await litellm.aembedding(
+                input=docs,
+                input_type="query",
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Voyage API call failed. Error: {e}") from e
+
+    async def aencode_documents(self, docs: list[str], **kwargs) -> list[list[float]]:
+        try:
+            embeds = await litellm.aembedding(
+                input=docs,
+                input_type="document",
+                model=f"{self.type}/{self.name}",
+                **kwargs,
+            )
+            return litellm_to_list(embeds)
+        except Exception as e:
+            raise ValueError(f"Voyage API call failed. Error: {e}") from e
path: semantic_router/encoders/voyage.py
user: login: jamescalam
body: looks good
created_at: 2025-04-09T10:46:36Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-08T09:01:51Z
body: ## PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

<table>
<tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 4 🔵🔵🔵🔵⚪</td></tr>
<tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr>
<tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr>
<tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br>

<details><summary><a href='https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-d889d6b45bcb301ba87646cbf443aa759645826d27605b7b6de6137a55c7613eR60-R65'><strong>Error Message</strong></a>

The API key error message uses a literal placeholder instead of interpolating the actual uppercased type. Consider updating the string to use dynamic formatting for clarity.
</summary>

```python
if api_key is None:
    raise ValueError(
        "Expected API key via `api_key` parameter or `{self.type.upper()}_API_KEY` "
        "environment variable."
    )
os.environ[self.type.upper() + "_API_KEY"] = api_key
```

</details>

<details><summary><a href='https://github.com/aurelio-labs/semantic-router/pull/581/files#diff-63f98a7f33cadbad5e7fc0ad2984d0cb0f16535da13c3b99508ea6fe9e3a6ba8R52-R56'><strong>Deprecation Notice</strong></a>

Deprecated methods (such as _initialize_client) are still present with TODO comments. Please ensure their removal is well-documented and that users are guided toward the new litellm-based implementation.
</summary>

```python
# TODO: deprecated, to remove in v0.2.0
@deprecated("_initialize_client method no longer required")
def _initialize_client(self, cohere_api_key: str | None = None):
    """Initializes the Cohere client.

```

</details>

</td></tr>
</table>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-08T09:02:22Z
body: ## PR Code Suggestions ✨

No code suggestions found for the PR.
author_association: NONE
user: login: codecov[bot]
created_at: 2025-04-09T08:46:47Z
body: ## [Codecov](https://app.codecov.io/gh/aurelio-labs/semantic-router/pull/581?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs) Report
Attention: Patch coverage is `64.40678%` with `21 lines` in your changes missing coverage. Please review.
> Project coverage is 73.70%. Comparing base [(`37d4eee`)](https://app.codecov.io/gh/aurelio-labs/semantic-router/commit/37d4eee3942f40a7c5dfd63882cf4567c3b58d3d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs) to head [(`816e29d`)](https://app.codecov.io/gh/aurelio-labs/semantic-router/commit/816e29d57d0f8c349951a35bc6e9327496256aa7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs).
> Report is 13 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/aurelio-labs/semantic-router/pull/581?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs) | Patch % | Lines |
|---|---|---|
| [semantic\_router/encoders/voyage.py](https://app.codecov.io/gh/aurelio-labs/semantic-router/pull/581?src=pr&el=tree&filepath=semantic_router%2Fencoders%2Fvoyage.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs#diff-c2VtYW50aWNfcm91dGVyL2VuY29kZXJzL3ZveWFnZS5weQ==) | 57.14% | [15 Missing :warning: ](https://app.codecov.io/gh/aurelio-labs/semantic-router/pull/581?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs) |
| [semantic\_router/encoders/\_\_init\_\_.py](https://app.codecov.io/gh/aurelio-labs/semantic-router/pull/581?src=pr&el=tree&filepath=semantic_router%2Fencoders%2F__init__.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs#diff-c2VtYW50aWNfcm91dGVyL2VuY29kZXJzL19faW5pdF9fLnB5) | 33.33% | [4 Missing :warning: ](https://app.codecov.io/gh/aurelio-labs/semantic-router/pull/581?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs) |
| [semantic\_router/schema.py](https://app.codecov.io/gh/aurelio-labs/semantic-router/pull/581?src=pr&el=tree&filepath=semantic_router%2Fschema.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs#diff-c2VtYW50aWNfcm91dGVyL3NjaGVtYS5weQ==) | 66.66% | [2 Missing :warning: ](https://app.codecov.io/gh/aurelio-labs/semantic-router/pull/581?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #581      +/-   ##
==========================================
- Coverage   73.83%   73.70%   -0.14%     
==========================================
  Files          45       47       +2     
  Lines        4219     4278      +59     
==========================================
+ Hits         3115     3153      +38     
- Misses       1104     1125      +21     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/aurelio-labs/semantic-router/pull/581?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aurelio-labs).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE