diff --git a/plugins/model-providers/alibaba-coding-plan/__init__.py b/plugins/model-providers/alibaba-coding-plan/__init__.py index 17744aeca5..b420fbbbd9 100644 --- a/plugins/model-providers/alibaba-coding-plan/__init__.py +++ b/plugins/model-providers/alibaba-coding-plan/__init__.py @@ -1,7 +1,14 @@ -"""Alibaba Cloud Coding Plan provider profile. +"""Alibaba Cloud Coding Plan provider profiles. Separate from the standard `alibaba` profile because it hits a different endpoint (coding-intl.dashscope.aliyuncs.com) with a dedicated API key tier. + +Region split, mirroring the base DashScope pair (#73265): + - ``alibaba-coding-plan`` → coding-intl.dashscope.aliyuncs.com (international) + - ``alibaba-coding-plan-cn`` → coding.dashscope.aliyuncs.com (mainland China) + +Profile names match the models.dev catalog keys exactly so model metadata +lines up and ``model.provider: alibaba-coding-plan-cn`` resolves at runtime. """ from providers import register_provider @@ -18,4 +25,16 @@ alibaba_coding_plan = ProviderProfile( auth_type="api_key", ) +alibaba_coding_plan_cn = ProviderProfile( + name="alibaba-coding-plan-cn", + aliases=("alibaba-coding-cn", "dashscope-coding-cn"), + display_name="Alibaba Cloud (Coding Plan, China)", + description="Alibaba Cloud Coding Plan, mainland-China endpoint", + signup_url="https://help.aliyun.com/zh/model-studio/", + env_vars=("ALIBABA_CODING_PLAN_API_KEY", "DASHSCOPE_API_KEY", "ALIBABA_CODING_PLAN_CN_BASE_URL"), + base_url="https://coding.dashscope.aliyuncs.com/v1", + auth_type="api_key", +) + register_provider(alibaba_coding_plan) +register_provider(alibaba_coding_plan_cn) diff --git a/plugins/model-providers/alibaba-token-plan/__init__.py b/plugins/model-providers/alibaba-token-plan/__init__.py new file mode 100644 index 0000000000..da8512b6a2 --- /dev/null +++ b/plugins/model-providers/alibaba-token-plan/__init__.py @@ -0,0 +1,42 @@ +"""Alibaba Cloud Token Plan (Model Studio flat-token tier) provider profiles. + +Token Plan is a separate purchase tier from both standard DashScope and the +Coding Plan, with its own dedicated endpoints and API key +(``ALIBABA_TOKEN_PLAN_API_KEY``). Both regional endpoints speak the +OpenAI-compatible chat-completions protocol: + + - ``alibaba-token-plan`` → token-plan.ap-southeast-1.maas.aliyuncs.com (international) + - ``alibaba-token-plan-cn`` → token-plan.cn-beijing.maas.aliyuncs.com (mainland China) + +Profile names match the models.dev catalog keys exactly so model metadata +lines up and ``model.provider: alibaba-token-plan-cn`` resolves at runtime +instead of failing with "Unknown provider" (#73265). +""" + +from providers import register_provider +from providers.base import ProviderProfile + +alibaba_token_plan = ProviderProfile( + name="alibaba-token-plan", + aliases=("dashscope-token-plan",), + display_name="Alibaba Cloud (Token Plan)", + description="Alibaba Cloud Model Studio Token Plan (flat-token tier)", + signup_url="https://help.aliyun.com/zh/model-studio/", + env_vars=("ALIBABA_TOKEN_PLAN_API_KEY", "ALIBABA_TOKEN_PLAN_BASE_URL"), + base_url="https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1", + auth_type="api_key", +) + +alibaba_token_plan_cn = ProviderProfile( + name="alibaba-token-plan-cn", + aliases=("dashscope-token-plan-cn",), + display_name="Alibaba Cloud (Token Plan, China)", + description="Alibaba Cloud Model Studio Token Plan, mainland-China endpoint", + signup_url="https://help.aliyun.com/zh/model-studio/", + env_vars=("ALIBABA_TOKEN_PLAN_API_KEY", "ALIBABA_TOKEN_PLAN_CN_BASE_URL"), + base_url="https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1", + auth_type="api_key", +) + +register_provider(alibaba_token_plan) +register_provider(alibaba_token_plan_cn) diff --git a/plugins/model-providers/alibaba-token-plan/plugin.yaml b/plugins/model-providers/alibaba-token-plan/plugin.yaml new file mode 100644 index 0000000000..7c9fd95f83 --- /dev/null +++ b/plugins/model-providers/alibaba-token-plan/plugin.yaml @@ -0,0 +1,5 @@ +name: alibaba-token-plan-provider +kind: model-provider +version: 1.0.0 +description: Alibaba Cloud Model Studio Token Plan (international + China endpoints) +author: Nous Research diff --git a/plugins/model-providers/alibaba/__init__.py b/plugins/model-providers/alibaba/__init__.py index 5772bc87e6..74fd171948 100644 --- a/plugins/model-providers/alibaba/__init__.py +++ b/plugins/model-providers/alibaba/__init__.py @@ -1,4 +1,13 @@ -"""Alibaba Cloud DashScope provider profile.""" +"""Alibaba Cloud DashScope provider profiles. + +DashScope has region-split endpoints with the same key type: + - ``alibaba`` → dashscope-intl.aliyuncs.com (international) + - ``alibaba-cn`` → dashscope.aliyuncs.com (mainland China) + +Profile names match the models.dev catalog keys exactly +(``alibaba`` / ``alibaba-cn``) so model metadata lines up and +``model.provider: alibaba-cn`` resolves at runtime (#73265). +""" from providers import register_provider from providers.base import ProviderProfile @@ -10,4 +19,14 @@ alibaba = ProviderProfile( base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1", ) +alibaba_cn = ProviderProfile( + name="alibaba-cn", + aliases=("dashscope-cn", "alibaba-cloud-cn"), + display_name="Alibaba Cloud DashScope (China)", + description="Alibaba Cloud DashScope, mainland-China endpoint", + env_vars=("DASHSCOPE_API_KEY",), + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", +) + register_provider(alibaba) +register_provider(alibaba_cn) diff --git a/tests/providers/test_provider_profiles.py b/tests/providers/test_provider_profiles.py index 16ca380f94..bc9b1f1a3e 100644 --- a/tests/providers/test_provider_profiles.py +++ b/tests/providers/test_provider_profiles.py @@ -262,3 +262,60 @@ class TestQwenProfile: + def test_build_api_kwargs_extras_empty(self): + p = ProviderProfile(name="test") + eb, tl = p.build_api_kwargs_extras() + assert eb == {} + assert tl == {} + + +class TestAlibabaRegionalAndTokenPlanProfiles: + """#73265: the models.dev catalog advertises alibaba-cn / + alibaba-token-plan(-cn) / alibaba-coding-plan-cn, but none were registered + at runtime — `model.provider: alibaba-coding-plan-cn` failed with + "Unknown provider" and users were forced onto the `custom` escape hatch. + Profile names intentionally match the catalog keys exactly so model + metadata lines up.""" + + def test_alibaba_cn_registered(self): + p = get_provider_profile("alibaba-cn") + assert p is not None and p.name == "alibaba-cn" + assert p.base_url == "https://dashscope.aliyuncs.com/compatible-mode/v1" + assert "DASHSCOPE_API_KEY" in p.env_vars + + def test_alibaba_coding_plan_cn_registered(self): + p = get_provider_profile("alibaba-coding-plan-cn") + assert p is not None and p.name == "alibaba-coding-plan-cn" + assert p.base_url == "https://coding.dashscope.aliyuncs.com/v1" + assert "ALIBABA_CODING_PLAN_API_KEY" in p.env_vars + + def test_alibaba_token_plan_registered(self): + p = get_provider_profile("alibaba-token-plan") + assert p is not None and p.name == "alibaba-token-plan" + assert p.base_url == "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1" + assert "ALIBABA_TOKEN_PLAN_API_KEY" in p.env_vars + + def test_alibaba_token_plan_cn_registered(self): + p = get_provider_profile("alibaba-token-plan-cn") + assert p is not None and p.name == "alibaba-token-plan-cn" + assert p.base_url == "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1" + assert "ALIBABA_TOKEN_PLAN_API_KEY" in p.env_vars + + def test_cn_variants_resolve_in_auth_registry(self, monkeypatch): + """The reporter's exact failure site: ``auth.resolve_provider()`` only + consults PROVIDER_REGISTRY (auto-extended from provider profiles, + hermes_cli/auth.py:461-490) and raised + "Unknown provider 'alibaba-coding-plan-cn'" (hermes_cli/auth.py:1937) + even though the models.dev catalog advertised the id — the + resolve_provider_full() catalog chain covers only the CLI --provider + path, not the credential/runtime path.""" + from hermes_cli.auth import PROVIDER_REGISTRY, resolve_provider + monkeypatch.setenv("DASHSCOPE_API_KEY", "sk-test") + monkeypatch.setenv("ALIBABA_CODING_PLAN_API_KEY", "sk-test") + monkeypatch.setenv("ALIBABA_TOKEN_PLAN_API_KEY", "sk-test") + for pid in ("alibaba-cn", "alibaba-coding-plan-cn", + "alibaba-token-plan", "alibaba-token-plan-cn"): + assert pid in PROVIDER_REGISTRY, f"{pid} missing from PROVIDER_REGISTRY" + assert resolve_provider(pid) == pid + assert (PROVIDER_REGISTRY["alibaba-token-plan-cn"].inference_base_url + == "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1")