From 2069484643b14212431c67c6c4b52229d0b51a92 Mon Sep 17 00:00:00 2001 From: Phani Paladugula <178738086+phanipaladugula@users.noreply.github.com> Date: Tue, 3 Nov 2026 09:32:04 +0530 Subject: [PATCH] fix(voyage): honor VOYAGE_API_BASE for embeddings Voyage embeddings only used an explicit api_base and otherwise fell back to a host chosen from the key prefix, so callers that cannot pass api_base had no way to target a Voyage-compatible gateway. Rerank and the other providers already read their base URL env var. Resolve the base once in get_voyage_api_base: explicit api_base, then VOYAGE_API_BASE, then the key-issuer default, and use it for embeddings, contextual embeddings and multimodal embeddings. Fixes #49411 --- litellm/llms/voyage/common_utils.py | 5 +++++ litellm/llms/voyage/embedding/transformation.py | 4 ++-- .../voyage/embedding/transformation_contextual.py | 4 ++-- .../voyage/embedding/transformation_multimodal.py | 4 ++-- .../embedding/test_transformation_contextual.py | 14 ++++++++++++++ .../embedding/test_transformation_multimodal.py | 14 ++++++++++++++ .../test_voyage_embedding_transformation.py | 14 ++++++++++++++ 7 files changed, 53 insertions(+), 6 deletions(-) diff --git a/litellm/llms/voyage/common_utils.py b/litellm/llms/voyage/common_utils.py index 2f4f63a8a43..42a95c99f98 100644 --- a/litellm/llms/voyage/common_utils.py +++ b/litellm/llms/voyage/common_utils.py @@ -21,6 +21,11 @@ def get_voyage_api_key(api_key: str | None = None) -> str | None: ) +def get_voyage_api_base(api_base: str | None = None, api_key: str | None = None) -> str: + """Resolve the Voyage embeddings host: explicit base, ``VOYAGE_API_BASE``, then key issuer.""" + return api_base or get_secret_str("VOYAGE_API_BASE") or get_default_base_url(api_key) + + def get_default_base_url(api_key: str | None = None) -> str: """ Pick the host that issued the key: MongoDB-issued keys (``al-`` prefix) are only diff --git a/litellm/llms/voyage/embedding/transformation.py b/litellm/llms/voyage/embedding/transformation.py index 1ba66e5ce65..b57a31d0c41 100644 --- a/litellm/llms/voyage/embedding/transformation.py +++ b/litellm/llms/voyage/embedding/transformation.py @@ -8,7 +8,7 @@ from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging from litellm.litellm_core_utils.url_utils import ensure_api_route from litellm.llms.base_llm.chat.transformation import BaseLLMException from litellm.llms.base_llm.embedding.transformation import BaseEmbeddingConfig -from litellm.llms.voyage.common_utils import get_default_base_url, get_voyage_api_key +from litellm.llms.voyage.common_utils import get_voyage_api_base, get_voyage_api_key from litellm.types.llms.openai import AllEmbeddingInputValues, AllMessageValues from litellm.types.utils import EmbeddingResponse, Usage @@ -51,7 +51,7 @@ class VoyageEmbeddingConfig(BaseEmbeddingConfig): litellm_params: dict, stream: bool | None = None, ) -> str: - return ensure_api_route(api_base or get_default_base_url(api_key), "embeddings") + return ensure_api_route(get_voyage_api_base(api_base, api_key), "embeddings") def get_supported_openai_params(self, model: str) -> list: return [ diff --git a/litellm/llms/voyage/embedding/transformation_contextual.py b/litellm/llms/voyage/embedding/transformation_contextual.py index 62c03139fbe..4f0529033fb 100644 --- a/litellm/llms/voyage/embedding/transformation_contextual.py +++ b/litellm/llms/voyage/embedding/transformation_contextual.py @@ -13,7 +13,7 @@ from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging from litellm.litellm_core_utils.url_utils import ensure_api_route from litellm.llms.base_llm.chat.transformation import BaseLLMException from litellm.llms.base_llm.embedding.transformation import BaseEmbeddingConfig -from litellm.llms.voyage.common_utils import get_default_base_url, get_voyage_api_key +from litellm.llms.voyage.common_utils import get_voyage_api_base, get_voyage_api_key from litellm.types.llms.openai import AllEmbeddingInputValues, AllMessageValues from litellm.types.utils import EmbeddingResponse, Usage @@ -59,7 +59,7 @@ class VoyageContextualEmbeddingConfig(BaseEmbeddingConfig): litellm_params: dict, stream: bool | None = None, ) -> str: - return ensure_api_route(api_base or get_default_base_url(api_key), "contextualizedembeddings") + return ensure_api_route(get_voyage_api_base(api_base, api_key), "contextualizedembeddings") def get_supported_openai_params(self, model: str) -> list: # mutable-ok: base class signature return ["encoding_format", "dimensions"] diff --git a/litellm/llms/voyage/embedding/transformation_multimodal.py b/litellm/llms/voyage/embedding/transformation_multimodal.py index c04a2e1f26b..222a3fa5689 100644 --- a/litellm/llms/voyage/embedding/transformation_multimodal.py +++ b/litellm/llms/voyage/embedding/transformation_multimodal.py @@ -16,7 +16,7 @@ from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging from litellm.litellm_core_utils.url_utils import ensure_api_route from litellm.llms.base_llm.chat.transformation import BaseLLMException from litellm.llms.base_llm.embedding.transformation import BaseEmbeddingConfig -from litellm.llms.voyage.common_utils import get_default_base_url, get_voyage_api_key +from litellm.llms.voyage.common_utils import get_voyage_api_base, get_voyage_api_key from litellm.types.llms.openai import AllEmbeddingInputValues, AllMessageValues from litellm.types.utils import EmbeddingResponse, Usage @@ -60,7 +60,7 @@ class VoyageMultimodalEmbeddingConfig(BaseEmbeddingConfig): litellm_params: dict, stream: bool | None = None, ) -> str: - return ensure_api_route(api_base or get_default_base_url(api_key), "multimodalembeddings") + return ensure_api_route(get_voyage_api_base(api_base, api_key), "multimodalembeddings") def get_supported_openai_params(self, model: str) -> list: return ["dimensions"] diff --git a/tests/unit/llms/voyage/embedding/test_transformation_contextual.py b/tests/unit/llms/voyage/embedding/test_transformation_contextual.py index c64f899a9f4..84f119fb985 100644 --- a/tests/unit/llms/voyage/embedding/test_transformation_contextual.py +++ b/tests/unit/llms/voyage/embedding/test_transformation_contextual.py @@ -579,3 +579,17 @@ def test_get_complete_url_ignores_a_trailing_slash_on_api_base(api_base: str): ) == "https://gateway.example.com/voyage/v1/contextualizedembeddings" ) + + +def test_get_complete_url_honors_voyage_api_base(monkeypatch): + monkeypatch.setenv("VOYAGE_API_BASE", "https://gateway.example.com/v1/") + config = VoyageContextualEmbeddingConfig() + + assert ( + config.get_complete_url(None, "vk-test", "voyage-3.5", {}, {}) + == "https://gateway.example.com/v1/contextualizedembeddings" + ) + assert ( + config.get_complete_url("https://explicit.example.com/v1", "vk-test", "voyage-3.5", {}, {}) + == "https://explicit.example.com/v1/contextualizedembeddings" + ) diff --git a/tests/unit/llms/voyage/embedding/test_transformation_multimodal.py b/tests/unit/llms/voyage/embedding/test_transformation_multimodal.py index 4b8d27bba7d..967256bb15d 100644 --- a/tests/unit/llms/voyage/embedding/test_transformation_multimodal.py +++ b/tests/unit/llms/voyage/embedding/test_transformation_multimodal.py @@ -106,3 +106,17 @@ def test_get_complete_url_ignores_a_trailing_slash_on_api_base(api_base: str): ) == "https://gateway.example.com/voyage/v1/multimodalembeddings" ) + + +def test_get_complete_url_honors_voyage_api_base(monkeypatch): + monkeypatch.setenv("VOYAGE_API_BASE", "https://gateway.example.com/v1/") + config = VoyageMultimodalEmbeddingConfig() + + assert ( + config.get_complete_url(None, "vk-test", "voyage-3.5", {}, {}) + == "https://gateway.example.com/v1/multimodalembeddings" + ) + assert ( + config.get_complete_url("https://explicit.example.com/v1", "vk-test", "voyage-3.5", {}, {}) + == "https://explicit.example.com/v1/multimodalembeddings" + ) diff --git a/tests/unit/llms/voyage/embedding/test_voyage_embedding_transformation.py b/tests/unit/llms/voyage/embedding/test_voyage_embedding_transformation.py index cfbc8dc24fb..f17429efecd 100644 --- a/tests/unit/llms/voyage/embedding/test_voyage_embedding_transformation.py +++ b/tests/unit/llms/voyage/embedding/test_voyage_embedding_transformation.py @@ -106,3 +106,17 @@ def test_get_complete_url_ignores_a_trailing_slash_on_api_base(api_base: str): ) == "https://gateway.example.com/voyage/v1/embeddings" ) + + +def test_get_complete_url_honors_voyage_api_base(monkeypatch): + monkeypatch.setenv("VOYAGE_API_BASE", "https://gateway.example.com/v1/") + config = VoyageEmbeddingConfig() + + assert ( + config.get_complete_url(None, "vk-test", "voyage-3.5", {}, {}) + == "https://gateway.example.com/v1/embeddings" + ) + assert ( + config.get_complete_url("https://explicit.example.com/v1", "vk-test", "voyage-3.5", {}, {}) + == "https://explicit.example.com/v1/embeddings" + )