From 7042a748f5773f962d508721639fcdceb5c81bd8 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 17 Mar 2026 02:49:22 -0700 Subject: [PATCH] feat: add Alibaba Cloud provider and Anthropic base_url override (#1673) Add Alibaba Cloud (DashScope) as a first-class inference provider using the Anthropic-compatible endpoint. This gives access to Qwen models (qwen3.5-plus, qwen3-max, qwen3-coder-plus, etc.) through the same api_mode as native Anthropic. Also add ANTHROPIC_BASE_URL env var support so users can point the Anthropic provider at any compatible endpoint. Changes: - auth.py: Add alibaba ProviderConfig + ANTHROPIC_BASE_URL on anthropic - models.py: Add alibaba to catalog, labels, aliases (dashscope/aliyun/qwen), provider order - runtime_provider.py: Add alibaba resolution (anthropic_messages api_mode) + ANTHROPIC_BASE_URL - model_metadata.py: Add Qwen model context lengths (128K) - config.py: Add DASHSCOPE_API_KEY, DASHSCOPE_BASE_URL, ANTHROPIC_BASE_URL env vars Usage: hermes --provider alibaba --model qwen3.5-plus # or via aliases: hermes --provider qwen --model qwen3-max --- agent/model_metadata.py | 8 ++++++++ hermes_cli/auth.py | 9 +++++++++ hermes_cli/config.py | 23 +++++++++++++++++++++++ hermes_cli/models.py | 16 +++++++++++++++- hermes_cli/runtime_provider.py | 17 ++++++++++++++++- 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/agent/model_metadata.py b/agent/model_metadata.py index ae7abb562..2f9ea666c 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -116,6 +116,14 @@ DEFAULT_CONTEXT_LENGTHS = { "kimi-k2": 262144, "qwen3-coder": 32768, "big-pickle": 128000, + # Alibaba Cloud / DashScope Qwen models + "qwen3.5-plus": 131072, + "qwen3-max": 131072, + "qwen3-coder-plus": 131072, + "qwen3-coder-next": 131072, + "qwen-plus-latest": 131072, + "qwen3.5-flash": 131072, + "qwen-vl-max": 32768, } diff --git a/hermes_cli/auth.py b/hermes_cli/auth.py index d30dc5b34..f1341d5d6 100644 --- a/hermes_cli/auth.py +++ b/hermes_cli/auth.py @@ -138,6 +138,15 @@ PROVIDER_REGISTRY: Dict[str, ProviderConfig] = { auth_type="api_key", inference_base_url="https://api.anthropic.com", api_key_env_vars=("ANTHROPIC_API_KEY", "ANTHROPIC_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN"), + base_url_env_var="ANTHROPIC_BASE_URL", + ), + "alibaba": ProviderConfig( + id="alibaba", + name="Alibaba Cloud (DashScope)", + auth_type="api_key", + inference_base_url="https://dashscope-intl.aliyuncs.com/apps/anthropic", + api_key_env_vars=("DASHSCOPE_API_KEY",), + base_url_env_var="DASHSCOPE_BASE_URL", ), "minimax-cn": ProviderConfig( id="minimax-cn", diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 85054350f..ae0d9cb75 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -492,6 +492,29 @@ OPTIONAL_ENV_VARS = { "password": False, "category": "provider", }, + "ANTHROPIC_BASE_URL": { + "description": "Custom Anthropic-compatible API base URL (e.g. Alibaba Cloud DashScope)", + "prompt": "Anthropic Base URL", + "url": "", + "password": False, + "category": "provider", + "advanced": True, + }, + "DASHSCOPE_API_KEY": { + "description": "Alibaba Cloud DashScope API key for Qwen models", + "prompt": "DashScope API Key", + "url": "https://modelstudio.console.alibabacloud.com/", + "password": True, + "category": "provider", + }, + "DASHSCOPE_BASE_URL": { + "description": "Custom DashScope base URL (default: international endpoint)", + "prompt": "DashScope Base URL", + "url": "", + "password": False, + "category": "provider", + "advanced": True, + }, "OPENCODE_ZEN_API_KEY": { "description": "OpenCode Zen API key (pay-as-you-go access to curated models)", "prompt": "OpenCode Zen API key", diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 5701641e0..25c9eea54 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -146,6 +146,15 @@ _PROVIDER_MODELS: dict[str, list[str]] = { "google/gemini-3-pro-preview", "google/gemini-3-flash-preview", ], + "alibaba": [ + "qwen3.5-plus", + "qwen3-max", + "qwen3-coder-plus", + "qwen3-coder-next", + "qwen-plus-latest", + "qwen3.5-flash", + "qwen-vl-max", + ], } _PROVIDER_LABELS = { @@ -162,6 +171,7 @@ _PROVIDER_LABELS = { "opencode-go": "OpenCode Go", "ai-gateway": "AI Gateway", "kilocode": "Kilo Code", + "alibaba": "Alibaba Cloud (DashScope)", "custom": "Custom endpoint", } @@ -187,6 +197,10 @@ _PROVIDER_ALIASES = { "kilo": "kilocode", "kilo-code": "kilocode", "kilo-gateway": "kilocode", + "dashscope": "alibaba", + "aliyun": "alibaba", + "qwen": "alibaba", + "alibaba-cloud": "alibaba", } @@ -220,7 +234,7 @@ def list_available_providers() -> list[dict[str, str]]: # Canonical providers in display order _PROVIDER_ORDER = [ "openrouter", "nous", "openai-codex", - "zai", "kimi-coding", "minimax", "minimax-cn", "kilocode", "anthropic", + "zai", "kimi-coding", "minimax", "minimax-cn", "kilocode", "anthropic", "alibaba", "opencode-zen", "opencode-go", "ai-gateway", "deepseek", "custom", ] diff --git a/hermes_cli/runtime_provider.py b/hermes_cli/runtime_provider.py index 148e30bfb..db96edccd 100644 --- a/hermes_cli/runtime_provider.py +++ b/hermes_cli/runtime_provider.py @@ -276,15 +276,30 @@ def resolve_runtime_provider( "No Anthropic credentials found. Set ANTHROPIC_TOKEN or ANTHROPIC_API_KEY, " "run 'claude setup-token', or authenticate with 'claude /login'." ) + # Support custom Anthropic-compatible endpoints via ANTHROPIC_BASE_URL + base_url = os.getenv("ANTHROPIC_BASE_URL", "").strip() or "https://api.anthropic.com" return { "provider": "anthropic", "api_mode": "anthropic_messages", - "base_url": "https://api.anthropic.com", + "base_url": base_url, "api_key": token, "source": "env", "requested_provider": requested_provider, } + # Alibaba Cloud / DashScope (Anthropic-compatible endpoint) + if provider == "alibaba": + creds = resolve_api_key_provider_credentials(provider) + base_url = creds.get("base_url", "").rstrip("/") or "https://dashscope-intl.aliyuncs.com/apps/anthropic" + return { + "provider": "alibaba", + "api_mode": "anthropic_messages", + "base_url": base_url, + "api_key": creds.get("api_key", ""), + "source": creds.get("source", "env"), + "requested_provider": requested_provider, + } + # API-key providers (z.ai/GLM, Kimi, MiniMax, MiniMax-CN) pconfig = PROVIDER_REGISTRY.get(provider) if pconfig and pconfig.auth_type == "api_key":