Merge pull request #39149 from BerriAI/litellm_qwencloud_provider_aliases

feat(dashscope): add QwenCloud and Qwen AI Platform provider aliases
This commit is contained in:
Mateo Wang 2026-09-01 12:18:05 -07:00 committed by GitHub
commit 435433fa07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 4644 additions and 66 deletions

View file

@ -354,6 +354,8 @@ curl -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
| [Petals (`petals`)](https://docs.litellm.ai/docs/providers/petals) | ✅ | ✅ | ✅ | | | | | | | |
| [Pinstripes (`pinstripes`)](https://docs.litellm.ai/docs/providers/pinstripes) | ✅ | ✅ | ✅ | | | | | | | |
| [Predibase (`predibase`)](https://docs.litellm.ai/docs/providers/predibase) | ✅ | ✅ | ✅ | | | | | | | |
| [Qwen AI Platform (`qwen_ai_platform`)](https://docs.litellm.ai/docs/providers/qwencloud) | ✅ | ✅ | ✅ | ✅ | ✅ | | | | | ✅ |
| [QwenCloud (`qwencloud`)](https://docs.litellm.ai/docs/providers/qwencloud) | ✅ | ✅ | ✅ | ✅ | ✅ | | | | | ✅ |
| [Recraft (`recraft`)](https://docs.litellm.ai/docs/providers/recraft) | | | | | ✅ | | | | | |
| [Replicate (`replicate`)](https://docs.litellm.ai/docs/providers/replicate) | ✅ | ✅ | ✅ | | | | | | | |
| [Sagemaker Chat (`sagemaker_chat`)](https://docs.litellm.ai/docs/providers/aws_sagemaker) | ✅ | ✅ | ✅ | | | | | | | |

View file

@ -659,6 +659,8 @@ aiml_models: Set = set()
deepgram_models: Set = set()
elevenlabs_models: Set = set()
dashscope_models: Set = set()
qwencloud_models: Set = set()
qwen_ai_platform_models: Set = set()
moonshot_models: Set = set()
publicai_models: Set = set()
darkbloom_models: Set = set()
@ -909,6 +911,10 @@ def _populate_provider_model_sets(model_cost_map: Dict) -> None:
heroku_models.add(key)
elif value.get("litellm_provider") == "dashscope":
dashscope_models.add(key)
elif value.get("litellm_provider") == "qwencloud":
qwencloud_models.add(key)
elif value.get("litellm_provider") == "qwen_ai_platform":
qwen_ai_platform_models.add(key)
elif value.get("litellm_provider") == "modelscope":
modelscope_models.add(key)
elif value.get("litellm_provider") == "moonshot":
@ -1072,6 +1078,8 @@ model_list = list(
| deepgram_models
| elevenlabs_models
| dashscope_models
| qwencloud_models
| qwen_ai_platform_models
| moonshot_models
| publicai_models
| darkbloom_models
@ -1178,6 +1186,8 @@ def _build_models_by_provider() -> dict:
"elevenlabs": elevenlabs_models,
"heroku": heroku_models,
"dashscope": dashscope_models,
"qwencloud": qwencloud_models,
"qwen_ai_platform": qwen_ai_platform_models,
"modelscope": modelscope_models,
"moonshot": moonshot_models,
"publicai": publicai_models,
@ -2014,6 +2024,24 @@ if TYPE_CHECKING:
from .llms.dashscope.rerank.transformation import (
DashScopeRerankConfig as DashScopeRerankConfig,
)
from .llms.dashscope.qwencloud import (
QwenCloudChatConfig as QwenCloudChatConfig,
)
from .llms.dashscope.qwencloud import (
QwenCloudEmbeddingConfig as QwenCloudEmbeddingConfig,
)
from .llms.dashscope.qwencloud import (
QwenCloudRerankConfig as QwenCloudRerankConfig,
)
from .llms.dashscope.qwen_ai_platform import (
QwenAIPlatformChatConfig as QwenAIPlatformChatConfig,
)
from .llms.dashscope.qwen_ai_platform import (
QwenAIPlatformEmbeddingConfig as QwenAIPlatformEmbeddingConfig,
)
from .llms.dashscope.qwen_ai_platform import (
QwenAIPlatformRerankConfig as QwenAIPlatformRerankConfig,
)
from .llms.modelscope.chat.transformation import (
ModelScopeChatConfig as ModelScopeChatConfig,
)

View file

@ -310,6 +310,8 @@ LLM_CONFIG_NAMES: Final = (
"GigaChatConfig",
"GigaChatEmbeddingConfig",
"DashScopeChatConfig",
"QwenCloudChatConfig",
"QwenAIPlatformChatConfig",
"ModelScopeChatConfig",
"MoonshotChatConfig",
"DockerModelRunnerChatConfig",
@ -1172,6 +1174,14 @@ _LLM_CONFIGS_IMPORT_MAP: Final = {
".llms.dashscope.chat.transformation",
"DashScopeChatConfig",
),
"QwenCloudChatConfig": (
".llms.dashscope.qwencloud",
"QwenCloudChatConfig",
),
"QwenAIPlatformChatConfig": (
".llms.dashscope.qwen_ai_platform",
"QwenAIPlatformChatConfig",
),
"GDCGeminiConfig": (
".llms.gdc.chat.transformation",
"GDCGeminiConfig",

View file

@ -630,6 +630,8 @@ LITELLM_CHAT_PROVIDERS: Final = [
"nscale",
"nebius",
"dashscope",
"qwencloud",
"qwen_ai_platform",
"modelscope",
"moonshot",
"publicai",
@ -799,6 +801,7 @@ openai_compatible_endpoints: Final[list] = [
"inference.api.nscale.com/v1",
"api.studio.nebius.ai/v1",
"https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
"https://dashscope.aliyuncs.com/compatible-mode/v1",
"https://api-inference.modelscope.cn/v1",
"https://api.moonshot.ai/v1",
"https://api.publicai.co/v1",
@ -872,6 +875,8 @@ openai_compatible_providers: Final[list] = [
"nscale",
"nebius",
"dashscope",
"qwencloud",
"qwen_ai_platform",
"modelscope",
"moonshot",
"v0",
@ -902,6 +907,8 @@ openai_text_completion_compatible_providers: Final[list] = [ # providers that s
"featherless_ai",
"nebius",
"dashscope",
"qwencloud",
"qwen_ai_platform",
"modelscope",
"moonshot",
"publicai",
@ -1109,7 +1116,7 @@ nebius_models: Final[set] = set(
]
)
dashscope_models: Final[set] = set(
dashscope_models: Final[frozenset] = frozenset(
[
"qwen-turbo",
"qwen-plus",
@ -1124,6 +1131,10 @@ dashscope_models: Final[set] = set(
]
)
qwencloud_models: Final[frozenset] = frozenset(dashscope_models)
qwen_ai_platform_models: Final[frozenset] = frozenset(dashscope_models)
nebius_embedding_models: Final[set] = set(
[
"BAAI/bge-en-icl",

View file

@ -641,12 +641,12 @@ def cost_per_token(
return xai_cost_per_token(model=model, usage=usage_block)
elif custom_llm_provider == "lemonade":
return lemonade_cost_per_token(model=model, usage=usage_block)
elif custom_llm_provider == "dashscope":
elif custom_llm_provider in ("dashscope", "qwencloud", "qwen_ai_platform"):
from litellm.llms.dashscope.cost_calculator import (
cost_per_token as dashscope_cost_per_token,
)
return dashscope_cost_per_token(model=model, usage=usage_block)
return dashscope_cost_per_token(model=model, usage=usage_block, custom_llm_provider=custom_llm_provider)
elif custom_llm_provider == "azure_ai":
return azure_ai_cost_per_token(
model=model,

View file

@ -386,6 +386,8 @@ def image_generation(
litellm.LlmProviders.VERTEX_AI,
litellm.LlmProviders.OPENROUTER,
litellm.LlmProviders.DASHSCOPE,
litellm.LlmProviders.QWENCLOUD,
litellm.LlmProviders.QWEN_AI_PLATFORM,
):
if image_generation_config is None:
raise ValueError(f"image generation config is not supported for {custom_llm_provider}")

View file

@ -536,6 +536,14 @@ def get_llm_provider(
)
def _dashscope_family_chat_config(custom_llm_provider: str) -> "litellm.DashScopeChatConfig":
if custom_llm_provider == "qwencloud":
return litellm.QwenCloudChatConfig()
if custom_llm_provider == "qwen_ai_platform":
return litellm.QwenAIPlatformChatConfig()
return litellm.DashScopeChatConfig()
def _get_openai_compatible_provider_info(
model: str,
api_base: str | None,
@ -785,11 +793,11 @@ def _get_openai_compatible_provider_info(
api_base,
dynamic_api_key,
) = litellm.HerokuChatConfig()._get_openai_compatible_provider_info(api_base, api_key)
elif custom_llm_provider == "dashscope":
elif custom_llm_provider in ("dashscope", "qwencloud", "qwen_ai_platform"):
(
api_base,
dynamic_api_key,
) = litellm.DashScopeChatConfig()._get_openai_compatible_provider_info(api_base, api_key)
) = _dashscope_family_chat_config(custom_llm_provider)._get_openai_compatible_provider_info(api_base, api_key)
elif custom_llm_provider == "modelscope":
(
api_base,

View file

@ -54,6 +54,9 @@ class DashScopeChatConfig(OpenAIGPTConfig):
dynamic_api_key: Final = api_key or get_secret_str("DASHSCOPE_API_KEY")
return api_base, dynamic_api_key
def _resolve_chat_api_base(self, api_base: str | None) -> str:
return api_base or "https://dashscope.aliyuncs.com/compatible-mode/v1"
def get_complete_url(
self,
api_base: str | None,
@ -66,10 +69,7 @@ class DashScopeChatConfig(OpenAIGPTConfig):
"""
If api_base is not provided, use the default DashScope /chat/completions endpoint.
"""
if not api_base:
api_base = "https://dashscope.aliyuncs.com/compatible-mode/v1"
if not api_base.endswith("/chat/completions"):
api_base = f"{api_base}/chat/completions"
return api_base
resolved_api_base: Final = self._resolve_chat_api_base(api_base)
if resolved_api_base.endswith("/chat/completions"):
return resolved_api_base
return f"{resolved_api_base}/chat/completions"

View file

@ -2,9 +2,89 @@
Common utilities for the DashScope LLM provider.
"""
from typing import TYPE_CHECKING
import httpx
from litellm.llms.base_llm.chat.transformation import BaseLLMException
from litellm.secret_managers.main import get_secret_str
if TYPE_CHECKING:
from litellm.llms.base_llm.embedding.transformation import BaseEmbeddingConfig
from litellm.llms.base_llm.image_generation.transformation import (
BaseImageGenerationConfig,
)
from litellm.llms.base_llm.rerank.transformation import BaseRerankConfig
def get_dashscope_family_embedding_config(custom_llm_provider: str) -> "BaseEmbeddingConfig":
if custom_llm_provider == "qwencloud":
from litellm.llms.dashscope.qwencloud import QwenCloudEmbeddingConfig
return QwenCloudEmbeddingConfig()
if custom_llm_provider == "qwen_ai_platform":
from litellm.llms.dashscope.qwen_ai_platform import (
QwenAIPlatformEmbeddingConfig,
)
return QwenAIPlatformEmbeddingConfig()
from litellm.llms.dashscope.embed.transformation import DashScopeEmbeddingConfig
return DashScopeEmbeddingConfig()
def get_dashscope_family_rerank_config(custom_llm_provider: str) -> "BaseRerankConfig":
if custom_llm_provider == "qwencloud":
from litellm.llms.dashscope.qwencloud import QwenCloudRerankConfig
return QwenCloudRerankConfig()
if custom_llm_provider == "qwen_ai_platform":
from litellm.llms.dashscope.qwen_ai_platform import QwenAIPlatformRerankConfig
return QwenAIPlatformRerankConfig()
from litellm.llms.dashscope.rerank.transformation import DashScopeRerankConfig
return DashScopeRerankConfig()
def get_dashscope_family_image_generation_config(
custom_llm_provider: str,
) -> "BaseImageGenerationConfig":
if custom_llm_provider == "qwencloud":
from litellm.llms.dashscope.qwencloud import QwenCloudImageGenerationConfig
return QwenCloudImageGenerationConfig()
if custom_llm_provider == "qwen_ai_platform":
from litellm.llms.dashscope.qwen_ai_platform import (
QwenAIPlatformImageGenerationConfig,
)
return QwenAIPlatformImageGenerationConfig()
from litellm.llms.dashscope.image_generation.transformation import (
DashScopeImageGenerationConfig,
)
return DashScopeImageGenerationConfig()
def resolve_dashscope_family_api_key(custom_llm_provider: str, api_key: str | None) -> str | None:
if custom_llm_provider == "dashscope":
return api_key or get_secret_str("DASHSCOPE_API_KEY")
return api_key or get_secret_str(f"{custom_llm_provider.upper()}_API_KEY") or get_secret_str("DASHSCOPE_API_KEY")
def missing_dashscope_family_key_message(custom_llm_provider: str) -> str:
if custom_llm_provider == "qwencloud":
return (
"Missing API key for QwenCloud. Set QWENCLOUD_API_KEY or "
"DASHSCOPE_API_KEY environment variable or pass api_key parameter."
)
if custom_llm_provider == "qwen_ai_platform":
return (
"Missing API key for Qwen AI Platform. Set QWEN_AI_PLATFORM_API_KEY or "
"DASHSCOPE_API_KEY environment variable or pass api_key parameter."
)
return "Missing API key for DashScope. Set DASHSCOPE_API_KEY environment variable or pass api_key parameter."
class DashScopeError(BaseLLMException):

View file

@ -110,7 +110,7 @@ def _calculate_completion_cost(
return (breakdown.completion_tokens * output_cost) + (breakdown.reasoning_tokens * reasoning_cost)
def cost_per_token(model: str, usage: Usage) -> tuple[float, float]:
def cost_per_token(model: str, usage: Usage, custom_llm_provider: str = "dashscope") -> tuple[float, float]:
"""
Calculate cost per token for Dashscope models.
@ -119,11 +119,12 @@ def cost_per_token(model: str, usage: Usage) -> tuple[float, float]:
Args:
model: Model name without provider prefix
usage: LiteLLM Usage block
custom_llm_provider: The provider id the request resolved to; dashscope or one of its brand aliases
Returns:
Tuple[float, float] - (prompt_cost_in_usd, completion_cost_in_usd)
"""
model_info: Final = get_model_info(model=model, custom_llm_provider="dashscope")
model_info: Final = get_model_info(model=model, custom_llm_provider=custom_llm_provider)
breakdown: Final = _extract_token_breakdown(usage)
raw_tiers: Final = model_info.get("tiered_pricing")
tiered_pricing: Final = raw_tiers if isinstance(raw_tiers, list) else None

View file

@ -62,6 +62,17 @@ class DashScopeEmbeddingConfig(BaseEmbeddingConfig):
# for drop_params=False before this method is called.
return optional_params
def _resolve_api_key(self, api_key: str | None) -> str:
resolved_api_key: Final = api_key if api_key is not None else get_secret_str("DASHSCOPE_API_KEY")
if resolved_api_key is None:
raise ValueError(
"DashScope API key is required. Set 'DASHSCOPE_API_KEY' env var or pass api_key explicitly."
)
return resolved_api_key
def _resolve_embedding_api_base(self, api_base: str | None) -> str:
return api_base or get_secret_str("DASHSCOPE_API_BASE") or DEFAULT_API_BASE
def validate_environment(
self,
headers: dict,
@ -72,17 +83,11 @@ class DashScopeEmbeddingConfig(BaseEmbeddingConfig):
api_key: str | None = None,
api_base: str | None = None,
) -> dict:
if api_key is None:
api_key = get_secret_str("DASHSCOPE_API_KEY")
if api_key is None:
raise ValueError(
"DashScope API key is required. Set 'DASHSCOPE_API_KEY' env var or pass api_key explicitly."
)
default_headers: Final = {
return {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
"Authorization": f"Bearer {self._resolve_api_key(api_key)}",
**headers,
}
return {**default_headers, **headers}
def get_complete_url(
self,
@ -93,8 +98,7 @@ class DashScopeEmbeddingConfig(BaseEmbeddingConfig):
litellm_params: dict,
stream: bool | None = None,
) -> str:
base = api_base or get_secret_str("DASHSCOPE_API_BASE") or DEFAULT_API_BASE
base = base.rstrip("/")
base: Final = self._resolve_embedding_api_base(api_base).rstrip("/")
if base.endswith("/embeddings"):
return base
return f"{base}/embeddings"

View file

@ -91,6 +91,15 @@ class DashScopeImageGenerationConfig(BaseImageGenerationConfig):
mapped[k] = v
return mapped
def _resolve_api_key(self, api_key: str | None) -> str:
resolved_api_key: Final = api_key or get_secret_str("DASHSCOPE_API_KEY")
if not resolved_api_key:
raise ValueError("DASHSCOPE_API_KEY is not set")
return resolved_api_key
def _resolve_image_api_base(self, image_api_base: str | None) -> str:
return image_api_base or get_secret_str("DASHSCOPE_API_BASE_IMAGE") or DEFAULT_API_BASE
def get_complete_url(
self,
api_base: str | None,
@ -103,7 +112,7 @@ class DashScopeImageGenerationConfig(BaseImageGenerationConfig):
image_api_base: Final = (
api_base if api_base and not api_base.rstrip("/").endswith(CHAT_COMPATIBLE_MODE_PATH) else None
)
return image_api_base or get_secret_str("DASHSCOPE_API_BASE_IMAGE") or DEFAULT_API_BASE
return self._resolve_image_api_base(image_api_base)
def validate_environment(
self,
@ -115,10 +124,7 @@ class DashScopeImageGenerationConfig(BaseImageGenerationConfig):
api_key: str | None = None,
api_base: str | None = None,
) -> dict:
final_api_key: Final = api_key or get_secret_str("DASHSCOPE_API_KEY")
if not final_api_key:
raise ValueError("DASHSCOPE_API_KEY is not set")
headers["Authorization"] = f"Bearer {final_api_key}"
headers["Authorization"] = f"Bearer {self._resolve_api_key(api_key)}"
headers["Content-Type"] = "application/json"
return headers

View file

@ -0,0 +1,62 @@
from typing import Final
from litellm.secret_managers.main import get_secret_str
from .chat.transformation import DashScopeChatConfig
from .embed.transformation import DashScopeEmbeddingConfig
from .image_generation.transformation import DashScopeImageGenerationConfig
from .rerank.transformation import DashScopeRerankConfig
QWEN_AI_PLATFORM_API_BASE: Final = "https://dashscope.aliyuncs.com/compatible-mode/v1"
QWEN_AI_PLATFORM_RERANK_API_BASE: Final = "https://dashscope.aliyuncs.com/compatible-api/v1/reranks"
QWEN_AI_PLATFORM_IMAGE_API_BASE: Final = (
"https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"
)
def _resolve_qwen_ai_platform_api_key(api_key: str | None) -> str | None:
return api_key or get_secret_str("QWEN_AI_PLATFORM_API_KEY") or get_secret_str("DASHSCOPE_API_KEY")
def _require_qwen_ai_platform_api_key(api_key: str | None) -> str:
resolved: Final = _resolve_qwen_ai_platform_api_key(api_key)
if resolved is None:
raise ValueError(
"Qwen AI Platform API key is required. Set 'QWEN_AI_PLATFORM_API_KEY' or 'DASHSCOPE_API_KEY' env var "
"or pass api_key explicitly."
)
return resolved
class QwenAIPlatformChatConfig(DashScopeChatConfig):
def _get_openai_compatible_provider_info(
self, api_base: str | None, api_key: str | None
) -> tuple[str | None, str | None]:
return self._resolve_chat_api_base(api_base), _resolve_qwen_ai_platform_api_key(api_key)
def _resolve_chat_api_base(self, api_base: str | None) -> str:
return api_base or get_secret_str("QWEN_AI_PLATFORM_API_BASE") or QWEN_AI_PLATFORM_API_BASE
class QwenAIPlatformEmbeddingConfig(DashScopeEmbeddingConfig):
def _resolve_api_key(self, api_key: str | None) -> str:
return _require_qwen_ai_platform_api_key(api_key)
def _resolve_embedding_api_base(self, api_base: str | None) -> str:
return api_base or get_secret_str("QWEN_AI_PLATFORM_API_BASE") or QWEN_AI_PLATFORM_API_BASE
class QwenAIPlatformRerankConfig(DashScopeRerankConfig):
def _resolve_api_key(self, api_key: str | None) -> str:
return _require_qwen_ai_platform_api_key(api_key)
def _resolve_rerank_api_base(self, api_base: str | None) -> str:
return api_base or get_secret_str("QWEN_AI_PLATFORM_API_BASE_RERANK") or QWEN_AI_PLATFORM_RERANK_API_BASE
class QwenAIPlatformImageGenerationConfig(DashScopeImageGenerationConfig):
def _resolve_api_key(self, api_key: str | None) -> str:
return _require_qwen_ai_platform_api_key(api_key)
def _resolve_image_api_base(self, image_api_base: str | None) -> str:
return image_api_base or get_secret_str("QWEN_AI_PLATFORM_API_BASE_IMAGE") or QWEN_AI_PLATFORM_IMAGE_API_BASE

View file

@ -0,0 +1,62 @@
from typing import Final
from litellm.secret_managers.main import get_secret_str
from .chat.transformation import DashScopeChatConfig
from .embed.transformation import DashScopeEmbeddingConfig
from .image_generation.transformation import DashScopeImageGenerationConfig
from .rerank.transformation import DashScopeRerankConfig
QWENCLOUD_API_BASE: Final = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
QWENCLOUD_RERANK_API_BASE: Final = "https://dashscope-intl.aliyuncs.com/compatible-api/v1/reranks"
QWENCLOUD_IMAGE_API_BASE: Final = (
"https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"
)
def _resolve_qwencloud_api_key(api_key: str | None) -> str | None:
return api_key or get_secret_str("QWENCLOUD_API_KEY") or get_secret_str("DASHSCOPE_API_KEY")
def _require_qwencloud_api_key(api_key: str | None) -> str:
resolved: Final = _resolve_qwencloud_api_key(api_key)
if resolved is None:
raise ValueError(
"QwenCloud API key is required. Set 'QWENCLOUD_API_KEY' or 'DASHSCOPE_API_KEY' env var "
"or pass api_key explicitly."
)
return resolved
class QwenCloudChatConfig(DashScopeChatConfig):
def _get_openai_compatible_provider_info(
self, api_base: str | None, api_key: str | None
) -> tuple[str | None, str | None]:
return self._resolve_chat_api_base(api_base), _resolve_qwencloud_api_key(api_key)
def _resolve_chat_api_base(self, api_base: str | None) -> str:
return api_base or get_secret_str("QWENCLOUD_API_BASE") or QWENCLOUD_API_BASE
class QwenCloudEmbeddingConfig(DashScopeEmbeddingConfig):
def _resolve_api_key(self, api_key: str | None) -> str:
return _require_qwencloud_api_key(api_key)
def _resolve_embedding_api_base(self, api_base: str | None) -> str:
return api_base or get_secret_str("QWENCLOUD_API_BASE") or QWENCLOUD_API_BASE
class QwenCloudRerankConfig(DashScopeRerankConfig):
def _resolve_api_key(self, api_key: str | None) -> str:
return _require_qwencloud_api_key(api_key)
def _resolve_rerank_api_base(self, api_base: str | None) -> str:
return api_base or get_secret_str("QWENCLOUD_API_BASE_RERANK") or QWENCLOUD_RERANK_API_BASE
class QwenCloudImageGenerationConfig(DashScopeImageGenerationConfig):
def _resolve_api_key(self, api_key: str | None) -> str:
return _require_qwencloud_api_key(api_key)
def _resolve_image_api_base(self, image_api_base: str | None) -> str:
return image_api_base or get_secret_str("QWENCLOUD_API_BASE_IMAGE") or QWENCLOUD_IMAGE_API_BASE

View file

@ -58,19 +58,30 @@ class DashScopeRerankConfig(BaseRerankConfig):
def __init__(self) -> None:
pass
def _resolve_api_key(self, api_key: str | None) -> str:
resolved_api_key: Final = api_key if api_key is not None else get_secret_str("DASHSCOPE_API_KEY")
if resolved_api_key is None:
raise ValueError(
"DashScope API key is required. Set 'DASHSCOPE_API_KEY' env var or pass api_key explicitly."
)
return resolved_api_key
def _resolve_rerank_api_base(self, api_base: str | None) -> str:
if api_base is not None:
return api_base
return get_secret_str("DASHSCOPE_API_BASE_RERANK") or DEFAULT_RERANK_URL
def get_complete_url(
self,
api_base: str | None,
model: str,
optional_params: dict | None = None,
) -> str:
if api_base is None:
api_base = get_secret_str("DASHSCOPE_API_BASE_RERANK") or DEFAULT_RERANK_URL
resolved_api_base: Final = self._resolve_rerank_api_base(api_base)
if resolved_api_base == DEFAULT_RERANK_URL:
return resolved_api_base
if api_base == DEFAULT_RERANK_URL:
return DEFAULT_RERANK_URL
cleaned: Final = api_base.rstrip("/")
cleaned: Final = resolved_api_base.rstrip("/")
if cleaned.endswith("/reranks") or cleaned.endswith("/rerank"):
return cleaned
@ -88,19 +99,12 @@ class DashScopeRerankConfig(BaseRerankConfig):
optional_params: dict | None = None,
litellm_params: Mapping[str, object] | None = None,
) -> dict:
if api_key is None:
api_key = get_secret_str("DASHSCOPE_API_KEY")
if api_key is None:
raise ValueError(
"DashScope API key is required. Set 'DASHSCOPE_API_KEY' env var or pass api_key explicitly."
)
default_headers: Final = {
"Authorization": f"Bearer {api_key}",
return {
"Authorization": f"Bearer {self._resolve_api_key(api_key)}",
"accept": "application/json",
"content-type": "application/json",
**headers,
}
return {**default_headers, **headers}
def get_supported_cohere_rerank_params(self, model: str) -> list:
return ["query", "documents", "top_n", "return_documents"]

View file

@ -6949,12 +6949,18 @@ def embedding(
aembedding=aembedding,
headers=headers,
)
elif custom_llm_provider == "dashscope":
dashscope_key: Final = api_key or litellm.api_key or get_secret_str("DASHSCOPE_API_KEY")
elif custom_llm_provider in ("dashscope", "qwencloud", "qwen_ai_platform"):
from litellm.llms.dashscope.common_utils import (
missing_dashscope_family_key_message,
resolve_dashscope_family_api_key,
)
dashscope_key: Final = resolve_dashscope_family_api_key(
custom_llm_provider=custom_llm_provider,
api_key=api_key or litellm.api_key,
)
if dashscope_key is None:
raise ValueError(
"Missing API key for DashScope. Set DASHSCOPE_API_KEY environment variable or pass api_key parameter."
)
raise ValueError(missing_dashscope_family_key_message(custom_llm_provider))
if extra_headers is not None and isinstance(extra_headers, dict):
headers = extra_headers
else:

File diff suppressed because it is too large Load diff

View file

@ -671,6 +671,42 @@
"interactions": true
}
},
"qwencloud": {
"display_name": "QwenCloud (`qwencloud`)",
"url": "https://docs.litellm.ai/docs/providers/qwencloud",
"endpoints": {
"chat_completions": true,
"messages": true,
"responses": true,
"embeddings": true,
"image_generations": true,
"audio_transcriptions": false,
"audio_speech": false,
"moderations": false,
"batches": false,
"rerank": true,
"a2a": true,
"interactions": true
}
},
"qwen_ai_platform": {
"display_name": "Qwen AI Platform (`qwen_ai_platform`)",
"url": "https://docs.litellm.ai/docs/providers/qwencloud",
"endpoints": {
"chat_completions": true,
"messages": true,
"responses": true,
"embeddings": true,
"image_generations": true,
"audio_transcriptions": false,
"audio_speech": false,
"moderations": false,
"batches": false,
"rerank": true,
"a2a": true,
"interactions": true
}
},
"databricks": {
"display_name": "Databricks (`databricks`)",
"url": "https://docs.litellm.ai/docs/providers/databricks",

View file

@ -986,6 +986,62 @@
],
"default_model_placeholder": "gpt-3.5-turbo"
},
{
"provider": "QwenCloud",
"provider_display_name": "QwenCloud",
"litellm_provider": "qwencloud",
"credential_fields": [
{
"key": "api_key",
"label": "QwenCloud API Key",
"placeholder": null,
"tooltip": null,
"required": true,
"field_type": "password",
"options": null,
"default_value": null
},
{
"key": "api_base",
"label": "API Base",
"placeholder": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
"tooltip": "The base URL for QwenCloud. Defaults to https://dashscope-intl.aliyuncs.com/compatible-mode/v1 if not specified.",
"required": true,
"field_type": "text",
"options": null,
"default_value": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
}
],
"default_model_placeholder": "gpt-3.5-turbo"
},
{
"provider": "Qwen_AI_Platform",
"provider_display_name": "Qwen AI Platform",
"litellm_provider": "qwen_ai_platform",
"credential_fields": [
{
"key": "api_key",
"label": "Qwen AI Platform API Key",
"placeholder": null,
"tooltip": null,
"required": true,
"field_type": "password",
"options": null,
"default_value": null
},
{
"key": "api_base",
"label": "API Base",
"placeholder": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"tooltip": "The base URL for Qwen AI Platform. Defaults to https://dashscope.aliyuncs.com/compatible-mode/v1 if not specified.",
"required": true,
"field_type": "text",
"options": null,
"default_value": "https://dashscope.aliyuncs.com/compatible-mode/v1"
}
],
"default_model_placeholder": "gpt-3.5-turbo"
},
{
"provider": "Databricks",
"provider_display_name": "Databricks",

View file

@ -3771,6 +3771,8 @@ class LlmProviders(str, Enum):
CODESTRAL = "codestral"
TEXT_COMPLETION_CODESTRAL = "text-completion-codestral"
DASHSCOPE = "dashscope"
QWENCLOUD = "qwencloud"
QWEN_AI_PLATFORM = "qwen_ai_platform"
MODELSCOPE = "modelscope"
MOONSHOT = "moonshot"
PUBLICAI = "publicai"

View file

@ -6586,11 +6586,11 @@ def validate_environment(
keys_in_environment = True
else:
missing_keys.append("WANDB_API_KEY")
elif custom_llm_provider == "dashscope":
if "DASHSCOPE_API_KEY" in os.environ:
elif custom_llm_provider in ("dashscope", "qwencloud", "qwen_ai_platform"):
if f"{custom_llm_provider.upper()}_API_KEY" in os.environ or "DASHSCOPE_API_KEY" in os.environ:
keys_in_environment = True
else:
missing_keys.append("DASHSCOPE_API_KEY")
missing_keys.append(f"{custom_llm_provider.upper()}_API_KEY")
elif custom_llm_provider == "modelscope":
if "MODELSCOPE_API_KEY" in os.environ:
keys_in_environment = True
@ -8152,6 +8152,11 @@ class ProviderConfigManager:
LlmProviders.NEBIUS: (lambda: litellm.NebiusConfig(), False),
LlmProviders.WANDB: (lambda: litellm.WandbConfig(), False),
LlmProviders.DASHSCOPE: (lambda: litellm.DashScopeChatConfig(), False),
LlmProviders.QWENCLOUD: (lambda: litellm.QwenCloudChatConfig(), False),
LlmProviders.QWEN_AI_PLATFORM: (
lambda: litellm.QwenAIPlatformChatConfig(),
False,
),
LlmProviders.MODELSCOPE: (lambda: litellm.ModelScopeChatConfig(), False),
LlmProviders.MOONSHOT: (lambda: litellm.MoonshotChatConfig(), False),
LlmProviders.DOCKER_MODEL_RUNNER: (
@ -8366,12 +8371,16 @@ class ProviderConfigManager:
)
return VolcEngineEmbeddingConfig()
elif litellm.LlmProviders.DASHSCOPE == provider:
from litellm.llms.dashscope.embed.transformation import (
DashScopeEmbeddingConfig,
elif provider in (
litellm.LlmProviders.DASHSCOPE,
litellm.LlmProviders.QWENCLOUD,
litellm.LlmProviders.QWEN_AI_PLATFORM,
):
from litellm.llms.dashscope.common_utils import (
get_dashscope_family_embedding_config,
)
return DashScopeEmbeddingConfig()
return get_dashscope_family_embedding_config(provider.value)
elif litellm.LlmProviders.OVHCLOUD == provider:
return litellm.OVHCloudEmbeddingConfig()
elif litellm.LlmProviders.SNOWFLAKE == provider:
@ -8444,12 +8453,16 @@ class ProviderConfigManager:
return litellm.VoyageRerankConfig()
elif litellm.LlmProviders.WATSONX == provider:
return litellm.IBMWatsonXRerankConfig()
elif litellm.LlmProviders.DASHSCOPE == provider:
from litellm.llms.dashscope.rerank.transformation import (
DashScopeRerankConfig,
elif provider in (
litellm.LlmProviders.DASHSCOPE,
litellm.LlmProviders.QWENCLOUD,
litellm.LlmProviders.QWEN_AI_PLATFORM,
):
from litellm.llms.dashscope.common_utils import (
get_dashscope_family_rerank_config,
)
return DashScopeRerankConfig()
return get_dashscope_family_rerank_config(provider.value)
return litellm.CohereRerankConfig()
@staticmethod
@ -9122,12 +9135,16 @@ class ProviderConfigManager:
)
return get_openrouter_image_generation_config(model)
elif LlmProviders.DASHSCOPE == provider:
from litellm.llms.dashscope.image_generation import (
get_dashscope_image_generation_config,
elif provider in (
LlmProviders.DASHSCOPE,
LlmProviders.QWENCLOUD,
LlmProviders.QWEN_AI_PLATFORM,
):
from litellm.llms.dashscope.common_utils import (
get_dashscope_family_image_generation_config,
)
return get_dashscope_image_generation_config(model)
return get_dashscope_family_image_generation_config(provider.value)
elif LlmProviders.MODELSCOPE == provider:
from litellm.llms.modelscope.image_generation import (
get_modelscope_image_generation_config,

File diff suppressed because it is too large Load diff

View file

@ -724,6 +724,42 @@
"interactions": true
}
},
"qwencloud": {
"display_name": "QwenCloud (`qwencloud`)",
"url": "https://docs.litellm.ai/docs/providers/qwencloud",
"endpoints": {
"chat_completions": true,
"messages": true,
"responses": true,
"embeddings": true,
"image_generations": true,
"audio_transcriptions": false,
"audio_speech": false,
"moderations": false,
"batches": false,
"rerank": true,
"a2a": true,
"interactions": true
}
},
"qwen_ai_platform": {
"display_name": "Qwen AI Platform (`qwen_ai_platform`)",
"url": "https://docs.litellm.ai/docs/providers/qwencloud",
"endpoints": {
"chat_completions": true,
"messages": true,
"responses": true,
"embeddings": true,
"image_generations": true,
"audio_transcriptions": false,
"audio_speech": false,
"moderations": false,
"batches": false,
"rerank": true,
"a2a": true,
"interactions": true
}
},
"databricks": {
"display_name": "Databricks (`databricks`)",
"url": "https://docs.litellm.ai/docs/providers/databricks",

View file

@ -0,0 +1,331 @@
import math
import pytest
import litellm
from litellm import completion, get_llm_provider
from litellm.llms.dashscope.chat.transformation import DashScopeChatConfig
from litellm.llms.dashscope.cost_calculator import (
cost_per_token as dashscope_cost_per_token,
)
from litellm.llms.dashscope.embed.transformation import DashScopeEmbeddingConfig
from litellm.llms.dashscope.image_generation.transformation import (
DashScopeImageGenerationConfig,
)
from litellm.llms.dashscope.qwen_ai_platform import (
QWEN_AI_PLATFORM_API_BASE,
QWEN_AI_PLATFORM_IMAGE_API_BASE,
QWEN_AI_PLATFORM_RERANK_API_BASE,
QwenAIPlatformChatConfig,
QwenAIPlatformEmbeddingConfig,
QwenAIPlatformImageGenerationConfig,
QwenAIPlatformRerankConfig,
)
from litellm.llms.dashscope.qwencloud import (
QWENCLOUD_API_BASE,
QWENCLOUD_IMAGE_API_BASE,
QWENCLOUD_RERANK_API_BASE,
QwenCloudChatConfig,
QwenCloudEmbeddingConfig,
QwenCloudImageGenerationConfig,
QwenCloudRerankConfig,
)
from litellm.llms.dashscope.rerank.transformation import DashScopeRerankConfig
from litellm.types.utils import LlmProviders, Usage
from litellm.utils import ProviderConfigManager
DASHSCOPE_FAMILY_ENV_VARS = [
"DASHSCOPE_API_KEY",
"DASHSCOPE_API_BASE",
"DASHSCOPE_API_BASE_RERANK",
"DASHSCOPE_API_BASE_IMAGE",
"QWENCLOUD_API_KEY",
"QWENCLOUD_API_BASE",
"QWENCLOUD_API_BASE_RERANK",
"QWENCLOUD_API_BASE_IMAGE",
"QWEN_AI_PLATFORM_API_KEY",
"QWEN_AI_PLATFORM_API_BASE",
"QWEN_AI_PLATFORM_API_BASE_RERANK",
"QWEN_AI_PLATFORM_API_BASE_IMAGE",
]
BRAND_CASES = [
pytest.param(
{
"provider": "qwencloud",
"enum": LlmProviders.QWENCLOUD,
"key_env": "QWENCLOUD_API_KEY",
"base_env": "QWENCLOUD_API_BASE",
"default_base": QWENCLOUD_API_BASE,
"default_rerank_base": QWENCLOUD_RERANK_API_BASE,
"default_image_base": QWENCLOUD_IMAGE_API_BASE,
"chat_config": QwenCloudChatConfig,
"embedding_config": QwenCloudEmbeddingConfig,
"rerank_config": QwenCloudRerankConfig,
"image_config": QwenCloudImageGenerationConfig,
},
id="qwencloud",
),
pytest.param(
{
"provider": "qwen_ai_platform",
"enum": LlmProviders.QWEN_AI_PLATFORM,
"key_env": "QWEN_AI_PLATFORM_API_KEY",
"base_env": "QWEN_AI_PLATFORM_API_BASE",
"default_base": QWEN_AI_PLATFORM_API_BASE,
"default_rerank_base": QWEN_AI_PLATFORM_RERANK_API_BASE,
"default_image_base": QWEN_AI_PLATFORM_IMAGE_API_BASE,
"chat_config": QwenAIPlatformChatConfig,
"embedding_config": QwenAIPlatformEmbeddingConfig,
"rerank_config": QwenAIPlatformRerankConfig,
"image_config": QwenAIPlatformImageGenerationConfig,
},
id="qwen_ai_platform",
),
]
@pytest.fixture(autouse=True)
def clear_dashscope_family_env(monkeypatch):
for env_var in DASHSCOPE_FAMILY_ENV_VARS:
monkeypatch.delenv(env_var, raising=False)
class TestQwenBrandProviderResolution:
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_get_llm_provider_resolves_brand_default_base(self, brand):
model, provider, api_key, api_base = get_llm_provider(f"{brand['provider']}/qwen-max", api_key="sk-explicit")
assert model == "qwen-max"
assert provider == brand["provider"]
assert api_key == "sk-explicit"
assert api_base == brand["default_base"]
def test_dashscope_resolution_unchanged(self):
model, provider, api_key, api_base = get_llm_provider("dashscope/qwen-max", api_key="sk-explicit")
assert model == "qwen-max"
assert provider == "dashscope"
assert api_base == "https://dashscope.aliyuncs.com/compatible-mode/v1"
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_brand_env_key_wins_over_dashscope_key(self, monkeypatch, brand):
monkeypatch.setenv(brand["key_env"], "sk-brand")
monkeypatch.setenv("DASHSCOPE_API_KEY", "sk-dashscope")
_, _, api_key, _ = get_llm_provider(f"{brand['provider']}/qwen-max")
assert api_key == "sk-brand"
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_dashscope_key_is_fallback(self, monkeypatch, brand):
monkeypatch.setenv("DASHSCOPE_API_KEY", "sk-dashscope")
_, _, api_key, _ = get_llm_provider(f"{brand['provider']}/qwen-max")
assert api_key == "sk-dashscope"
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_dashscope_api_base_does_not_leak_into_brand(self, monkeypatch, brand):
monkeypatch.setenv("DASHSCOPE_API_BASE", "https://legacy.example.com/v1")
_, _, _, api_base = get_llm_provider(f"{brand['provider']}/qwen-max", api_key="sk-explicit")
assert api_base == brand["default_base"]
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_brand_api_base_env_wins(self, monkeypatch, brand):
monkeypatch.setenv(brand["base_env"], "https://brand.example.com/v1")
_, _, _, api_base = get_llm_provider(f"{brand['provider']}/qwen-max", api_key="sk-explicit")
assert api_base == "https://brand.example.com/v1"
class TestQwenBrandConfigDispatch:
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_chat_config(self, brand):
config = ProviderConfigManager.get_provider_chat_config("qwen-max", brand["enum"])
assert isinstance(config, brand["chat_config"])
assert isinstance(config, DashScopeChatConfig)
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_embedding_config(self, brand):
config = ProviderConfigManager.get_provider_embedding_config(model="text-embedding-v3", provider=brand["enum"])
assert isinstance(config, brand["embedding_config"])
assert isinstance(config, DashScopeEmbeddingConfig)
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_rerank_config(self, brand):
config = ProviderConfigManager.get_provider_rerank_config(
model="gte-rerank-v2",
provider=brand["enum"],
api_base=None,
present_version_params=[],
)
assert isinstance(config, brand["rerank_config"])
assert isinstance(config, DashScopeRerankConfig)
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_image_generation_config(self, brand):
config = ProviderConfigManager.get_provider_image_generation_config(model="qwen-image", provider=brand["enum"])
assert isinstance(config, brand["image_config"])
assert isinstance(config, DashScopeImageGenerationConfig)
class TestQwenBrandDefaultUrls:
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_chat_complete_url(self, brand):
url = brand["chat_config"]().get_complete_url(
api_base=None,
api_key="sk-test",
model="qwen-max",
optional_params={},
litellm_params={},
)
assert url == f"{brand['default_base']}/chat/completions"
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_embedding_complete_url(self, brand):
url = brand["embedding_config"]().get_complete_url(
api_base=None,
api_key="sk-test",
model="text-embedding-v3",
optional_params={},
litellm_params={},
)
assert url == f"{brand['default_base']}/embeddings"
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_embedding_ignores_dashscope_api_base(self, monkeypatch, brand):
monkeypatch.setenv("DASHSCOPE_API_BASE", "https://legacy.example.com/v1")
url = brand["embedding_config"]().get_complete_url(
api_base=None,
api_key="sk-test",
model="text-embedding-v3",
optional_params={},
litellm_params={},
)
assert url == f"{brand['default_base']}/embeddings"
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_rerank_complete_url(self, brand):
url = brand["rerank_config"]().get_complete_url(api_base=None, model="gte-rerank-v2")
assert url == brand["default_rerank_base"]
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_rerank_env_override(self, monkeypatch, brand):
monkeypatch.setenv(f"{brand['base_env']}_RERANK", "https://rerank.example.com/v1/reranks")
url = brand["rerank_config"]().get_complete_url(api_base=None, model="gte-rerank-v2")
assert url == "https://rerank.example.com/v1/reranks"
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_image_generation_complete_url(self, brand):
url = brand["image_config"]().get_complete_url(
api_base=None,
api_key="sk-test",
model="qwen-image",
optional_params={},
litellm_params={},
)
assert url == brand["default_image_base"]
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_image_generation_ignores_chat_compatible_api_base(self, brand):
url = brand["image_config"]().get_complete_url(
api_base=brand["default_base"],
api_key="sk-test",
model="qwen-image",
optional_params={},
litellm_params={},
)
assert url == brand["default_image_base"]
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_validate_environment_requires_key(self, brand):
with pytest.raises(ValueError, match="DASHSCOPE_API_KEY"):
brand["embedding_config"]().validate_environment(
headers={},
model="text-embedding-v3",
messages=[],
optional_params={},
litellm_params={},
api_key=None,
api_base=None,
)
class TestQwenBrandCostParity:
@pytest.fixture(autouse=True)
def setup_model_cost_map(self, monkeypatch):
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_get_model_info(self, brand):
model_info = litellm.get_model_info(f"{brand['provider']}/qwen-max")
dashscope_info = litellm.get_model_info("dashscope/qwen-max")
assert model_info["litellm_provider"] == brand["provider"]
assert model_info["input_cost_per_token"] == dashscope_info["input_cost_per_token"]
assert model_info["output_cost_per_token"] == dashscope_info["output_cost_per_token"]
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_flat_pricing_matches_dashscope(self, brand):
usage = Usage(prompt_tokens=1000, completion_tokens=500)
brand_costs = dashscope_cost_per_token(model="qwen-max", usage=usage, custom_llm_provider=brand["provider"])
dashscope_costs = dashscope_cost_per_token(model="qwen-max", usage=usage)
assert brand_costs == dashscope_costs
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_tiered_pricing_matches_dashscope(self, brand):
usage = Usage(prompt_tokens=300000, completion_tokens=300000)
brand_costs = dashscope_cost_per_token(model="qwen-flash", usage=usage, custom_llm_provider=brand["provider"])
dashscope_costs = dashscope_cost_per_token(model="qwen-flash", usage=usage)
assert brand_costs == dashscope_costs
tier_2 = litellm.get_model_info(f"{brand['provider']}/qwen-flash")["tiered_pricing"][1]
assert math.isclose(brand_costs[0], 300000 * tier_2["input_cost_per_token"], rel_tol=1e-10)
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_public_cost_per_token_routes_to_dashscope_calculator(self, brand):
brand_costs = litellm.cost_per_token(
model=f"{brand['provider']}/qwen-max",
prompt_tokens=1000,
completion_tokens=500,
custom_llm_provider=brand["provider"],
)
dashscope_costs = litellm.cost_per_token(
model="dashscope/qwen-max",
prompt_tokens=1000,
completion_tokens=500,
custom_llm_provider="dashscope",
)
assert brand_costs == dashscope_costs
class TestQwenBrandCompletionMock:
@pytest.mark.respx()
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_completion_hits_brand_default_host(self, respx_mock, brand, monkeypatch):
monkeypatch.setattr(litellm, "disable_aiohttp_transport", True)
respx_mock.post(f"{brand['default_base']}/chat/completions").respond(
json={
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "qwen-turbo",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Hey from LiteLLM!"},
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21,
},
},
status_code=200,
)
response = completion(
model=f"{brand['provider']}/qwen-turbo",
messages=[{"role": "user", "content": "say hey from LiteLLM"}],
api_key="fake-brand-key",
)
assert response.choices[0].message.content == "Hey from LiteLLM!"
request = respx_mock.calls[0].request
assert request.url == f"{brand['default_base']}/chat/completions"
assert request.headers["Authorization"] == "Bearer fake-brand-key"

View file

@ -150,6 +150,8 @@ export enum Providers {
PETALS = "Petals",
PG_VECTOR = "Pg Vector",
PREDIBASE = "Predibase",
Qwen_AI_Platform = "Qwen AI Platform",
QwenCloud = "QwenCloud",
RECRAFT = "Recraft",
REPLICATE = "Replicate",
RunwayML = "RunwayML",
@ -262,6 +264,8 @@ export const provider_map: Record<string, string> = {
PETALS: "petals",
PG_VECTOR: "pg_vector",
PREDIBASE: "predibase",
Qwen_AI_Platform: "qwen_ai_platform",
QwenCloud: "qwencloud",
RECRAFT: "recraft",
REPLICATE: "replicate",
RunwayML: "runwayml",
@ -357,6 +361,8 @@ export const providerLogoMap: Partial<Record<Providers, string>> = {
[Providers.Openrouter]: openrouterLogo.src,
[Providers.Oracle]: oracleLogo.src,
[Providers.Perplexity]: perplexityAiLogo.src,
[Providers.Qwen_AI_Platform]: qwenLogo.src,
[Providers.QwenCloud]: qwenLogo.src,
[Providers.RECRAFT]: recraftLogo.src,
[Providers.REPLICATE]: replicateLogo.src,
[Providers.RunwayML]: runwayLogo.src,