Skip to content

Commit 9947b18

Browse files
authored
feat: add Ollama text vectorizer (#617)
## Summary Adds `OllamaTextVectorizer` support for local Ollama embedding models. This PR includes Ollama optional dependency wiring, a sync/async `OllamaTextVectorizer` implementation, vectorizer registry/from-dict support for `type: "ollama"`, mocked unit tests, and opt-in real Ollama integration tests guarded by `REDISVL_TEST_OLLAMA=1`. ## Usage ```python from redisvl.utils.vectorize import OllamaTextVectorizer vectorizer = OllamaTextVectorizer(model="nomic-embed-text") embedding = vectorizer.embed("hello world") ``` To run the real Ollama integration tests locally: ```bash ollama pull nomic-embed-text REDISVL_TEST_OLLAMA=1 uv run pytest tests/integration/test_ollama_vectorizer_integration.py ``` Testing: ```bash uv run pytest tests/unit/test_ollama_vectorizer.py tests/integration/test_ollama_vectorizer_integration.py -q REDISVL_TEST_OLLAMA=1 uv run pytest tests/integration/test_ollama_vectorizer_integration.py -q uv run black --check ./redisvl ./tests uv run isort ./redisvl ./tests --check-only --profile black uv lock --check uv run mypy redisvl make test ``` Full repo test result: <img width="556" height="51" alt="image" src="https://github.com/user-attachments/assets/0b970471-df71-4f9e-ad8c-69b49b6e047a" /> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Additive embedding provider with optional install; no changes to auth, Redis core, or existing vectorizer behavior beyond new enum and factory branch. > > **Overview** > Adds **local Ollama** as a first-class text embedding provider in RedisVL. > > A new **`OllamaTextVectorizer`** talks to a running Ollama daemon (sync/async `embed` / `embed_many`, optional `host`, dimension probing at init, retries). It is exported from `redisvl.utils.vectorize`, registered as **`Vectorizers.ollama`**, and constructible via **`vectorizer_from_dict`** with `type: "ollama"`. > > Packaging adds optional **`redisvl[ollama]`** / `all` extra (`ollama>=0.5.4`) and bumps the package to **0.18.2** in the lockfile. The vectorizers user guide gains an Ollama section with skip-on-error examples. **Unit tests** use a fake `ollama` client; **integration tests** run only when `REDISVL_TEST_OLLAMA=1`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ce7b52e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 4041119 commit 9947b18

8 files changed

Lines changed: 703 additions & 11 deletions

File tree

docs/user_guide/04_vectorizers.ipynb

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@
66
"source": [
77
"# Create Embeddings with Vectorizers\n",
88
"\n",
9-
"This guide demonstrates how to create embeddings using RedisVL's built-in text vectorizers. RedisVL supports multiple embedding providers: OpenAI, HuggingFace, Vertex AI, Cohere, Mistral AI, Amazon Bedrock, VoyageAI, and custom vectorizers.\n",
9+
"This guide demonstrates how to create embeddings using RedisVL's built-in text vectorizers. RedisVL supports multiple embedding providers: OpenAI, HuggingFace, Ollama, Vertex AI, Cohere, Mistral AI, Amazon Bedrock, VoyageAI, and custom vectorizers.\n",
1010
"\n",
1111
"## Prerequisites\n",
1212
"\n",
1313
"Before you begin, ensure you have:\n",
1414
"- Installed RedisVL: `pip install redisvl`\n",
1515
"- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))\n",
16-
"- API keys for the embedding providers you plan to use\n",
16+
"- API keys or local model servers for the embedding providers you plan to use\n",
1717
"\n",
1818
"## What You'll Learn\n",
1919
"\n",
2020
"By the end of this guide, you will be able to:\n",
21-
"- Create embeddings using multiple providers (OpenAI, HuggingFace, Cohere, etc.)\n",
21+
"- Create embeddings using multiple providers (OpenAI, HuggingFace, Ollama, Cohere, etc.)\n",
2222
"- Use synchronous and asynchronous embedding methods\n",
2323
"- Batch embed multiple texts efficiently\n",
2424
"- Build custom vectorizers for your own embedding functions\n",
@@ -290,6 +290,72 @@
290290
"embeddings = hf.embed_many(sentences, as_buffer=True)\n"
291291
]
292292
},
293+
{
294+
"cell_type": "markdown",
295+
"metadata": {},
296+
"source": [
297+
"### Ollama\n",
298+
"\n",
299+
"[Ollama](https://ollama.com/) lets you run embedding models locally. RedisVL supports Ollama through the `OllamaTextVectorizer`.\n",
300+
"\n",
301+
"Install the Python client and pull an embedding model before running this example:\n",
302+
"\n",
303+
"```bash\n",
304+
"pip install 'redisvl[ollama]'\n",
305+
"ollama pull nomic-embed-text\n",
306+
"```\n",
307+
"\n",
308+
"Make sure the Ollama daemon is running with `ollama serve`. By default, the Ollama client uses `OLLAMA_HOST` if set, otherwise it connects to `http://localhost:11434`. To connect to a custom Ollama server explicitly, pass `host=\"http://your-host:11434\"` when creating the vectorizer."
309+
]
310+
},
311+
{
312+
"cell_type": "code",
313+
"execution_count": null,
314+
"metadata": {},
315+
"outputs": [],
316+
"source": [
317+
"from redisvl.utils.vectorize import OllamaTextVectorizer\n",
318+
"\n",
319+
"ollama_model = os.environ.get(\"OLLAMA_MODEL\", \"nomic-embed-text\")\n",
320+
"\n",
321+
"try:\n",
322+
" ollama = OllamaTextVectorizer(model=ollama_model)\n",
323+
"\n",
324+
" test = ollama.embed(\"This is a test sentence.\")\n",
325+
" print(\"Vector dimensions:\", len(test))\n",
326+
" print(test[:10])\n",
327+
"except (ImportError, ConnectionError, ValueError) as exc:\n",
328+
" print(\"Skipping Ollama example:\", exc)\n",
329+
" ollama = None\n"
330+
]
331+
},
332+
{
333+
"cell_type": "code",
334+
"execution_count": null,
335+
"metadata": {},
336+
"outputs": [],
337+
"source": [
338+
"if ollama is not None:\n",
339+
" embeddings = ollama.embed_many(sentences, batch_size=2)\n",
340+
" print(\"Number of embeddings:\", len(embeddings))\n",
341+
" print(\"Vector dimensions:\", len(embeddings[0]))\n",
342+
"else:\n",
343+
" print(\"Skipping: run the Ollama cell above with a running Ollama server and pulled model.\")\n"
344+
]
345+
},
346+
{
347+
"cell_type": "code",
348+
"execution_count": null,
349+
"metadata": {},
350+
"outputs": [],
351+
"source": [
352+
"if ollama is not None:\n",
353+
" embeddings = await ollama.aembed_many(sentences, batch_size=2)\n",
354+
" print(\"Number of async embeddings:\", len(embeddings))\n",
355+
"else:\n",
356+
" print(\"Skipping: run the Ollama cell above with a running Ollama server and pulled model.\")\n"
357+
]
358+
},
293359
{
294360
"cell_type": "markdown",
295361
"metadata": {},

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ all = [
7373
"urllib3<2.2.0",
7474
"pillow>=11.3.0",
7575
"sql-redis>=0.5.0",
76+
"ollama>=0.5.4",
77+
]
78+
ollama = [
79+
"ollama>=0.5.4",
7680
]
7781

7882
[project.urls]

redisvl/utils/vectorize/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from redisvl.utils.vectorize.text.custom import CustomTextVectorizer
99
from redisvl.utils.vectorize.text.huggingface import HFTextVectorizer
1010
from redisvl.utils.vectorize.text.mistral import MistralAITextVectorizer
11+
from redisvl.utils.vectorize.text.ollama import OllamaTextVectorizer
1112
from redisvl.utils.vectorize.text.openai import OpenAITextVectorizer
1213
from redisvl.utils.vectorize.text.vertexai import VertexAITextVectorizer
1314
from redisvl.utils.vectorize.text.voyageai import VoyageAITextVectorizer
@@ -23,6 +24,7 @@
2324
"VertexAITextVectorizer",
2425
"AzureOpenAITextVectorizer",
2526
"MistralAITextVectorizer",
27+
"OllamaTextVectorizer",
2628
"CustomVectorizer",
2729
"CustomTextVectorizer",
2830
"BedrockVectorizer",
@@ -55,6 +57,8 @@ def vectorizer_from_dict(
5557
return HFTextVectorizer(**args)
5658
elif vectorizer_type == Vectorizers.mistral:
5759
return MistralAITextVectorizer(**args)
60+
elif vectorizer_type == Vectorizers.ollama:
61+
return OllamaTextVectorizer(**args)
5862
elif vectorizer_type == Vectorizers.vertexai:
5963
return VertexAIVectorizer(**args)
6064
elif vectorizer_type == Vectorizers.voyageai:

redisvl/utils/vectorize/base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Vectorizers(Enum):
2626
openai = "openai"
2727
cohere = "cohere"
2828
mistral = "mistral"
29+
ollama = "ollama"
2930
vertexai = "vertexai"
3031
hf = "hf"
3132
voyageai = "voyageai"

0 commit comments

Comments
 (0)