Skip to content

Commit 1e97e3c

Browse files
Add VertexAI Text vectorizer (#52)
1 parent 583f09d commit 1e97e3c

File tree

8 files changed

+236
-45
lines changed

8 files changed

+236
-45
lines changed

.github/workflows/run_tests.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,22 @@ jobs:
3535
run: |
3636
REDIS_URL=redis://localhost:6379
3737
echo REDIS_URL=$REDIS_URL >> $GITHUB_ENV
38+
- name: Authenticate to Google Cloud
39+
uses: google-github-actions/auth@v1
40+
with:
41+
credentials_json: ${{ secrets.GOOGLE_CREDENTIALS }}
3842
- name: Run tests
3943
env:
4044
OPENAI_API_KEY: ${{ secrets.OPENAI_KEY }}
45+
GCP_LOCATION: ${{ secrets.GCP_LOCATION }}
46+
GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
4147
run: |
4248
make test-cov
4349
- name: Run notebooks
4450
env:
4551
OPENAI_API_KEY: ${{ secrets.OPENAI_KEY }}
52+
GCP_LOCATION: ${{ secrets.GCP_LOCATION }}
53+
GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
4654
run: |
4755
cd docs/ && treon -v --exclude="./examples/openai_qna.ipynb"
4856
- name: Publish coverage results

docs/api/vectorizer.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,21 @@ OpenAITextVectorizer
4343
:members:
4444

4545

46+
VertexAITextVectorizer
47+
================
48+
49+
.. _vertexaitextvectorizer_api:
50+
51+
.. currentmodule:: redisvl.vectorize.text.vertexai
52+
53+
.. autosummary::
54+
55+
VertexAITextVectorizer.__init__
56+
VertexAITextVectorizer.embed
57+
VertexAITextVectorizer.embed_many
58+
59+
.. autoclass:: VertexAITextVectorizer
60+
:show-inheritance:
61+
:inherited-members:
62+
:members:
4663

docs/user_guide/vectorizers_03.ipynb

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,19 @@
1010
"In this notebook, we will show how to use RedisVL to create embeddings using the built-in text embedding vectorizers. Today RedisVL supports:\n",
1111
"1. OpenAI\n",
1212
"2. HuggingFace\n",
13+
"3. Vertex AI\n",
1314
"\n",
1415
"Before running this notebook, be sure to\n",
1516
"1. Have installed ``redisvl`` and have that environment active for this notebook.\n",
16-
"2. Have a running Redis instance with RediSearch > 2.4 running.\n",
17+
"2. Have a running Redis Stack instance with RediSearch > 2.4 active.\n",
1718
"\n",
18-
"For example, you can run Redis locally with Docker:\n",
19+
"For example, you can run Redis Stack locally with Docker:\n",
1920
"\n",
2021
"```bash\n",
2122
"docker run -d -p 6379:6379 -p 8001:8001 redis/redis-stack:latest\n",
2223
"```\n",
2324
"\n",
24-
"which will run Redis on port 6379 and RedisInsight at http://localhost:8001."
25+
"This will run Redis on port 6379 and RedisInsight at http://localhost:8001."
2526
]
2627
},
2728
{
@@ -107,6 +108,7 @@
107108
"source": [
108109
"from redisvl.vectorize.text import OpenAITextVectorizer\n",
109110
"\n",
111+
"# create a vectorizer\n",
110112
"oai = OpenAITextVectorizer(\n",
111113
" model=\"text-embedding-ada-002\",\n",
112114
" api_config={\"api_key\": api_key},\n",
@@ -179,7 +181,7 @@
179181
"source": [
180182
"### Huggingface\n",
181183
"\n",
182-
"Huggingface is a popular NLP library that has a number of pre-trained models. RedisVL supports using Huggingface to create embeddings from these models. To use Huggingface, you will need to install the ``sentence-transformers`` library.\n",
184+
"[Huggingface](https://huggingface.co/models) is a popular NLP platform that has a number of pre-trained models you can use off the shelf. RedisVL supports using Huggingface \"Sentence Transformers\" to create embeddings from text. To use Huggingface, you will need to install the ``sentence-transformers`` library.\n",
183185
"\n",
184186
"```bash\n",
185187
"pip install sentence-transformers\n",
@@ -188,43 +190,16 @@
188190
},
189191
{
190192
"cell_type": "code",
191-
"execution_count": 6,
193+
"execution_count": null,
192194
"metadata": {},
193-
"outputs": [
194-
{
195-
"name": "stderr",
196-
"output_type": "stream",
197-
"text": [
198-
"/Users/sam.partee/.virtualenvs/rvl/lib/python3.8/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",
199-
" from .autonotebook import tqdm as notebook_tqdm\n"
200-
]
201-
},
202-
{
203-
"data": {
204-
"text/plain": [
205-
"[0.00037813105154782534,\n",
206-
" -0.05080341547727585,\n",
207-
" -0.03514720872044563,\n",
208-
" -0.023251093924045563,\n",
209-
" -0.04415826499462128,\n",
210-
" 0.020487893372774124,\n",
211-
" 0.0014619074063375592,\n",
212-
" 0.03126181662082672,\n",
213-
" 0.056051574647426605,\n",
214-
" 0.0188154224306345]"
215-
]
216-
},
217-
"execution_count": 6,
218-
"metadata": {},
219-
"output_type": "execute_result"
220-
}
221-
],
195+
"outputs": [],
222196
"source": [
223197
"os.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\n",
224198
"from redisvl.vectorize.text import HFTextVectorizer\n",
225199
"\n",
226200
"\n",
227-
"# create a provider\n",
201+
"# create a vectorizer\n",
202+
"# choose your model from the huggingface website\n",
228203
"hf = HFTextVectorizer(model=\"sentence-transformers/all-mpnet-base-v2\")\n",
229204
"\n",
230205
"# embed a sentence\n",
@@ -242,6 +217,44 @@
242217
"embeddings = hf.embed_many(sentences, as_buffer=True)\n"
243218
]
244219
},
220+
{
221+
"cell_type": "markdown",
222+
"metadata": {},
223+
"source": [
224+
"### VertexAI\n",
225+
"\n",
226+
"[VertexAI](https://cloud.google.com/vertex-ai/docs/generative-ai/embeddings/get-text-embeddings) is GCP's fully-featured AI platform including a number of pretrained LLMs. RedisVL supports using VertexAI to create embeddings from these models. To use VertexAI, you will first need to install the ``google-cloud-aiplatform`` library.\n",
227+
"\n",
228+
"```bash\n",
229+
"pip install google-cloud-aiplatform>=1.26\n",
230+
"```\n",
231+
"\n",
232+
"1. Then you need to gain access to a [Google Cloud Project](https://cloud.google.com/gcp?hl=en) and provide [access to credentials](https://cloud.google.com/docs/authentication/application-default-credentials). This typically accomplished with the `GOOGLE_APPLICATION_CREDENTIALS` environment variable pointing to the path of a JSON key file downloaded from your service account on GCP.\n",
233+
"2. Lastly, you need to find your [project ID](https://support.google.com/googleapi/answer/7014113?hl=en) and [geographic region for VertexAI](https://cloud.google.com/vertex-ai/docs/general/locations)."
234+
]
235+
},
236+
{
237+
"cell_type": "code",
238+
"execution_count": null,
239+
"metadata": {},
240+
"outputs": [],
241+
"source": [
242+
"from redisvl.vectorize.text import VertexAITextVectorizer\n",
243+
"\n",
244+
"\n",
245+
"# create a vectorizer\n",
246+
"vtx = VertexAITextVectorizer(\n",
247+
" api_config={\n",
248+
" \"project_id\": os.environ[\"GCP_PROJECT_ID\"],\n",
249+
" \"location\": os.environ[\"GCP_LOCATION\"]\n",
250+
" }\n",
251+
")\n",
252+
"\n",
253+
"# embed a sentence\n",
254+
"test = vtx.embed(\"This is a test sentence.\")\n",
255+
"test[:10]"
256+
]
257+
},
245258
{
246259
"cell_type": "markdown",
247260
"metadata": {},
@@ -377,7 +390,7 @@
377390
"name": "python",
378391
"nbconvert_exporter": "python",
379392
"pygments_lexer": "ipython3",
380-
"version": "3.10.10"
393+
"version": "3.9.12"
381394
},
382395
"orig_nbformat": 4,
383396
"vscode": {

redisvl/vectorize/text/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
from redisvl.vectorize.text.huggingface import HFTextVectorizer
22
from redisvl.vectorize.text.openai import OpenAITextVectorizer
3+
from redisvl.vectorize.text.vertexai import VertexAITextVectorizer
34

4-
__all__ = [
5-
"OpenAITextVectorizer",
6-
"HFTextVectorizer",
7-
]
5+
__all__ = ["OpenAITextVectorizer", "HFTextVectorizer", "VertexAITextVectorizer"]

redisvl/vectorize/text/openai.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def embed_many(
8080
perform before vectorization. Defaults to None.
8181
batch_size (int, optional): Batch size of texts to use when creating
8282
embeddings. Defaults to 10.
83-
as_buffer (Optional[float], optional): Whether to convert the raw embedding
83+
as_buffer (bool, optional): Whether to convert the raw embedding
8484
to a byte string. Defaults to False.
8585
8686
Returns:
@@ -197,7 +197,7 @@ async def aembed(
197197
text (str): Chunk of text to embed.
198198
preprocess (Optional[Callable], optional): Optional preprocessing callable to
199199
perform before vectorization. Defaults to None.
200-
as_buffer (float, optional): Whether to convert the raw embedding
200+
as_buffer (bool, optional): Whether to convert the raw embedding
201201
to a byte string. Defaults to False.
202202
203203
Returns:

redisvl/vectorize/text/vertexai.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
from typing import Callable, Dict, List, Optional
2+
3+
from tenacity import retry, stop_after_attempt, wait_random_exponential
4+
from tenacity.retry import retry_if_not_exception_type
5+
6+
from redisvl.vectorize.base import BaseVectorizer
7+
8+
9+
class VertexAITextVectorizer(BaseVectorizer):
10+
"""VertexAI text vectorizer
11+
12+
This vectorizer uses the VertexAI Palm 2 embedding model API to create embeddings for text. It requires an
13+
active GCP project, location, and application credentials.
14+
"""
15+
16+
def __init__(
17+
self, model: str = "textembedding-gecko", api_config: Optional[Dict] = None
18+
):
19+
"""Initialize the VertexAI vectorizer.
20+
21+
Args:
22+
model (str): Model to use for embedding.
23+
api_config (Optional[Dict], optional): Dictionary containing the API key.
24+
Defaults to None.
25+
26+
Raises:
27+
ImportError: If the google-cloud-aiplatform library is not installed.
28+
ValueError: If the API key is not provided.
29+
"""
30+
super().__init__(model)
31+
32+
if (
33+
not api_config
34+
or "project_id" not in api_config
35+
or "location" not in api_config
36+
):
37+
raise ValueError(
38+
"GCP project id and valid location are required in the api_config"
39+
)
40+
41+
try:
42+
import vertexai
43+
from vertexai.preview.language_models import TextEmbeddingModel
44+
45+
vertexai.init(
46+
project=api_config["project_id"], location=api_config["location"]
47+
)
48+
except ImportError:
49+
raise ImportError(
50+
"VertexAI vectorizer requires the google-cloud-aiplatform library."
51+
"Please install with pip install google-cloud-aiplatform>=1.26"
52+
)
53+
54+
self._model_client = TextEmbeddingModel.from_pretrained(model)
55+
self._dims = self._set_model_dims()
56+
57+
def _set_model_dims(self) -> int:
58+
try:
59+
embedding = self._model_client.get_embeddings(["dimension test"])[0].values
60+
except (KeyError, IndexError) as ke:
61+
raise ValueError(f"Unexpected response from the VertexAI API: {str(ke)}")
62+
except Exception as e: # pylint: disable=broad-except
63+
# fall back (TODO get more specific)
64+
raise ValueError(f"Error setting embedding model dimensions: {str(e)}")
65+
return len(embedding)
66+
67+
@retry(
68+
wait=wait_random_exponential(min=1, max=60),
69+
stop=stop_after_attempt(6),
70+
retry=retry_if_not_exception_type(TypeError),
71+
)
72+
def embed_many(
73+
self,
74+
texts: List[str],
75+
preprocess: Optional[Callable] = None,
76+
batch_size: int = 10,
77+
as_buffer: bool = False,
78+
) -> List[List[float]]:
79+
"""Embed many chunks of texts using the VertexAI API.
80+
81+
Args:
82+
texts (List[str]): List of text chunks to embed.
83+
preprocess (Optional[Callable], optional): Optional preprocessing callable to
84+
perform before vectorization. Defaults to None.
85+
batch_size (int, optional): Batch size of texts to use when creating
86+
embeddings. Defaults to 10.
87+
as_buffer (bool, optional): Whether to convert the raw embedding
88+
to a byte string. Defaults to False.
89+
90+
Returns:
91+
List[List[float]]: List of embeddings.
92+
93+
Raises:
94+
TypeError: If the wrong input type is passed in for the test.
95+
"""
96+
if not isinstance(texts, list):
97+
raise TypeError("Must pass in a list of str values to embed.")
98+
if len(texts) > 0 and not isinstance(texts[0], str):
99+
raise TypeError("Must pass in a list of str values to embed.")
100+
101+
embeddings: List = []
102+
for batch in self.batchify(texts, batch_size, preprocess):
103+
response = self._model_client.get_embeddings(batch)
104+
embeddings += [
105+
self._process_embedding(r.values, as_buffer) for r in response
106+
]
107+
return embeddings
108+
109+
@retry(
110+
wait=wait_random_exponential(min=1, max=60),
111+
stop=stop_after_attempt(6),
112+
retry=retry_if_not_exception_type(TypeError),
113+
)
114+
def embed(
115+
self,
116+
text: str,
117+
preprocess: Optional[Callable] = None,
118+
as_buffer: bool = False,
119+
) -> List[float]:
120+
"""Embed a chunk of text using the VertexAI API.
121+
122+
Args:
123+
text (str): Chunk of text to embed.
124+
preprocess (Optional[Callable], optional): Optional preprocessing callable to
125+
perform before vectorization. Defaults to None.
126+
as_buffer (bool, optional): Whether to convert the raw embedding
127+
to a byte string. Defaults to False.
128+
129+
Returns:
130+
List[float]: Embedding.
131+
132+
Raises:
133+
TypeError: If the wrong input type is passed in for the test.
134+
"""
135+
if not isinstance(text, str):
136+
raise TypeError("Must pass in a str value to embed.")
137+
138+
if preprocess:
139+
text = preprocess(text)
140+
result = self._model_client.get_embeddings([text])
141+
return self._process_embedding(result[0].values, as_buffer)

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ def read_dev_requirements():
1414
extras_require = {
1515
"all": [
1616
"openai>=0.26.4",
17-
"sentence-transformers>=2.2.2"
17+
"sentence-transformers>=2.2.2",
18+
"google-cloud-aiplatform>=1.26"
1819
],
1920
"dev": read_dev_requirements()
2021
}

tests/integration/test_vectorizers.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
import pytest
44

5-
from redisvl.vectorize.text import HFTextVectorizer, OpenAITextVectorizer
5+
from redisvl.vectorize.text import (
6+
HFTextVectorizer,
7+
OpenAITextVectorizer,
8+
VertexAITextVectorizer,
9+
)
610

711

8-
@pytest.fixture(params=[HFTextVectorizer, OpenAITextVectorizer])
12+
@pytest.fixture(params=[HFTextVectorizer, OpenAITextVectorizer, VertexAITextVectorizer])
913
def vectorizer(request, openai_key):
1014
# Here we use actual models for integration test
1115
if request.param == HFTextVectorizer:
@@ -14,6 +18,15 @@ def vectorizer(request, openai_key):
1418
return request.param(
1519
model="text-embedding-ada-002", api_config={"api_key": openai_key}
1620
)
21+
elif request.param == VertexAITextVectorizer:
22+
# also need to set GOOGLE_APPLICATION_CREDENTIALS env var
23+
return request.param(
24+
model="textembedding-gecko",
25+
api_config={
26+
"location": os.environ["GCP_LOCATION"],
27+
"project_id": os.environ["GCP_PROJECT_ID"],
28+
},
29+
)
1730

1831

1932
def test_vectorizer_embed(vectorizer):

0 commit comments

Comments
 (0)