Repository: chroma-core/chroma
Pull request #4244: [ENH] Update Jina embedding function to support all models and configurations
Author: login: jairad26
Created: 2025-04-09T15:28:09Z | Merged: 2025-04-17T18:50:39Z

## Description of changes

This PR updates the Jina embedding function in both python and typescript to make v3 the default embedding model, and adds support for all configuration attributes that Jina supports.
this includes: late_chunking, task, truncate, dimensions, embedding_type, and normalized

## Test plan
*How are these changes tested?*

- [ ] Tests pass locally with `pytest` for python, `yarn test` for js, `cargo test` for rust

## Documentation Changes
*Are all docstrings for user-facing APIs updated if required? Do we need to make documentation changes in the [docs repository](https://github.com/chroma-core/docs)?*



Diff:
diff --git a/chromadb/utils/embedding_functions/jina_embedding_function.py b/chromadb/utils/embedding_functions/jina_embedding_function.py
index 25aabc27883..2ce5e36fbd6 100644
--- a/chromadb/utils/embedding_functions/jina_embedding_function.py
+++ b/chromadb/utils/embedding_functions/jina_embedding_function.py
@@ -16,6 +16,12 @@ def __init__(
         api_key: Optional[str] = None,
         model_name: str = "jina-embeddings-v2-base-en",
         api_key_env_var: str = "CHROMA_JINA_API_KEY",
+        task: Optional[str] = None,
+        late_chunking: Optional[bool] = None,
+        truncate: Optional[bool] = None,
+        dimensions: Optional[int] = None,
+        embedding_type: Optional[str] = None,
+        normalized: Optional[bool] = None,
     ):
         """
         Initialize the JinaEmbeddingFunction.
@@ -25,6 +31,19 @@ def __init__(
                 Defaults to "CHROMA_JINA_API_KEY".
             model_name (str, optional): The name of the model to use for text embeddings.
                 Defaults to "jina-embeddings-v2-base-en".
+            task (str, optional): The task to use for the Jina AI API.
+                Defaults to None.
+            late_chunking (bool, optional): Whether to use late chunking for the Jina AI API.
+                Defaults to None.
+            truncate (bool, optional): Whether to truncate the Jina AI API.
+                Defaults to None.
+            dimensions (int, optional): The number of dimensions to use for the Jina AI API.
+                Defaults to None.
+            embedding_type (str, optional): The type of embedding to use for the Jina AI API.
+                Defaults to None.
+            normalized (bool, optional): Whether to normalize the Jina AI API.
+                Defaults to None.
+
         """
         try:
             import httpx
@@ -40,6 +59,14 @@ def __init__(
 
         self.model_name = model_name
 
+        # Initialize optional attributes to None
+        self.task = task
+        self.late_chunking = late_chunking
+        self.truncate = truncate
+        self.dimensions = dimensions
+        self.embedding_type = embedding_type
+        self.normalized = normalized
+
         self._api_url = "https://api.jina.ai/v1/embeddings"
         self._session = httpx.Client()
         self._session.headers.update(
@@ -51,7 +78,7 @@ def __call__(self, input: Documents) -> Embeddings:
         Get the embeddings for a list of texts.
 
         Args:
-            input (Documents): A list of texts or images to get embeddings for.
+            input (Documents): A list of texts to get embeddings for.
 
         Returns:
             Embeddings: The embeddings for the texts.
@@ -64,10 +91,31 @@ def __call__(self, input: Documents) -> Embeddings:
         if not all(isinstance(item, str) for item in input):
             raise ValueError("Jina AI only supports text documents, not images")
 
+        payload: Dict[str, Any] = {
+            "input": input,
+            "model": self.model_name,
+        }
+
+        if self.task is not None:
+            payload["task"] = self.task
+
+        if self.late_chunking is not None:
+            payload["late_chunking"] = self.late_chunking
+
+        if self.truncate is not None:
+            payload["truncate"] = self.truncate
+
+        if self.dimensions is not None:
+            payload["dimensions"] = self.dimensions
+
+        if self.embedding_type is not None:
+            payload["embedding_type"] = self.embedding_type
+
+        if self.normalized is not None:
+            payload["normalized"] = self.normalized
+
         # Call Jina AI Embedding API
-        resp = self._session.post(
-            self._api_url, json={"input": input, "model": self.model_name}
-        ).json()
+        resp = self._session.post(self._api_url, json=payload).json()
 
         if "data" not in resp:
             raise RuntimeError(resp.get("detail", "Unknown error"))
@@ -97,16 +145,38 @@ def supported_spaces(self) -> List[Space]:
     def build_from_config(config: Dict[str, Any]) -> "EmbeddingFunction[Documents]":
         api_key_env_var = config.get("api_key_env_var")
         model_name = config.get("model_name")
+        task = config.get("task")
+        late_chunking = config.get("late_chunking")
+        truncate = config.get("truncate")
+        dimensions = config.get("dimensions")
+        embedding_type = config.get("embedding_type")
+        normalized = config.get("normalized")
 
         if api_key_env_var is None or model_name is None:
-            assert False, "This code should not be reached"
+            assert False, "This code should not be reached"  # this is for type checking
 
         return JinaEmbeddingFunction(
-            api_key_env_var=api_key_env_var, model_name=model_name
+            api_key_env_var=api_key_env_var,
+            model_name=model_name,
+            task=task,
+            late_chunking=late_chunking,
+            truncate=truncate,
+            dimensions=dimensions,
+            embedding_type=embedding_type,
+            normalized=normalized,
         )
 
     def get_config(self) -> Dict[str, Any]:
-        return {"api_key_env_var": self.api_key_env_var, "model_name": self.model_name}
+        return {
+            "api_key_env_var": self.api_key_env_var,
+            "model_name": self.model_name,
+            "task": self.task,
+            "late_chunking": self.late_chunking,
+            "truncate": self.truncate,
+            "dimensions": self.dimensions,
+            "embedding_type": self.embedding_type,
+            "normalized": self.normalized,
+        }
 
     def validate_config_update(
         self, old_config: Dict[str, Any], new_config: Dict[str, Any]
diff --git a/clients/js/packages/chromadb-core/src/embeddings/JinaEmbeddingFunction.ts b/clients/js/packages/chromadb-core/src/embeddings/JinaEmbeddingFunction.ts
index d2c74479c69..fe54dc31a0c 100644
--- a/clients/js/packages/chromadb-core/src/embeddings/JinaEmbeddingFunction.ts
+++ b/clients/js/packages/chromadb-core/src/embeddings/JinaEmbeddingFunction.ts
@@ -3,8 +3,25 @@ import { validateConfigSchema } from "../schemas/schemaUtils";
 type StoredConfig = {
   api_key_env_var: string;
   model_name: string;
+  task?: string;
+  late_chunking?: boolean;
+  truncate?: boolean;
+  dimensions?: number;
+  embedding_type?: string;
+  normalized?: boolean;
 };
 
+interface JinaRequestBody {
+  input: string[];
+  model: string;
+  task?: string;
+  late_chunking?: boolean;
+  truncate?: boolean;
+  dimensions?: number;
+  embedding_type?: string;
+  normalized?: boolean;
+}
+
 export class JinaEmbeddingFunction implements IEmbeddingFunction {
   name = "jina";
 
@@ -12,15 +29,33 @@ export class JinaEmbeddingFunction implements IEmbeddingFunction {
   private model_name: string;
   private api_url: string;
   private headers: { [key: string]: string };
+  private task: string | undefined;
+  private late_chunking: boolean | undefined;
+  private truncate: boolean | undefined;
+  private dimensions: number | undefined;
+  private embedding_type: string | undefined;
+  private normalized: boolean | undefined;
 
   constructor({
     jinaai_api_key,
     model_name = "jina-embeddings-v2-base-en",
     api_key_env_var = "JINAAI_API_KEY",
+    task,
+    late_chunking,
+    truncate,
+    dimensions,
+    embedding_type,
+    normalized,
   }: {
     jinaai_api_key?: string;
     model_name?: string;
     api_key_env_var: string;
+    task?: string;
+    late_chunking?: boolean;
+    truncate?: boolean;
+    dimensions?: number;
+    embedding_type?: string;
+    normalized?: boolean;
   }) {
     const apiKey = jinaai_api_key ?? process.env[api_key_env_var];
     if (!apiKey) {
@@ -31,6 +66,12 @@ export class JinaEmbeddingFunction implements IEmbeddingFunction {
 
     this.model_name = model_name;
     this.api_key_env_var = api_key_env_var;
+    this.task = task;
+    this.late_chunking = late_chunking;
+    this.truncate = truncate;
+    this.dimensions = dimensions;
+    this.embedding_type = embedding_type;
+    this.normalized = normalized;
 
     this.api_url = "https://api.jina.ai/v1/embeddings";
     this.headers = {
@@ -41,14 +82,40 @@ export class JinaEmbeddingFunction implements IEmbeddingFunction {
   }
 
   public async generate(texts: string[]) {
+    let json_body: JinaRequestBody = {
+      input: texts,
+      model: this.model_name,
+    };
+
+    if (this.task) {
+      json_body.task = this.task;
+    }
+
+    if (this.late_chunking) {
+      json_body.late_chunking = this.late_chunking;
+    }
+
+    if (this.truncate) {
+      json_body.truncate = this.truncate;
+    }
+
+    if (this.dimensions) {
+      json_body.dimensions = this.dimensions;
+    }
+
+    if (this.embedding_type) {
+      json_body.embedding_type = this.embedding_type;
+    }
+
+    if (this.normalized) {
+      json_body.normalized = this.normalized;
+    }
+
     try {
       const response = await fetch(this.api_url, {
         method: "POST",
         headers: this.headers,
-        body: JSON.stringify({
-          input: texts,
-          model: this.model_name,
-        }),
+        body: JSON.stringify(json_body),
       });
 
       const data = (await response.json()) as { data: any[]; detail: string };
@@ -73,6 +140,12 @@ export class JinaEmbeddingFunction implements IEmbeddingFunction {
     return new JinaEmbeddingFunction({
       model_name: config.model_name,
       api_key_env_var: config.api_key_env_var,
+      task: config.task,
+      late_chunking: config.late_chunking,
+      truncate: config.truncate,
+      dimensions: config.dimensions,
+      embedding_type: config.embedding_type,
+      normalized: config.normalized,
     });
   }
 
@@ -80,6 +153,12 @@ export class JinaEmbeddingFunction implements IEmbeddingFunction {
     return {
       api_key_env_var: this.api_key_env_var,
       model_name: this.model_name,
+      task: this.task,
+      late_chunking: this.late_chunking,
+      truncate: this.truncate,
+      dimensions: this.dimensions,
+      embedding_type: this.embedding_type,
+      normalized: this.normalized,
     };
   }
 
diff --git a/docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md b/docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
index ac24c397aa9..25f4e7d4f13 100644
--- a/docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
+++ b/docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
@@ -12,10 +12,10 @@ Chroma provides a convenient wrapper around JinaAI's embedding API. This embeddi
 {% Tab label="python" %}
 
 ```python
-import chromadb.utils.embedding_functions as embedding_functions
-jinaai_ef = embedding_functions.JinaEmbeddingFunction(
+from chromadb.utils.embedding_functions import JinaEmbeddingFunction
+jinaai_ef = JinaEmbeddingFunction(
                 api_key="YOUR_API_KEY",
-                model_name="jina-embeddings-v2-base-en"
+                model_name="jina-embeddings-v2-base-en",
             )
 jinaai_ef(input=["This is my first text to embed", "This is my second document"])
 ```
@@ -45,3 +45,55 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+{% note type="tip" title="" %}
+
+Jina has added new attributes on embedding functions, including `task`, `late_chunking`, `truncate`, `dimensions`, `embedding_type`, and `normalized`. See [JinaAI](https://jina.ai/embeddings/) for references on which models support these attributes.
+
+{% /note %}
+
+### Late Chunking Example
+
+jina-embeddings-v3 supports [Late Chunking](https://jina.ai/news/late-chunking-in-long-context-embedding-models/), a technique to leverage the model’s long-context capabilities for generating contextual chunk embeddings. Include `late_chunking=True` in your request to enable contextual chunked representation. When set to true, Jina AI API will concatenate all sentences in the input field and feed them as a single string to the model. Internally, the model embeds this long concatenated string and then performs late chunking, returning a list of embeddings that matches the size of the input list.
+
+{% tabs group="code-lang" hideTabs=true %}
+{% Tab label="python" %}
+
+```python
+from chromadb.utils.embedding_functions import JinaEmbeddingFunction
+jinaai_ef = JinaEmbeddingFunction(
+                api_key="YOUR_API_KEY",
+                model_name="jina-embeddings-v3",
+                late_chunking=True,
+                task="text-matching",
+            )
+
+collection = client.create_collection(name="late_chunking", embedding_function=jinaai_ef)
+
+documents = [
+    'Berlin is the capital and largest city of Germany.',
+    'The city has a rich history dating back centuries.',
+    'It was founded in the 13th century and has been a significant cultural and political center throughout European history.',
+]
+
+ids = [str(i+1) for i in range(len(documents))]
+
+collection.add(ids=ids, documents=documents)
+
+results = normal_collection.query(
+    query_texts=["What is Berlin's population?", "When was Berlin founded?"],
+    n_results=1,
+)
+
+print(results)
+```
+{% /Tab %}
+{% /tabs %}
+
+### Task parameter
+`jina-embeddings-v3` has been trained with 5 task-specific adapters for different embedding uses. Include task in your request to optimize your downstream application:
+- `retrieval.query`: Used to encode user queries or questions in retrieval tasks.
+- `retrieval.passage`: Used to encode large documents in retrieval tasks at indexing time.
+- `classification`: Used to encode text for text classification tasks.
+- `text-matching`: Used to encode text for similarity matching, such as measuring similarity between two sentences.
+- `separation`: Used for clustering or reranking tasks.
diff --git a/examples/use_with/jina/late_chunking.ipynb b/examples/use_with/jina/late_chunking.ipynb
new file mode 100644
index 00000000000..bba8699d392
--- /dev/null
+++ b/examples/use_with/jina/late_chunking.ipynb
@@ -0,0 +1,209 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Late Chunking\n",
+    "Late chunking is a technique to leverage the model’s long-context capabilities for generating contextual chunk embeddings. Include `late_chunking=True` in your request to enable contextual chunked representation. When set to true, Jina AI API will concatenate all sentences in the input field and feed them as a single string to the model. Internally, the model embeds this long concatenated string and then performs late chunking, returning a list of embeddings that matches the size of the input list."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# set up chromadb and jina API key\n",
+    "! pip install chromadb --quiet\n",
+    "! pip install httpx --quiet\n",
+    "! pip install pandas --quiet\n",
+    "import os\n",
+    "import chromadb\n",
+    "import getpass\n",
+    "import pandas as pd\n",
+    "\n",
+    "from chromadb.utils.embedding_functions import JinaEmbeddingFunction\n",
+    "from chromadb.api.types import QueryResult\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def convert_to_df(qr: QueryResult):\n",
+    "    df = pd.DataFrame(qr[\"ids\"], columns=[\"id\"])\n",
+    "    df[\"document\"] = qr[\"documents\"]\n",
+    "    df[\"distance\"] = qr[\"distances\"]\n",
+    "    return df\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "os.environ[\"CHROMA_JINA_API_KEY\"] = getpass.getpass(\"Jina API Key:\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Setup\n",
+    "\n",
+    "Let's set up two collections to compare the difference in retrieval with late chunking on and off using `jina-embeddings-v3`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "client = chromadb.EphemeralClient()\n",
+    "\n",
+    "# create collection with Jina embedding function with late chunking enabled\n",
+    "late_chunking_collection = client.create_collection(name=\"late_chunking\", configuration={\n",
+    "    \"embedding_function\": JinaEmbeddingFunction(\n",
+    "        model_name=\"jina-embeddings-v3\",\n",
+    "        # enable late chunking\n",
+    "        late_chunking=True,\n",
+    "        task=\"text-matching\",\n",
+    "    )\n",
+    "})\n",
+    "\n",
+    "# create collection with Jina embedding function with late chunking disabled\n",
+    "normal_collection = client.create_collection(name=\"normal\", configuration={\n",
+    "    \"embedding_function\": JinaEmbeddingFunction(\n",
+    "        model_name=\"jina-embeddings-v3\",\n",
+    "        task=\"text-matching\",\n",
+    "    )\n",
+    "})\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Documents & When to use Late Chunking\n",
+    "\n",
+    "Late chunking works best with Chroma when a group of documents share similar context. In this case, all documents are about Berlin, with documents referring to \"It\" and \"The city\". Normally, the model will not have the context to understand these are referring to Berlin, but with late chunking that context is now imbued with the other documents' embeddings.\n",
+    "\n",
+    "For best retrieval results, try to separate differing topics with separate adds when using late chunking. For example, if the first set of documents talks about Berlin, and the next refers to computer operating systems, it would be best to not contaminate one set of documents' context with the other."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# set up documents\n",
+    "documents = [\n",
+    "    'Berlin is the capital and largest city of Germany.', \n",
+    "    'The city has a rich history dating back centuries.', \n",
+    "    'It was founded in the 13th century and has been a significant cultural and political center throughout European history.', \n",
+    "    'The metropolis experienced dramatic changes during the 20th century, including two world wars and a period of division.', \n",
+    "    'After reunification, it underwent extensive reconstruction and modernization efforts.', \n",
+    "    'Its population reached 3.85 million inhabitants in 2023, making it the most populous urban area in the country.', \n",
+    "    'This represents a significant increase from previous decades, driven largely by immigration and economic opportunities.', \n",
+    "    'The city is known for its vibrant cultural scene and historical significance.', \n",
+    "    'Many tourists visit its famous landmarks each year, contributing significantly to the local economy.', \n",
+    "    'The Brandenburg Gate stands as its most iconic symbol.'\n",
+    "]\n",
+    "\n",
+    "ids = [str(i+1) for i in range(len(documents))]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# add documents to the normal collection\n",
+    "normal_collection.add(\n",
+    "    ids=ids,\n",
+    "    documents=documents,\n",
+    ")\n",
+    "\n",
+    "# add documents to the late chunking collection\n",
+    "late_chunking_collection.add(\n",
+    "    ids=ids,\n",
+    "    documents=documents,\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Normal Collection Results:\n",
+      "  id                                           document              distance\n",
+      "0  1  [Berlin is the capital and largest city of Ger...  [0.7308309078216553]\n",
+      "1  1  [Berlin is the capital and largest city of Ger...  [0.8990052342414856]\n",
+      "\n",
+      "--------------------------------\n",
+      "\n",
+      "Late Chunking Collection Results:\n",
+      "  id                                           document              distance\n",
+      "0  6  [Its population reached 3.85 million inhabitan...  [0.5808092951774597]\n",
+      "1  3  [It was founded in the 13th century and has be...  [0.7019132375717163]\n"
+     ]
+    }
+   ],
+   "source": [
+    "# let's query the normal collection and see the results\n",
+    "results = normal_collection.query(\n",
+    "    query_texts=[\"What is Berlin's population?\", \"When was Berlin founded?\"],\n",
+    "    n_results=1,\n",
+    ")\n",
+    "\n",
+    "print(\"Normal Collection Results:\")\n",
+    "print(convert_to_df(results))\n",
+    "\n",
+    "\n",
+    "print(\"\\n--------------------------------\\n\")\n",
+    "\n",
+    "# let's query the late chunking collection and see the results\n",
+    "results = late_chunking_collection.query(\n",
+    "    query_texts=[\"What is Berlin's population?\", \"When was Berlin founded?\"],\n",
+    "    n_results=1,\n",
+    ")\n",
+    "\n",
+    "print(\"Late Chunking Collection Results:\")\n",
+    "print(convert_to_df(results))"
+   ]
+  }
+ ],
+ "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.12.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/schemas/embedding_functions/jina.json b/schemas/embedding_functions/jina.json
index 987dffdd530..e9a9d9dfa41 100644
--- a/schemas/embedding_functions/jina.json
+++ b/schemas/embedding_functions/jina.json
@@ -12,6 +12,30 @@
     "api_key_env_var": {
       "type": "string",
       "description": "Parameter api_key_env_var for the jina embedding function"
+    },
+    "task": {
+      "type": "string",
+      "description": "Parameter task for the jina embedding function"
+    },
+    "late_chunking": {
+      "type": "boolean",
+      "description": "Parameter late_chunking for the jina embedding function"
+    },
+    "truncate": {
+      "type": "boolean",
+      "description": "Parameter truncate for the jina embedding function"
+    },
+    "dimensions": {
+      "type": "integer",
+      "description": "Parameter dimensions for the jina embedding function"
+    },
+    "embedding_type": {
+      "type": "string",
+      "description": "Parameter embedding_type for the jina embedding function"
+    },
+    "normalized": {
+      "type": "boolean",
+      "description": "Parameter normalized for the jina embedding function"
     }
   },
   "required": [


Reviews:
user: login: bwanglzu
body: thanks for your PR, much appreciated! I left some comments and looking forward to hear your feedback!
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-10T07:23:46Z
user: login: jairad26
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T15:44:38Z
user: login: jairad26
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T15:44:39Z
user: login: jairad26
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T15:44:54Z
user: login: jairad26
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T15:46:00Z
user: login: bwanglzu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-10T15:51:48Z
user: login: jairad26
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T15:58:23Z
user: login: jairad26
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T16:53:21Z
user: login: jairad26
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T17:32:46Z
user: login: HammadB
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T23:18:59Z
user: login: HammadB
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T23:19:19Z
user: login: jairad26
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T23:40:30Z
user: login: jairad26
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T23:41:11Z
user: login: bwanglzu
body: LGTM!
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-16T15:36:22Z

Review comments:
diff_hunk: @@ -25,6 +31,19 @@ def __init__(
                 Defaults to "CHROMA_JINA_API_KEY".
             model_name (str, optional): The name of the model to use for text embeddings.
                 Defaults to "jina-embeddings-v2-base-en".
+            task (str, optional): The task to use for the Jina AI API.
+                Defaults to None.
path: chromadb/utils/embedding_functions/jina_embedding_function.py
user: login: bwanglzu
body: do you think it make sense to change the default model from `jina-embeddings-v2-base-en` to `jina-embeddings-v3`? will it break ppl's user experience? If so the default task would be nice to set to `text-matching`
created_at: 2025-04-10T07:09:34Z
author_association: NONE
diff_hunk: @@ -25,6 +31,19 @@ def __init__(
                 Defaults to "CHROMA_JINA_API_KEY".
             model_name (str, optional): The name of the model to use for text embeddings.
                 Defaults to "jina-embeddings-v2-base-en".
+            task (str, optional): The task to use for the Jina AI API.
+                Defaults to None.
+            late_chunking (bool, optional): Whether to use late chunking for the Jina AI API.
+                Defaults to None.
+            truncate (bool, optional): Whether to truncate the Jina AI API.
+                Defaults to None.
+            dimensions (int, optional): The number of dimensions to use for the Jina AI API.
+                Defaults to None.
path: chromadb/utils/embedding_functions/jina_embedding_function.py
user: login: bwanglzu
body: side note, the above configurations (from task to dimensions) is only supported by jina-embeddings-v3 
created_at: 2025-04-10T07:11:10Z
author_association: NONE
diff_hunk: @@ -51,7 +78,7 @@ def __call__(self, input: Documents) -> Embeddings:
         Get the embeddings for a list of texts.
 
         Args:
-            input (Documents): A list of texts or images to get embeddings for.
+            input (Documents): A list of texts to get embeddings for.
path: chromadb/utils/embedding_functions/jina_embedding_function.py
user: login: bwanglzu
body: just a question ,is the current integration support multimodal embeddings? i.e. jina-clip-v1 and jina-clip-v2
created_at: 2025-04-10T07:12:04Z
author_association: NONE
diff_hunk: @@ -45,3 +45,40 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+### Late Chunking Example
+
+JinaAI supports late chunking on `jina-embeddings-v3`, which can enrich the context across the documents being inserted by combining multiple chunks.
+
+{% tabs group="code-lang" hideTabs=true %}
+{% Tab label="python" %}
+
+```python
+from chromadb.utils.embedding_functions import JinaEmbeddingFunction
+jinaai_ef = JinaEmbeddingFunction(
+                api_key="YOUR_API_KEY",
+                model_name="jina-embeddings-v3",
+                late_chunking=True,
+            )
path: docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
user: login: bwanglzu
body: note, when model being set to jina-embeddings-v3, user is expected to set a task to get optimal performance.

in this example (QA/Retrieval) at indexing time `task=retrieval.passage`, at searching time, `task=retrieval.query` would be best.

however this might leads to 2 separate JinaEmbeddingFunction instances.. not very elegant, another option is set `task=text-matching`, this should also give better result than not offering a task. We need to think about how to handle it better
created_at: 2025-04-10T07:18:22Z
author_association: NONE
diff_hunk: @@ -45,3 +45,40 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+### Late Chunking Example
+
+JinaAI supports late chunking on `jina-embeddings-v3`, which can enrich the context across the documents being inserted by combining multiple chunks.
+
+{% tabs group="code-lang" hideTabs=true %}
+{% Tab label="python" %}
+
+```python
+from chromadb.utils.embedding_functions import JinaEmbeddingFunction
+jinaai_ef = JinaEmbeddingFunction(
+                api_key="YOUR_API_KEY",
+                model_name="jina-embeddings-v3",
+                late_chunking=True,
+            )
path: docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
user: login: bwanglzu
body: the same is expected for the jupyter notebook below
created_at: 2025-04-10T07:22:59Z
author_association: NONE
diff_hunk: @@ -25,6 +31,19 @@ def __init__(
                 Defaults to "CHROMA_JINA_API_KEY".
             model_name (str, optional): The name of the model to use for text embeddings.
                 Defaults to "jina-embeddings-v2-base-en".
+            task (str, optional): The task to use for the Jina AI API.
+                Defaults to None.
+            late_chunking (bool, optional): Whether to use late chunking for the Jina AI API.
+                Defaults to None.
+            truncate (bool, optional): Whether to truncate the Jina AI API.
+                Defaults to None.
+            dimensions (int, optional): The number of dimensions to use for the Jina AI API.
+                Defaults to None.
path: chromadb/utils/embedding_functions/jina_embedding_function.py
user: login: jairad26
body: will add validation and doc string updates to inform, thanks
created_at: 2025-04-10T15:44:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -25,6 +31,19 @@ def __init__(
                 Defaults to "CHROMA_JINA_API_KEY".
             model_name (str, optional): The name of the model to use for text embeddings.
                 Defaults to "jina-embeddings-v2-base-en".
+            task (str, optional): The task to use for the Jina AI API.
+                Defaults to None.
path: chromadb/utils/embedding_functions/jina_embedding_function.py
user: login: jairad26
body: you're right that it'll break existing user's experiences, so not ideal for right now. we may update in the future though
created_at: 2025-04-10T15:44:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -51,7 +78,7 @@ def __call__(self, input: Documents) -> Embeddings:
         Get the embeddings for a list of texts.
 
         Args:
-            input (Documents): A list of texts or images to get embeddings for.
+            input (Documents): A list of texts to get embeddings for.
path: chromadb/utils/embedding_functions/jina_embedding_function.py
user: login: jairad26
body: right now it doesnt. i can add that as part of the PR
created_at: 2025-04-10T15:44:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,3 +45,40 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+### Late Chunking Example
+
+JinaAI supports late chunking on `jina-embeddings-v3`, which can enrich the context across the documents being inserted by combining multiple chunks.
+
+{% tabs group="code-lang" hideTabs=true %}
+{% Tab label="python" %}
+
+```python
+from chromadb.utils.embedding_functions import JinaEmbeddingFunction
+jinaai_ef = JinaEmbeddingFunction(
+                api_key="YOUR_API_KEY",
+                model_name="jina-embeddings-v3",
+                late_chunking=True,
+            )
path: docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
user: login: jairad26
body: yea for right now, we persist 1 configuration of embedding function per collection, so its constant between query time and insert time. will update to use text-matching
created_at: 2025-04-10T15:46:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,3 +45,40 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+### Late Chunking Example
+
+JinaAI supports late chunking on `jina-embeddings-v3`, which can enrich the context across the documents being inserted by combining multiple chunks.
+
+{% tabs group="code-lang" hideTabs=true %}
+{% Tab label="python" %}
+
+```python
+from chromadb.utils.embedding_functions import JinaEmbeddingFunction
+jinaai_ef = JinaEmbeddingFunction(
+                api_key="YOUR_API_KEY",
+                model_name="jina-embeddings-v3",
+                late_chunking=True,
+            )
path: docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
user: login: bwanglzu
body: sounds good, i think other embedding providers also do the `task` trick, e.g. for different task use different encode function, i was wondering how Choma is handling that now?
created_at: 2025-04-10T15:51:48Z
author_association: NONE
diff_hunk: @@ -25,6 +31,19 @@ def __init__(
                 Defaults to "CHROMA_JINA_API_KEY".
             model_name (str, optional): The name of the model to use for text embeddings.
                 Defaults to "jina-embeddings-v2-base-en".
+            task (str, optional): The task to use for the Jina AI API.
+                Defaults to None.
+            late_chunking (bool, optional): Whether to use late chunking for the Jina AI API.
+                Defaults to None.
+            truncate (bool, optional): Whether to truncate the Jina AI API.
+                Defaults to None.
+            dimensions (int, optional): The number of dimensions to use for the Jina AI API.
+                Defaults to None.
path: chromadb/utils/embedding_functions/jina_embedding_function.py
user: login: jairad26
body: after thinking about it, we would ideally want this embedding function validation to run server side. ie if we validate the user is only using these arguments with v3, if v4 comes out it with support for these arguments, it would require another PR to update validation. ive tested it with older models, and the API gives a fairly clear error that these arguments are not supported on those models. Thoughts on leaving validation logic as is?
created_at: 2025-04-10T15:58:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,3 +45,40 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+### Late Chunking Example
+
+JinaAI supports late chunking on `jina-embeddings-v3`, which can enrich the context across the documents being inserted by combining multiple chunks.
+
+{% tabs group="code-lang" hideTabs=true %}
+{% Tab label="python" %}
+
+```python
+from chromadb.utils.embedding_functions import JinaEmbeddingFunction
+jinaai_ef = JinaEmbeddingFunction(
+                api_key="YOUR_API_KEY",
+                model_name="jina-embeddings-v3",
+                late_chunking=True,
+            )
path: docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
user: login: jairad26
body: you're right that other providers do the task trick. for now, users would have to pass in the embedding function again during get_collection, which is not ideal. we'll have a more elegant solution, ideally with a configuration for query time
created_at: 2025-04-10T16:53:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -51,7 +78,7 @@ def __call__(self, input: Documents) -> Embeddings:
         Get the embeddings for a list of texts.
 
         Args:
-            input (Documents): A list of texts or images to get embeddings for.
+            input (Documents): A list of texts to get embeddings for.
path: chromadb/utils/embedding_functions/jina_embedding_function.py
user: login: jairad26
body: update: can't do that right now, since the format for input on multimodal models in jina does not match the existing ones. will have a workaround at a future point, so will keep this PR focused
created_at: 2025-04-10T17:32:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,3 +45,47 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+{% note type="tip" title="" %}
+
+Jina has added new attributes on embedding functions, including `task`, `late_chunking`, `truncate`, `dimensions`, `embedding_type`, and `normalized`. See [JinaAI](https://jina.ai/embeddings/) for references on which models support these attributes.
+
+{% /note %}
+
+### Late Chunking Example
path: docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
user: login: HammadB
body: should we create a new notebook for this?
created_at: 2025-04-11T23:18:59Z
author_association: COLLABORATOR
diff_hunk: @@ -45,3 +45,47 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+{% note type="tip" title="" %}
+
+Jina has added new attributes on embedding functions, including `task`, `late_chunking`, `truncate`, `dimensions`, `embedding_type`, and `normalized`. See [JinaAI](https://jina.ai/embeddings/) for references on which models support these attributes.
+
+{% /note %}
+
+### Late Chunking Example
+
+JinaAI supports late chunking on `jina-embeddings-v3`, which can enrich the context across the documents being inserted by combining multiple chunks.
path: docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
user: login: HammadB
body: this doesn't explain the feature that well to me on a first read
created_at: 2025-04-11T23:19:19Z
author_association: COLLABORATOR
diff_hunk: @@ -45,3 +45,47 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+{% note type="tip" title="" %}
+
+Jina has added new attributes on embedding functions, including `task`, `late_chunking`, `truncate`, `dimensions`, `embedding_type`, and `normalized`. See [JinaAI](https://jina.ai/embeddings/) for references on which models support these attributes.
+
+{% /note %}
+
+### Late Chunking Example
path: docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
user: login: jairad26
body: right now i just made the late_chunking.ipynb inside of jina/, will move to a new folder called tutorials/ when we've decided on that
created_at: 2025-04-11T23:40:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,3 +45,47 @@ const collectionGet = await client.getCollection({name:"name", embeddingFunction
 {% /TabbedCodeBlock %}
 
 You can pass in an optional `model_name` argument, which lets you choose which Jina model to use. By default, Chroma uses `jina-embedding-v2-base-en`.
+
+{% note type="tip" title="" %}
+
+Jina has added new attributes on embedding functions, including `task`, `late_chunking`, `truncate`, `dimensions`, `embedding_type`, and `normalized`. See [JinaAI](https://jina.ai/embeddings/) for references on which models support these attributes.
+
+{% /note %}
+
+### Late Chunking Example
+
+JinaAI supports late chunking on `jina-embeddings-v3`, which can enrich the context across the documents being inserted by combining multiple chunks.
path: docs/docs.trychroma.com/markdoc/content/integrations/embedding-models/jina-ai.md
user: login: jairad26
body: modified
created_at: 2025-04-11T23:41:11Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-09T15:28:22Z
body: # Reviewer Checklist
  Please leverage this checklist to ensure your code review is thorough before approving
  ## Testing, Bugs, Errors, Logs, Documentation
  - [ ] Can you think of any use case in which the code does not behave as intended? Have they been tested?
  - [ ] Can you think of any inputs or external events that could break the code? Is user input validated and safe? Have they been tested?
  - [ ] If appropriate, are there adequate property based tests?
  - [ ] If appropriate, are there adequate unit tests?
  - [ ] Should any logging, debugging, tracing information be added or removed?
  - [ ] Are error messages user-friendly?
  - [ ] Have all documentation changes needed been made?
  - [ ] Have all non-obvious changes been commented?
  ## System Compatibility
  - [ ] Are there any potential impacts on other parts of the system or backward compatibility?
  - [ ] Does this change intersect with any items on our roadmap, and if so, is there a plan for fitting them together?
  ## Quality
  - [ ] Is this code of a unexpectedly high quality (Readability, Modularity, Intuitiveness)
author_association: NONE
user: login: jairad26
created_at: 2025-04-09T15:28:29Z
body: * **#4244** <a href="https://app.graphite.dev/github/pr/chroma-core/chroma/4244?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/chroma-core/chroma/4244?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: CONTRIBUTOR