Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ jobs:
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
VOYAGE_API_KEY: ${{ secrets.VOYAGE_API_KEY }}
# The live Azure OpenAI tests gate themselves on AZURE_OPENAI_ENDPOINT,
# AZURE_OPENAI_API_KEY and OPENAI_API_VERSION: if any of the three is empty
# or unset they skip, so an absent secret disables them and re-adding it
# re-enables them with no code change. These lines stay wired up for that
# reason. (test_non_supported_dtypes is deliberately left ungated -- it
# validates dtype before any client is built and needs no network.)
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
AZURE_OPENAI_DEPLOYMENT_NAME: ${{ secrets.AZURE_OPENAI_DEPLOYMENT_NAME }}
Expand Down Expand Up @@ -192,6 +198,12 @@ jobs:
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
VOYAGE_API_KEY: ${{ secrets.VOYAGE_API_KEY }}
# The Azure cells in docs/user_guide/04_vectorizers.ipynb carry an
# unconditional `# NBVAL_SKIP`, so notebook validation skips them whether or
# not these secrets are set, and never blocks on getpass for missing
# credentials. Restoring the secrets is therefore not enough on its own --
# those markers have to be removed too. These lines stay wired up so that
# re-enabling needs no workflow change.
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
AZURE_OPENAI_DEPLOYMENT_NAME: ${{ secrets.AZURE_OPENAI_DEPLOYMENT_NAME }}
Expand Down
7 changes: 6 additions & 1 deletion docs/user_guide/04_vectorizers.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,14 @@
"metadata": {},
"outputs": [],
"source": [
"# NBVAL_SKIP\n",
"# additionally to the API Key, setup the API endpoint and version\n",
"api_key = os.environ.get(\"AZURE_OPENAI_API_KEY\") or getpass.getpass(\"Enter your AzureOpenAI API key: \")\n",
"api_version = os.environ.get(\"OPENAI_API_VERSION\") or getpass.getpass(\"Enter your AzureOpenAI API version: \")\n",
"azure_endpoint = os.environ.get(\"AZURE_OPENAI_ENDPOINT\") or getpass.getpass(\"Enter your AzureOpenAI API endpoint: \")\n",
"deployment_name = os.environ.get(\"AZURE_OPENAI_DEPLOYMENT_NAME\", \"text-embedding-ada-002\")\n",
"\n",
"# Skip Azure examples when required values are missing (e.g. CI or Run All without Azure).\n",
"# Skip Azure examples when required values are missing (e.g. Run All without Azure configured).\n",
"_azure_configured = bool(azure_endpoint and api_key and api_version)\n"
]
},
Expand All @@ -208,6 +209,8 @@
"metadata": {},
"outputs": [],
"source": [
"# NBVAL_SKIP\n",
"# Depends on the Azure OpenAI cell above, which is not executed in CI.\n",
"from redisvl.utils.vectorize import AzureOpenAITextVectorizer\n",
"\n",
"if not _azure_configured:\n",
Expand All @@ -234,6 +237,8 @@
"metadata": {},
"outputs": [],
"source": [
"# NBVAL_SKIP\n",
"# Depends on the Azure OpenAI cell above, which is not executed in CI.\n",
"# Just like OpenAI, AzureOpenAI supports batching embeddings and asynchronous requests.\n",
"sentences = [\n",
" \"That is a happy dog\",\n",
Expand Down
26 changes: 11 additions & 15 deletions redisvl/utils/vectorize/text/azureopenai.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ def _initialize_clients(self, api_config: dict[str, Any] | None, **kwargs):
ImportError: If the openai library is not installed
ValueError: If required parameters are not provided
"""
if api_config is None:
api_config = {}
# Copy so we never mutate the caller's dict.
api_config = dict(api_config or {})

# Dynamic import of the openai module
try:
Expand All @@ -142,10 +142,8 @@ def _initialize_clients(self, api_config: dict[str, Any] | None, **kwargs):
)

# Fetch the API key, version and endpoint from api_config or environment variable
azure_endpoint = (
api_config.pop("azure_endpoint")
if api_config
else os.getenv("AZURE_OPENAI_ENDPOINT")
azure_endpoint = api_config.pop("azure_endpoint", None) or os.getenv(
"AZURE_OPENAI_ENDPOINT"
)

if not azure_endpoint:
Expand All @@ -154,10 +152,8 @@ def _initialize_clients(self, api_config: dict[str, Any] | None, **kwargs):
"Provide it in api_config or set the AZURE_OPENAI_ENDPOINT environment variable."
)

api_version = (
api_config.pop("api_version")
if api_config
else os.getenv("OPENAI_API_VERSION")
api_version = api_config.pop("api_version", None) or os.getenv(
"OPENAI_API_VERSION"
)

if not api_version:
Expand All @@ -166,11 +162,7 @@ def _initialize_clients(self, api_config: dict[str, Any] | None, **kwargs):
"Provide it in api_config or set the OPENAI_API_VERSION environment variable."
)

api_key = (
api_config.pop("api_key")
if api_config
else os.getenv("AZURE_OPENAI_API_KEY")
)
api_key = api_config.pop("api_key", None) or os.getenv("AZURE_OPENAI_API_KEY")

if not api_key:
raise ValueError(
Expand Down Expand Up @@ -219,6 +211,7 @@ def _set_model_dims(self) -> int:
wait=wait_random_exponential(min=1, max=60),
stop=stop_after_attempt(6),
retry=retry_if_not_exception_type(TypeError),
reraise=True,
)
def _embed(self, content: str = "", text: str = "", **kwargs) -> list[float]:
"""
Expand Down Expand Up @@ -253,6 +246,7 @@ def _embed(self, content: str = "", text: str = "", **kwargs) -> list[float]:
wait=wait_random_exponential(min=1, max=60),
stop=stop_after_attempt(6),
retry=retry_if_not_exception_type(TypeError),
reraise=True,
)
def _embed_many(
self,
Expand Down Expand Up @@ -299,6 +293,7 @@ def _embed_many(
wait=wait_random_exponential(min=1, max=60),
stop=stop_after_attempt(6),
retry=retry_if_not_exception_type(TypeError),
reraise=True,
)
async def _aembed(self, content: str = "", text: str = "", **kwargs) -> list[float]:
"""
Expand Down Expand Up @@ -333,6 +328,7 @@ async def _aembed(self, content: str = "", text: str = "", **kwargs) -> list[flo
wait=wait_random_exponential(min=1, max=60),
stop=stop_after_attempt(6),
retry=retry_if_not_exception_type(TypeError),
reraise=True,
)
async def _aembed_many(
self,
Expand Down
24 changes: 22 additions & 2 deletions tests/integration/test_vectorizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,33 @@ def embeddings_cache(client):
cache.clear()


# Azure OpenAI live tests need a reachable deployment. _initialize_clients()
# requires all three of these before it will construct a client, so gate on all
# three rather than guessing from one. AZURE_OPENAI_DEPLOYMENT_NAME is
# deliberately excluded -- it has a real default at every call site below.
_AZURE_CONFIGURED = all(
os.getenv(var)
for var in ("AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_API_KEY", "OPENAI_API_VERSION")
)
skip_without_azure = pytest.mark.skipif(
not _AZURE_CONFIGURED,
reason=(
"Azure OpenAI is not configured. Set AZURE_OPENAI_ENDPOINT, "
"AZURE_OPENAI_API_KEY and OPENAI_API_VERSION to run these, plus "
"AZURE_OPENAI_DEPLOYMENT_NAME if your deployment is not named "
"text-embedding-ada-002. Offline coverage lives in "
"tests/unit/test_azure_openai_vectorizer.py."
),
)


_vectorizer_params = [
pytest.param(HFTextVectorizer, marks=pytest.mark.requires_hf),
OpenAITextVectorizer,
VertexAIVectorizer,
GoogleGenAIVectorizer,
CohereTextVectorizer,
AzureOpenAITextVectorizer,
pytest.param(AzureOpenAITextVectorizer, marks=skip_without_azure),
BedrockVectorizer,
MistralAITextVectorizer,
CustomVectorizer,
Expand Down Expand Up @@ -416,7 +436,7 @@ def bad_return_type(text: str) -> str:


_dtype_params = [
AzureOpenAITextVectorizer,
pytest.param(AzureOpenAITextVectorizer, marks=skip_without_azure),
BedrockVectorizer,
CohereTextVectorizer,
CustomVectorizer,
Expand Down
Loading
Loading