fix(dashscope): keep a full DashScope image endpoint URL as given and type the brand tests

This commit is contained in:
mateo-berri 2026-10-28 18:21:45 -07:00
parent abb6b5f8b3
commit 2a48c69e3e
3 changed files with 49 additions and 3 deletions

View file

@ -25,6 +25,7 @@ Response format:
from collections.abc import Iterable, Mapping
from typing import TYPE_CHECKING, Any, Final
from urllib.parse import urlparse
import httpx
from pydantic import ConfigDict, TypeAdapter
@ -42,6 +43,7 @@ from litellm.types.utils import ImageObject, ImageResponse
from ..common_utils import (
DASHSCOPE_CHAT_COMPATIBLE_PATH,
DASHSCOPE_IMAGE_PATH,
DASHSCOPE_NATIVE_V1_PATH,
resolve_dashscope_endpoints,
)
@ -70,6 +72,10 @@ OPENAI_TO_DASHSCOPE_SIZE: Final[dict] = {
}
def _is_native_endpoint_url(api_base: str) -> bool:
return urlparse(api_base).path.rstrip("/").startswith(f"{DASHSCOPE_NATIVE_V1_PATH}/")
class DashScopeImageGenerationConfig(BaseImageGenerationConfig):
"""
Configuration for DashScope image generation (qwen-image-2.0, qwen-image-2.0-pro,
@ -128,6 +134,8 @@ class DashScopeImageGenerationConfig(BaseImageGenerationConfig):
configured_api_base: Final = api_base or self._configured_chat_api_base()
if configured_api_base and configured_api_base.rstrip("/").endswith(DASHSCOPE_CHAT_COMPATIBLE_PATH):
return image_env_api_base or self._default_image_api_base()
if configured_api_base and _is_native_endpoint_url(configured_api_base):
return configured_api_base
endpoints: Final = resolve_dashscope_endpoints(configured_api_base)
if endpoints is not None:
return (None if api_base else image_env_api_base) or f"{endpoints.origin}{DASHSCOPE_IMAGE_PATH}"

View file

@ -2,9 +2,10 @@ import json
import math
import re
from pathlib import Path
from typing import Final
from typing import Final, Literal
import pytest
from typing_extensions import ReadOnly, TypedDict
import litellm
from litellm import completion, get_llm_provider
@ -54,6 +55,23 @@ DASHSCOPE_FAMILY_ENV_VARS = [
"QWEN_AI_PLATFORM_API_BASE_IMAGE",
]
class BrandCase(TypedDict):
provider: ReadOnly[str]
display_name: ReadOnly[str]
enum: ReadOnly[LlmProviders]
key_env: ReadOnly[str]
base_env: ReadOnly[str]
default_base: ReadOnly[str]
default_rerank_base: ReadOnly[str]
default_image_base: ReadOnly[str]
chat_config: ReadOnly[type[DashScopeChatConfig]]
embedding_config: ReadOnly[type[DashScopeEmbeddingConfig]]
rerank_config: ReadOnly[type[DashScopeRerankConfig]]
image_config: ReadOnly[type[DashScopeImageGenerationConfig]]
BRAND_CASES = [
pytest.param(
{
@ -214,7 +232,9 @@ class TestQwenBrandDefaultUrls:
@pytest.mark.parametrize("brand", BRAND_CASES)
@pytest.mark.parametrize(("config_key", "route"), [("chat_config", "chat/completions"), ("embedding_config", "embeddings")])
def test_openai_routes_derive_from_a_domain_root(self, brand, config_key, route):
def test_openai_routes_derive_from_a_domain_root(
self, brand: BrandCase, config_key: Literal["chat_config", "embedding_config"], route: str
) -> None:
url: Final = brand[config_key]().get_complete_url(
api_base="https://dashscope-intl.aliyuncs.com",
api_key="sk-test",
@ -263,7 +283,7 @@ class TestQwenBrandDefaultUrls:
assert url == brand["default_image_base"]
@pytest.mark.parametrize("brand", BRAND_CASES)
def test_image_generation_uses_its_own_configured_base(self, monkeypatch, brand):
def test_image_generation_uses_its_own_configured_base(self, monkeypatch: pytest.MonkeyPatch, brand: BrandCase) -> None:
monkeypatch.setenv("DASHSCOPE_API_BASE", "https://legacy.example.com/images")
monkeypatch.setenv(brand["base_env"], "https://gateway.example.com/qwen/images")
url: Final = brand["image_config"]().get_complete_url(

View file

@ -97,6 +97,10 @@ class TestDashScopeImageGenerationConfig:
"https://llm-abc123.cn-beijing.maas.aliyuncs.com/apps/anthropic",
"https://llm-abc123.cn-beijing.maas.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
),
(
"https://dashscope-us.aliyuncs.com/api/v1/",
"https://dashscope-us.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
),
],
)
def test_get_complete_url_derives_from_the_domain(
@ -105,6 +109,20 @@ class TestDashScopeImageGenerationConfig:
url: Final = self.cfg.get_complete_url(api_base, None, "qwen-image-3.0", {}, {})
assert url == expected
@pytest.mark.parametrize(
"endpoint_url",
[
"https://dashscope.aliyuncs.com/api/v1/services/aigc/image-generation/generation",
"https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis",
],
)
def test_get_complete_url_keeps_a_full_dashscope_endpoint_url(
self, monkeypatch: pytest.MonkeyPatch, endpoint_url: str
):
monkeypatch.setenv("DASHSCOPE_API_BASE_IMAGE", "https://images.example.com/generate")
url: Final = self.cfg.get_complete_url(endpoint_url, None, "qwen-image-3.0", {}, {})
assert url == endpoint_url
def test_validate_environment_sets_auth_header(self):
headers = self.cfg.validate_environment(
headers={},