mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
Merge ed2d5d2e00 into a5e9f275ea
This commit is contained in:
commit
26375370e2
13 changed files with 238 additions and 50 deletions
|
|
@ -399,7 +399,7 @@ Set `LITELLM_PROXY_API_BASE` and `LITELLM_PROXY_API_KEY` and every model call th
|
|||
| [Vercel AI Gateway (`vercel_ai_gateway`)](https://docs.litellm.ai/docs/providers/vercel_ai_gateway) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [VLLM (`vllm`)](https://docs.litellm.ai/docs/providers/vllm) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [Volcengine (`volcengine`)](https://docs.litellm.ai/docs/providers/volcano) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [Voyage AI (`voyage`)](https://docs.litellm.ai/docs/providers/voyage) | | | | ✅ | | | | | | |
|
||||
| [VoyageAI by MongoDB (`voyage`)](https://docs.litellm.ai/docs/providers/voyage) | | | | ✅ | | | | | | |
|
||||
| [WandB Inference (`wandb`)](https://docs.litellm.ai/docs/providers/wandb_inference) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [Watsonx Text (`watsonx_text`)](https://docs.litellm.ai/docs/providers/watsonx) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
| [xAI (`xai`)](https://docs.litellm.ai/docs/providers/xai) | ✅ | ✅ | ✅ | | | | | | | |
|
||||
|
|
|
|||
35
litellm/llms/voyage/common_utils.py
Normal file
35
litellm/llms/voyage/common_utils.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
"""
|
||||
Shared helpers for the Voyage (VoyageAI by MongoDB) provider.
|
||||
"""
|
||||
|
||||
from typing import Final
|
||||
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
|
||||
VOYAGE_API_BASE: Final = "https://api.voyageai.com/v1"
|
||||
MONGODB_API_BASE: Final = "https://ai.mongodb.com/v1"
|
||||
MONGODB_API_KEY_PREFIX: Final = "al-"
|
||||
|
||||
|
||||
def get_voyage_api_key(api_key: str | None = None) -> str | None:
|
||||
"""Resolve the key a Voyage request will authenticate with, explicit value first."""
|
||||
return (
|
||||
api_key
|
||||
or get_secret_str("VOYAGE_API_KEY")
|
||||
or get_secret_str("VOYAGE_AI_API_KEY")
|
||||
or get_secret_str("VOYAGE_AI_TOKEN")
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
valid on ai.mongodb.com, every other key on api.voyageai.com.
|
||||
|
||||
Mirrors ``voyageai.util.get_default_base_url`` in the official SDK:
|
||||
https://github.com/voyage-ai/voyageai-python/blob/main/voyageai/util.py
|
||||
"""
|
||||
resolved: Final = get_voyage_api_key(api_key)
|
||||
if resolved is not None and resolved.startswith(MONGODB_API_KEY_PREFIX):
|
||||
return MONGODB_API_BASE
|
||||
return VOYAGE_API_BASE
|
||||
|
|
@ -5,7 +5,7 @@ import httpx
|
|||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
from litellm.llms.base_llm.embedding.transformation import BaseEmbeddingConfig
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.llms.voyage.common_utils import get_default_base_url, get_voyage_api_key
|
||||
from litellm.types.llms.openai import AllEmbeddingInputValues, AllMessageValues
|
||||
from litellm.types.utils import EmbeddingResponse, Usage
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ class VoyageEmbeddingConfig(BaseEmbeddingConfig):
|
|||
if not api_base.endswith("/embeddings"):
|
||||
api_base = f"{api_base}/embeddings"
|
||||
return api_base
|
||||
return "https://api.voyageai.com/v1/embeddings"
|
||||
return f"{get_default_base_url(api_key)}/embeddings"
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> list:
|
||||
return [
|
||||
|
|
@ -85,14 +85,8 @@ class VoyageEmbeddingConfig(BaseEmbeddingConfig):
|
|||
api_key: str | None = None,
|
||||
api_base: str | None = None,
|
||||
) -> dict:
|
||||
if api_key is None:
|
||||
api_key = (
|
||||
get_secret_str("VOYAGE_API_KEY")
|
||||
or get_secret_str("VOYAGE_AI_API_KEY")
|
||||
or get_secret_str("VOYAGE_AI_TOKEN")
|
||||
)
|
||||
return {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Authorization": f"Bearer {get_voyage_api_key(api_key)}",
|
||||
}
|
||||
|
||||
def transform_embedding_request(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import httpx
|
|||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
from litellm.llms.base_llm.embedding.transformation import BaseEmbeddingConfig
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.llms.voyage.common_utils import get_default_base_url, get_voyage_api_key
|
||||
from litellm.types.llms.openai import AllEmbeddingInputValues, AllMessageValues
|
||||
from litellm.types.utils import EmbeddingResponse, Usage
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ class VoyageContextualEmbeddingConfig(BaseEmbeddingConfig):
|
|||
if not api_base.endswith("/contextualizedembeddings"):
|
||||
api_base = f"{api_base}/contextualizedembeddings"
|
||||
return api_base
|
||||
return "https://api.voyageai.com/v1/contextualizedembeddings"
|
||||
return f"{get_default_base_url(api_key)}/contextualizedembeddings"
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> list: # mutable-ok: base class signature
|
||||
return ["encoding_format", "dimensions"]
|
||||
|
|
@ -91,14 +91,8 @@ class VoyageContextualEmbeddingConfig(BaseEmbeddingConfig):
|
|||
api_key: str | None = None,
|
||||
api_base: str | None = None,
|
||||
) -> dict:
|
||||
if api_key is None:
|
||||
api_key = (
|
||||
get_secret_str("VOYAGE_API_KEY")
|
||||
or get_secret_str("VOYAGE_AI_API_KEY")
|
||||
or get_secret_str("VOYAGE_AI_TOKEN")
|
||||
)
|
||||
return {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Authorization": f"Bearer {get_voyage_api_key(api_key)}",
|
||||
}
|
||||
|
||||
AUTO_CHUNK_SIZE: Final = 32000
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import httpx
|
|||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
from litellm.llms.base_llm.embedding.transformation import BaseEmbeddingConfig
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.llms.voyage.common_utils import get_default_base_url, get_voyage_api_key
|
||||
from litellm.types.llms.openai import AllEmbeddingInputValues, AllMessageValues
|
||||
from litellm.types.utils import EmbeddingResponse, Usage
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ class VoyageMultimodalEmbeddingConfig(BaseEmbeddingConfig):
|
|||
if not api_base.endswith("/multimodalembeddings"):
|
||||
api_base = f"{api_base}/multimodalembeddings"
|
||||
return api_base
|
||||
return "https://api.voyageai.com/v1/multimodalembeddings"
|
||||
return f"{get_default_base_url(api_key)}/multimodalembeddings"
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> list:
|
||||
return ["dimensions"]
|
||||
|
|
@ -84,19 +84,14 @@ class VoyageMultimodalEmbeddingConfig(BaseEmbeddingConfig):
|
|||
api_key: str | None = None,
|
||||
api_base: str | None = None,
|
||||
) -> dict:
|
||||
if api_key is None:
|
||||
api_key = (
|
||||
get_secret_str("VOYAGE_API_KEY")
|
||||
or get_secret_str("VOYAGE_AI_API_KEY")
|
||||
or get_secret_str("VOYAGE_AI_TOKEN")
|
||||
)
|
||||
if not api_key:
|
||||
resolved_api_key: Final = get_voyage_api_key(api_key)
|
||||
if not resolved_api_key:
|
||||
raise ValueError(
|
||||
"Voyage API key is required for multimodal embeddings. "
|
||||
"Set VOYAGE_API_KEY / VOYAGE_AI_API_KEY / VOYAGE_AI_TOKEN "
|
||||
"or pass `api_key` explicitly."
|
||||
)
|
||||
return {"Authorization": f"Bearer {api_key}"}
|
||||
return {"Authorization": f"Bearer {resolved_api_key}"}
|
||||
|
||||
def _normalize_content_item(self, item: dict[str, object]) -> dict[str, object]:
|
||||
item_type: Final = item.get("type")
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import httpx
|
|||
from litellm._uuid import uuid
|
||||
from litellm.llms.base_llm.chat.transformation import LiteLLMLoggingObj
|
||||
from litellm.llms.base_llm.rerank.transformation import BaseRerankConfig
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.llms.voyage.common_utils import get_default_base_url, get_voyage_api_key
|
||||
from litellm.types.rerank import (
|
||||
RerankBilledUnits,
|
||||
RerankResponse,
|
||||
|
|
@ -25,6 +25,16 @@ from ..embedding.transformation import VoyageError
|
|||
|
||||
|
||||
class VoyageRerankConfig(BaseRerankConfig):
|
||||
"""
|
||||
``validate_environment`` stores the credential it authenticates with so ``get_complete_url``
|
||||
can select the host that issued it. ``ProviderConfigManager.get_provider_rerank_config``
|
||||
builds this config per request, so that key never reaches another one.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._api_key: str | None = None
|
||||
|
||||
def get_supported_cohere_rerank_params(self, model: str) -> list:
|
||||
return ["query", "documents", "top_n", "return_documents"]
|
||||
|
||||
|
|
@ -60,7 +70,7 @@ class VoyageRerankConfig(BaseRerankConfig):
|
|||
optional_params: dict | None = None,
|
||||
) -> str:
|
||||
if api_base is None:
|
||||
return "https://api.voyageai.com/v1/rerank"
|
||||
return f"{get_default_base_url(self._api_key)}/rerank"
|
||||
api_base = api_base.rstrip("/")
|
||||
if not api_base.endswith("/v1/rerank"):
|
||||
if api_base.endswith("/v1"):
|
||||
|
|
@ -141,12 +151,12 @@ class VoyageRerankConfig(BaseRerankConfig):
|
|||
optional_params: dict | None = None,
|
||||
litellm_params: Mapping[str, object] | None = None,
|
||||
) -> dict:
|
||||
if api_key is None:
|
||||
api_key = get_secret_str("VOYAGE_API_KEY") or get_secret_str("VOYAGE_AI_API_KEY")
|
||||
if api_key is None:
|
||||
resolved_api_key: Final = get_voyage_api_key(api_key)
|
||||
if resolved_api_key is None:
|
||||
raise ValueError("Voyage AI API key is required. Set via `api_key` parameter or `VOYAGE_API_KEY` env var.")
|
||||
self._api_key = resolved_api_key
|
||||
return {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Authorization": f"Bearer {resolved_api_key}",
|
||||
"content-type": "application/json",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2336,7 +2336,7 @@
|
|||
}
|
||||
},
|
||||
"voyage": {
|
||||
"display_name": "Voyage AI (`voyage`)",
|
||||
"display_name": "VoyageAI by MongoDB (`voyage`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/voyage",
|
||||
"endpoints": {
|
||||
"chat_completions": false,
|
||||
|
|
|
|||
|
|
@ -3499,7 +3499,7 @@
|
|||
},
|
||||
{
|
||||
"provider": "Voyage",
|
||||
"provider_display_name": "Voyage AI",
|
||||
"provider_display_name": "VoyageAI by MongoDB",
|
||||
"litellm_provider": "voyage",
|
||||
"credential_fields": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2697,7 +2697,7 @@
|
|||
}
|
||||
},
|
||||
"voyage": {
|
||||
"display_name": "Voyage AI (`voyage`)",
|
||||
"display_name": "VoyageAI by MongoDB (`voyage`)",
|
||||
"url": "https://docs.litellm.ai/docs/providers/voyage",
|
||||
"endpoints": {
|
||||
"chat_completions": false,
|
||||
|
|
|
|||
|
|
@ -294,11 +294,10 @@ class TestVoyageRerankTransform:
|
|||
assert "top_n" in supported_params
|
||||
assert "return_documents" in supported_params
|
||||
|
||||
@patch("litellm.llms.voyage.rerank.transformation.get_secret_str")
|
||||
def test_validate_environment_missing_api_key(self, mock_get_secret_str):
|
||||
def test_validate_environment_missing_api_key(self, monkeypatch):
|
||||
"""Test that validate_environment raises error when API key is missing."""
|
||||
# Mock get_secret_str to return None for both environment variables
|
||||
mock_get_secret_str.return_value = None
|
||||
for env_var in ("VOYAGE_API_KEY", "VOYAGE_AI_API_KEY", "VOYAGE_AI_TOKEN"):
|
||||
monkeypatch.delenv(env_var, raising=False)
|
||||
with pytest.raises(ValueError, match="Voyage AI API key is required"):
|
||||
self.config.validate_environment(
|
||||
headers={},
|
||||
|
|
|
|||
164
tests/unit/llms/voyage/test_common_utils.py
Normal file
164
tests/unit/llms/voyage/test_common_utils.py
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
import pytest
|
||||
|
||||
from litellm.llms.voyage.common_utils import (
|
||||
MONGODB_API_BASE,
|
||||
VOYAGE_API_BASE,
|
||||
get_default_base_url,
|
||||
get_voyage_api_key,
|
||||
)
|
||||
from litellm.llms.voyage.embedding.transformation import VoyageEmbeddingConfig
|
||||
from litellm.llms.voyage.embedding.transformation_contextual import (
|
||||
VoyageContextualEmbeddingConfig,
|
||||
)
|
||||
from litellm.llms.voyage.embedding.transformation_multimodal import (
|
||||
VoyageMultimodalEmbeddingConfig,
|
||||
)
|
||||
from litellm.llms.voyage.rerank.transformation import VoyageRerankConfig
|
||||
|
||||
VOYAGE_KEY_ENV_VARS = ("VOYAGE_API_KEY", "VOYAGE_AI_API_KEY", "VOYAGE_AI_TOKEN")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clear_voyage_env(monkeypatch):
|
||||
for env_var in VOYAGE_KEY_ENV_VARS:
|
||||
monkeypatch.delenv(env_var, raising=False)
|
||||
|
||||
|
||||
def test_mongodb_key_routes_to_mongodb_host():
|
||||
"""MongoDB-issued keys carry the `al-` prefix and are only valid on ai.mongodb.com"""
|
||||
assert get_default_base_url("al-1234567890") == MONGODB_API_BASE
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api_key", ["pa-1234567890", "sk-1234567890", "al", "", None])
|
||||
def test_non_mongodb_key_routes_to_voyage_host(api_key):
|
||||
assert get_default_base_url(api_key) == VOYAGE_API_BASE
|
||||
|
||||
|
||||
@pytest.mark.parametrize("env_var", VOYAGE_KEY_ENV_VARS)
|
||||
def test_mongodb_key_from_any_supported_env_var_routes_to_mongodb_host(monkeypatch, env_var):
|
||||
monkeypatch.setenv(env_var, "al-from-env")
|
||||
assert get_default_base_url() == MONGODB_API_BASE
|
||||
|
||||
|
||||
def test_explicit_key_wins_over_env_for_routing(monkeypatch):
|
||||
monkeypatch.setenv("VOYAGE_API_KEY", "al-from-env")
|
||||
assert get_default_base_url("pa-explicit") == VOYAGE_API_BASE
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"config, endpoint",
|
||||
[
|
||||
(VoyageEmbeddingConfig(), "embeddings"),
|
||||
(VoyageContextualEmbeddingConfig(), "contextualizedembeddings"),
|
||||
(VoyageMultimodalEmbeddingConfig(), "multimodalembeddings"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("api_key, expected_host", [("al-key", MONGODB_API_BASE), ("pa-key", VOYAGE_API_BASE)])
|
||||
def test_embedding_configs_route_by_key_prefix(config, endpoint, api_key, expected_host):
|
||||
url = config.get_complete_url(None, api_key, "voyage-3", {}, {})
|
||||
assert url == f"{expected_host}/{endpoint}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"config, endpoint",
|
||||
[
|
||||
(VoyageEmbeddingConfig(), "embeddings"),
|
||||
(VoyageContextualEmbeddingConfig(), "contextualizedembeddings"),
|
||||
(VoyageMultimodalEmbeddingConfig(), "multimodalembeddings"),
|
||||
],
|
||||
)
|
||||
def test_explicit_api_base_overrides_key_routing(config, endpoint):
|
||||
url = config.get_complete_url("https://gateway.internal/v1", "al-key", "voyage-3", {}, {})
|
||||
assert url == f"https://gateway.internal/v1/{endpoint}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api_key, expected_host", [("al-key", MONGODB_API_BASE), ("pa-key", VOYAGE_API_BASE)])
|
||||
def test_rerank_routes_by_request_key_prefix(api_key, expected_host):
|
||||
config = VoyageRerankConfig()
|
||||
config.validate_environment({}, "rerank-2.5", api_key=api_key)
|
||||
|
||||
assert config.get_complete_url(None, "rerank-2.5") == f"{expected_host}/rerank"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api_key, expected_host", [("al-key", MONGODB_API_BASE), ("pa-key", VOYAGE_API_BASE)])
|
||||
def test_rerank_routes_by_env_key_prefix(monkeypatch, api_key, expected_host):
|
||||
monkeypatch.setenv("VOYAGE_API_KEY", api_key)
|
||||
config = VoyageRerankConfig()
|
||||
config.validate_environment({}, "rerank-2.5")
|
||||
|
||||
assert config.get_complete_url(None, "rerank-2.5") == f"{expected_host}/rerank"
|
||||
|
||||
|
||||
def test_rerank_request_key_beats_env_key_for_routing(monkeypatch):
|
||||
"""A MongoDB key on the request must not be posted to the Voyage host the env key names"""
|
||||
monkeypatch.setenv("VOYAGE_API_KEY", "pa-from-env")
|
||||
config = VoyageRerankConfig()
|
||||
|
||||
headers = config.validate_environment({}, "rerank-2.5", api_key="al-on-request")
|
||||
|
||||
assert headers["Authorization"] == "Bearer al-on-request"
|
||||
assert config.get_complete_url(None, "rerank-2.5") == f"{MONGODB_API_BASE}/rerank"
|
||||
|
||||
|
||||
def test_rerank_config_is_built_per_request_so_keys_cannot_leak(monkeypatch):
|
||||
"""get_complete_url reads a key off the instance, so each request must get its own instance"""
|
||||
import litellm
|
||||
from litellm.types.utils import LlmProviders
|
||||
from litellm.utils import ProviderConfigManager
|
||||
|
||||
monkeypatch.delenv("VOYAGE_API_KEY", raising=False)
|
||||
first = ProviderConfigManager.get_provider_rerank_config(
|
||||
model="rerank-2.5", provider=LlmProviders.VOYAGE, api_base=None, present_version_params=[]
|
||||
)
|
||||
second = ProviderConfigManager.get_provider_rerank_config(
|
||||
model="rerank-2.5", provider=LlmProviders.VOYAGE, api_base=None, present_version_params=[]
|
||||
)
|
||||
assert isinstance(first, litellm.VoyageRerankConfig) and first is not second
|
||||
|
||||
first.validate_environment({}, "rerank-2.5", api_key="al-first-request")
|
||||
second.validate_environment({}, "rerank-2.5", api_key="pa-second-request")
|
||||
|
||||
assert first.get_complete_url(None, "rerank-2.5") == f"{MONGODB_API_BASE}/rerank"
|
||||
assert second.get_complete_url(None, "rerank-2.5") == f"{VOYAGE_API_BASE}/rerank"
|
||||
|
||||
|
||||
def test_rerank_falls_back_to_env_when_validate_environment_did_not_run(monkeypatch):
|
||||
"""A caller that skips validate_environment keeps the pre-existing env-only behaviour"""
|
||||
monkeypatch.setenv("VOYAGE_API_KEY", "al-from-env")
|
||||
|
||||
assert VoyageRerankConfig().get_complete_url(None, "rerank-2.5") == f"{MONGODB_API_BASE}/rerank"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"config",
|
||||
[VoyageEmbeddingConfig(), VoyageContextualEmbeddingConfig(), VoyageMultimodalEmbeddingConfig()],
|
||||
)
|
||||
def test_auth_header_uses_the_key_the_url_was_routed_on(monkeypatch, config):
|
||||
"""The host is picked from a key, so the Authorization header has to carry that same key"""
|
||||
monkeypatch.setenv("VOYAGE_AI_TOKEN", "al-from-env")
|
||||
|
||||
headers = config.validate_environment({}, "voyage-3", [], {}, {})
|
||||
url = config.get_complete_url(None, None, "voyage-3", {}, {})
|
||||
|
||||
assert headers["Authorization"] == "Bearer al-from-env"
|
||||
assert url.startswith(MONGODB_API_BASE)
|
||||
|
||||
|
||||
def test_rerank_auth_header_uses_the_key_the_url_was_routed_on(monkeypatch):
|
||||
monkeypatch.setenv("VOYAGE_AI_TOKEN", "al-from-env")
|
||||
config = VoyageRerankConfig()
|
||||
|
||||
headers = config.validate_environment({}, "rerank-2.5")
|
||||
|
||||
assert headers["Authorization"] == "Bearer al-from-env"
|
||||
assert config.get_complete_url(None, "rerank-2.5").startswith(MONGODB_API_BASE)
|
||||
|
||||
|
||||
def test_get_voyage_api_key_prefers_env_vars_in_documented_order(monkeypatch):
|
||||
monkeypatch.setenv("VOYAGE_AI_API_KEY", "second")
|
||||
monkeypatch.setenv("VOYAGE_AI_TOKEN", "third")
|
||||
assert get_voyage_api_key() == "second"
|
||||
|
||||
monkeypatch.setenv("VOYAGE_API_KEY", "first")
|
||||
assert get_voyage_api_key() == "first"
|
||||
assert get_voyage_api_key("explicit") == "explicit"
|
||||
|
|
@ -172,15 +172,12 @@ class TestVoyageMultimodalEmbeddings:
|
|||
assert headers == {"Authorization": "Bearer test-key"}
|
||||
|
||||
def test_validate_environment_uses_secret_fallback(self, monkeypatch):
|
||||
import litellm.llms.voyage.embedding.transformation_multimodal as module
|
||||
from litellm.llms.voyage.embedding.transformation_multimodal import (
|
||||
VoyageMultimodalEmbeddingConfig,
|
||||
)
|
||||
|
||||
def fake_get_secret(name):
|
||||
return "secret-key" if name == "VOYAGE_AI_API_KEY" else None
|
||||
|
||||
monkeypatch.setattr(module, "get_secret_str", fake_get_secret)
|
||||
monkeypatch.delenv("VOYAGE_API_KEY", raising=False)
|
||||
monkeypatch.setenv("VOYAGE_AI_API_KEY", "secret-key")
|
||||
config = VoyageMultimodalEmbeddingConfig()
|
||||
headers = config.validate_environment(
|
||||
{}, "voyage-multimodal-3.5", [], {}, {}, api_key=None
|
||||
|
|
@ -188,12 +185,12 @@ class TestVoyageMultimodalEmbeddings:
|
|||
assert headers == {"Authorization": "Bearer secret-key"}
|
||||
|
||||
def test_validate_environment_raises_without_api_key(self, monkeypatch):
|
||||
import litellm.llms.voyage.embedding.transformation_multimodal as module
|
||||
from litellm.llms.voyage.embedding.transformation_multimodal import (
|
||||
VoyageMultimodalEmbeddingConfig,
|
||||
)
|
||||
|
||||
monkeypatch.setattr(module, "get_secret_str", lambda name: None)
|
||||
for env_var in ("VOYAGE_API_KEY", "VOYAGE_AI_API_KEY", "VOYAGE_AI_TOKEN"):
|
||||
monkeypatch.delenv(env_var, raising=False)
|
||||
config = VoyageMultimodalEmbeddingConfig()
|
||||
with pytest.raises(ValueError, match='Voyage API key is required for multimodal embeddings\\. Set') as exc_info:
|
||||
config.validate_environment(
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ export enum Providers {
|
|||
VERTEX_AI_BETA = "Vertex Ai Beta",
|
||||
VLLM = "Local vLLM",
|
||||
VolcEngine = "VolcEngine",
|
||||
Voyage = "Voyage AI",
|
||||
Voyage = "VoyageAI by MongoDB",
|
||||
WANDB = "Wandb",
|
||||
WATSONX = "Watsonx",
|
||||
WATSONX_TEXT = "Watsonx Text",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue