Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions hermes_cli/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,17 @@ class ProviderConfig:
api_key_env_vars=("ALIBABA_CODING_PLAN_API_KEY", "DASHSCOPE_API_KEY"),
base_url_env_var="ALIBABA_CODING_PLAN_BASE_URL",
),
"alibaba-token-plan": ProviderConfig(
id="alibaba-token-plan",
name="Alibaba Cloud (Token Plan)",
auth_type="api_key",
# Alibaba's Hermes Agent setup guide recommends the Anthropic-compatible
# endpoint for Token Plan. Users can still override this with
# ALIBABA_TOKEN_PLAN_BASE_URL if they need the OpenAI-compatible URL.
inference_base_url="https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic",
api_key_env_vars=("ALIBABA_TOKEN_PLAN_API_KEY",),
base_url_env_var="ALIBABA_TOKEN_PLAN_BASE_URL",
),
"minimax-cn": ProviderConfig(
id="minimax-cn",
name="MiniMax (China)",
Expand Down Expand Up @@ -1428,6 +1439,8 @@ def resolve_provider(
"minimax-portal": "minimax-oauth", "minimax-global": "minimax-oauth", "minimax_oauth": "minimax-oauth",
"alibaba_coding": "alibaba-coding-plan", "alibaba-coding": "alibaba-coding-plan",
"alibaba_coding_plan": "alibaba-coding-plan",
"alibaba_token": "alibaba-token-plan", "alibaba-token": "alibaba-token-plan",
"alibaba_token_plan": "alibaba-token-plan",
"claude": "anthropic", "claude-code": "anthropic",
"github": "copilot", "github-copilot": "copilot",
"github-models": "copilot", "github-model": "copilot",
Expand Down
20 changes: 20 additions & 0 deletions hermes_cli/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,22 @@ def _xai_curated_models() -> list[str]:
"glm-4.7",
"MiniMax-M2.5",
],
# Alibaba Token Plan (Team Edition) — broader model selection including
# DeepSeek, latest Qwen, and third-party providers. Singapore region only.
# https://modelstudio.console.alibabacloud.com/ap-southeast-1/?tab=doc#/doc/?type=model&url=3028858
"alibaba-token-plan": [
"qwen3.7-max",
"qwen3.6-plus",
"qwen3.6-flash",
"deepseek-v4-pro",
"deepseek-v4-flash",
"deepseek-v3.2",
"kimi-k2.6",
"kimi-k2.5",
"glm-5.1",
"glm-5",
"MiniMax-M2.5",
],
# Curated HF model list — only agentic models that map to OpenRouter defaults.
"huggingface": [
"moonshotai/Kimi-K2.5",
Expand Down Expand Up @@ -914,6 +930,7 @@ class ProviderEntry(NamedTuple):
ProviderEntry("openai-codex", "OpenAI Codex", "OpenAI Codex"),
ProviderEntry("openai-api", "OpenAI API", "OpenAI API (api.openai.com, API key)"),
ProviderEntry("alibaba", "Qwen Cloud", "Qwen Cloud / DashScope Coding (Qwen + multi-provider)"),
ProviderEntry("alibaba-token-plan", "Alibaba Token Plan", "Alibaba Cloud Token Plan (Team Edition — Singapore, broad model catalog)"),
ProviderEntry("xai-oauth", "xAI Grok OAuth (SuperGrok / Premium+)", "xAI Grok OAuth (SuperGrok / Premium+)"),
ProviderEntry("xiaomi", "Xiaomi MiMo", "Xiaomi MiMo (MiMo-V2.5 and V2 models — pro, omni, flash)"),
ProviderEntry("tencent-tokenhub", "Tencent TokenHub", "Tencent TokenHub (Hy3 Preview — direct API via tokenhub.tencentmaas.com)"),
Expand Down Expand Up @@ -1010,6 +1027,9 @@ class ProviderEntry(NamedTuple):
"aliyun": "alibaba",
"qwen": "alibaba",
"alibaba-cloud": "alibaba",
"alibaba_token": "alibaba-token-plan",
"alibaba-token": "alibaba-token-plan",
"alibaba_token_plan": "alibaba-token-plan",
"qwen-portal": "qwen-oauth",
"gemini-cli": "google-gemini-cli",
"gemini-oauth": "google-gemini-cli",
Expand Down
14 changes: 14 additions & 0 deletions hermes_cli/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,16 @@ class HermesOverlay:
transport="openai_chat",
base_url_env_var="ALIBABA_CODING_PLAN_BASE_URL",
),
"alibaba-token-plan": HermesOverlay(
transport="anthropic_messages",
extra_env_vars=("ALIBABA_TOKEN_PLAN_API_KEY",),
base_url_override="https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic",
base_url_env_var="ALIBABA_TOKEN_PLAN_BASE_URL",
),
"vercel": HermesOverlay(
transport="openai_chat",
is_aggregator=True,
),
"opencode": HermesOverlay(
transport="openai_chat",
is_aggregator=True,
Expand Down Expand Up @@ -310,6 +320,9 @@ class ProviderDef:
"alibaba_coding": "alibaba-coding-plan",
"alibaba-coding": "alibaba-coding-plan",
"alibaba_coding_plan": "alibaba-coding-plan",
"alibaba_token": "alibaba-token-plan",
"alibaba-token": "alibaba-token-plan",
"alibaba_token_plan": "alibaba-token-plan",

# google-gemini-cli (OAuth + Code Assist)
"gemini-cli": "google-gemini-cli",
Expand Down Expand Up @@ -376,6 +389,7 @@ class ProviderDef:
"lmstudio": "LM Studio",
"local": "Local endpoint",
"bedrock": "AWS Bedrock",
"alibaba-token-plan": "Alibaba Token Plan",
"ollama-cloud": "Ollama Cloud",
"xai-oauth": "xAI Grok OAuth (SuperGrok / Premium+)",
}
Expand Down
45 changes: 45 additions & 0 deletions tests/hermes_cli/test_alibaba_token_plan_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Regression tests for Alibaba Token Plan provider wiring."""

from hermes_cli.auth import PROVIDER_REGISTRY, resolve_provider
from hermes_cli.providers import HERMES_OVERLAYS, TRANSPORT_TO_API_MODE, get_label, get_provider
from hermes_cli.runtime_provider import _detect_api_mode_for_url


TOKEN_PLAN_ANTHROPIC_URL = "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic"


def test_auth_registry_uses_token_plan_anthropic_endpoint():
cfg = PROVIDER_REGISTRY["alibaba-token-plan"]

assert cfg.inference_base_url == TOKEN_PLAN_ANTHROPIC_URL
assert cfg.api_key_env_vars == ("ALIBABA_TOKEN_PLAN_API_KEY",)
assert cfg.base_url_env_var == "ALIBABA_TOKEN_PLAN_BASE_URL"


def test_provider_overlay_uses_anthropic_messages_transport():
overlay = HERMES_OVERLAYS["alibaba-token-plan"]

assert overlay.transport == "anthropic_messages"
assert TRANSPORT_TO_API_MODE[overlay.transport] == "anthropic_messages"
assert overlay.base_url_override == TOKEN_PLAN_ANTHROPIC_URL
assert overlay.extra_env_vars == ("ALIBABA_TOKEN_PLAN_API_KEY",)


def test_provider_definition_exposes_base_url_key_and_label():
provider = get_provider("alibaba_token_plan")

assert provider is not None
assert provider.id == "alibaba-token-plan"
assert provider.base_url == TOKEN_PLAN_ANTHROPIC_URL
assert provider.api_key_env_vars == ("ALIBABA_TOKEN_PLAN_API_KEY",)
assert get_label("alibaba_token") == "Alibaba Token Plan"


def test_token_plan_aliases_resolve_to_canonical_provider():
assert resolve_provider("alibaba_token") == "alibaba-token-plan"
assert resolve_provider("alibaba-token") == "alibaba-token-plan"
assert resolve_provider("alibaba_token_plan") == "alibaba-token-plan"


def test_token_plan_anthropic_url_auto_detects_api_mode():
assert _detect_api_mode_for_url(TOKEN_PLAN_ANTHROPIC_URL) == "anthropic_messages"
12 changes: 12 additions & 0 deletions tests/hermes_cli/test_model_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ def test_stepfun_alias_resolved(self):
assert provider == "stepfun"
assert model == "step-3.5-flash"

def test_alibaba_token_plan_alias_resolved(self):
provider, model = parse_model_input("alibaba-token:qwen3.7-max", "openrouter")
assert provider == "alibaba-token-plan"
assert model == "qwen3.7-max"
assert provider_label(provider) == "Alibaba Token Plan"

def test_no_slash_no_colon_keeps_provider(self):
provider, model = parse_model_input("gpt-5.4", "openrouter")
assert provider == "openrouter"
Expand Down Expand Up @@ -606,6 +612,12 @@ def test_zai_known_model_accepted_via_catalog_when_api_down(self):
assert result["persist"] is True
assert result["recognized"] is True

def test_alibaba_token_plan_known_models_accepted_via_catalog_when_api_down(self):
result = _validate("deepseek-v4-pro", provider="alibaba-token-plan", api_models=None)
assert result["accepted"] is True
assert result["persist"] is True
assert result["recognized"] is True

def test_unknown_provider_soft_accepted_when_api_down(self):
# No catalog for unknown providers — soft-accept with a Note.
with patch("hermes_cli.models.provider_model_ids", return_value=[]):
Expand Down