docs: add Alibaba Cloud and DingTalk to setup wizard and docs (#1687)
* feat(gateway): add DingTalk platform adapter
Add DingTalk as a messaging platform using the dingtalk-stream SDK
for real-time message reception via Stream Mode (no webhook needed).
Replies are sent via session webhook using markdown format.
Features:
- Stream Mode connection (long-lived WebSocket, no public URL needed)
- Text and rich text message support
- DM and group chat support
- Message deduplication with 5-minute window
- Auto-reconnection with exponential backoff
- Session webhook caching for reply routing
Configuration:
export DINGTALK_CLIENT_ID=your-app-key
export DINGTALK_CLIENT_SECRET=your-app-secret
# or in config.yaml:
platforms:
dingtalk:
enabled: true
extra:
client_id: your-app-key
client_secret: your-app-secret
Files:
- gateway/platforms/dingtalk.py (340 lines) — adapter implementation
- gateway/config.py — add DINGTALK to Platform enum
- gateway/run.py — add DingTalk to _create_adapter
- hermes_cli/config.py — add env vars to _EXTRA_ENV_KEYS
- hermes_cli/tools_config.py — add dingtalk to PLATFORMS
- tests/gateway/test_dingtalk.py — 21 tests
* docs: add Alibaba Cloud and DingTalk to setup wizard and docs
Wire Alibaba Cloud (DashScope) into hermes setup and hermes model
provider selection flows. Add DingTalk env vars to documentation.
Changes:
- setup.py: Add Alibaba Cloud as provider choice (index 11) with
DASHSCOPE_API_KEY prompt and model studio link
- main.py: Add alibaba to provider_labels, providers list, and
model flow dispatch
- environment-variables.md: Add DASHSCOPE_API_KEY, DINGTALK_CLIENT_ID,
DINGTALK_CLIENT_SECRET, and alibaba to HERMES_INFERENCE_PROVIDER
This commit is contained in:
@@ -784,6 +784,7 @@ def cmd_model(args):
|
||||
"opencode-go": "OpenCode Go",
|
||||
"ai-gateway": "AI Gateway",
|
||||
"kilocode": "Kilo Code",
|
||||
"alibaba": "Alibaba Cloud (DashScope)",
|
||||
"custom": "Custom endpoint",
|
||||
}
|
||||
active_label = provider_labels.get(active, active)
|
||||
@@ -807,6 +808,7 @@ def cmd_model(args):
|
||||
("opencode-zen", "OpenCode Zen (35+ curated models, pay-as-you-go)"),
|
||||
("opencode-go", "OpenCode Go (open models, $10/month subscription)"),
|
||||
("ai-gateway", "AI Gateway (Vercel — 200+ models, pay-per-use)"),
|
||||
("alibaba", "Alibaba Cloud / DashScope (Qwen models, Anthropic-compatible)"),
|
||||
]
|
||||
|
||||
# Add user-defined custom providers from config.yaml
|
||||
@@ -875,7 +877,7 @@ def cmd_model(args):
|
||||
_model_flow_anthropic(config, current_model)
|
||||
elif selected_provider == "kimi-coding":
|
||||
_model_flow_kimi(config, current_model)
|
||||
elif selected_provider in ("zai", "minimax", "minimax-cn", "kilocode", "opencode-zen", "opencode-go", "ai-gateway"):
|
||||
elif selected_provider in ("zai", "minimax", "minimax-cn", "kilocode", "opencode-zen", "opencode-go", "ai-gateway", "alibaba"):
|
||||
_model_flow_api_key_provider(config, selected_provider, current_model)
|
||||
|
||||
|
||||
|
||||
@@ -738,6 +738,7 @@ def setup_model_provider(config: dict):
|
||||
"Kilo Code (Kilo Gateway API)",
|
||||
"Anthropic (Claude models — API key or Claude Code subscription)",
|
||||
"AI Gateway (Vercel — 200+ models, pay-per-use)",
|
||||
"Alibaba Cloud / DashScope (Qwen models via Anthropic-compatible API)",
|
||||
"OpenCode Zen (35+ curated models, pay-as-you-go)",
|
||||
"OpenCode Go (open models, $10/month subscription)",
|
||||
]
|
||||
@@ -1313,7 +1314,39 @@ def setup_model_provider(config: dict):
|
||||
_update_config_for_provider("ai-gateway", pconfig.inference_base_url, default_model="anthropic/claude-opus-4.6")
|
||||
_set_model_provider(config, "ai-gateway", pconfig.inference_base_url)
|
||||
|
||||
elif provider_idx == 11: # OpenCode Zen
|
||||
elif provider_idx == 11: # Alibaba Cloud / DashScope
|
||||
selected_provider = "alibaba"
|
||||
print()
|
||||
print_header("Alibaba Cloud / DashScope API Key")
|
||||
pconfig = PROVIDER_REGISTRY["alibaba"]
|
||||
print_info(f"Provider: {pconfig.name}")
|
||||
print_info("Get your API key at: https://modelstudio.console.alibabacloud.com/")
|
||||
print()
|
||||
|
||||
existing_key = get_env_value("DASHSCOPE_API_KEY")
|
||||
if existing_key:
|
||||
print_info(f"Current: {existing_key[:8]}... (configured)")
|
||||
if prompt_yes_no("Update API key?", False):
|
||||
new_key = prompt(" DashScope API key", password=True)
|
||||
if new_key:
|
||||
save_env_value("DASHSCOPE_API_KEY", new_key)
|
||||
print_success("DashScope API key updated")
|
||||
else:
|
||||
new_key = prompt(" DashScope API key", password=True)
|
||||
if new_key:
|
||||
save_env_value("DASHSCOPE_API_KEY", new_key)
|
||||
print_success("DashScope API key saved")
|
||||
else:
|
||||
print_warning("Skipped - agent won't work without an API key")
|
||||
|
||||
# Clear custom endpoint vars if switching
|
||||
if existing_custom:
|
||||
save_env_value("OPENAI_BASE_URL", "")
|
||||
save_env_value("OPENAI_API_KEY", "")
|
||||
_update_config_for_provider("alibaba", pconfig.inference_base_url, default_model="qwen3.5-plus")
|
||||
_set_model_provider(config, "alibaba", pconfig.inference_base_url)
|
||||
|
||||
elif provider_idx == 12: # OpenCode Zen
|
||||
selected_provider = "opencode-zen"
|
||||
print()
|
||||
print_header("OpenCode Zen API Key")
|
||||
@@ -1346,7 +1379,7 @@ def setup_model_provider(config: dict):
|
||||
_set_model_provider(config, "opencode-zen", pconfig.inference_base_url)
|
||||
selected_base_url = pconfig.inference_base_url
|
||||
|
||||
elif provider_idx == 12: # OpenCode Go
|
||||
elif provider_idx == 13: # OpenCode Go
|
||||
selected_provider = "opencode-go"
|
||||
print()
|
||||
print_header("OpenCode Go API Key")
|
||||
@@ -1379,7 +1412,7 @@ def setup_model_provider(config: dict):
|
||||
_set_model_provider(config, "opencode-go", pconfig.inference_base_url)
|
||||
selected_base_url = pconfig.inference_base_url
|
||||
|
||||
# else: provider_idx == 13 (Keep current) — only shown when a provider already exists
|
||||
# else: provider_idx == 14 (Keep current) — only shown when a provider already exists
|
||||
# Normalize "keep current" to an explicit provider so downstream logic
|
||||
# doesn't fall back to the generic OpenRouter/static-model path.
|
||||
if selected_provider is None:
|
||||
|
||||
Reference in New Issue
Block a user