mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat(providers): add Arcee AI as direct API provider
Adds Arcee AI as a standard direct provider (ARCEEAI_API_KEY) with Trinity models: trinity-large-thinking, trinity-large-preview, trinity-mini. Standard OpenAI-compatible provider checklist: auth.py, config.py, models.py, main.py, providers.py, doctor.py, model_normalize.py, model_metadata.py, setup.py, trajectory_compressor.py. Based on PR #9274 by arthurbr11, simplified to a standard direct provider without dual-endpoint OpenRouter routing.
This commit is contained in:
parent
78fa758451
commit
0a4cf5b3e1
19 changed files with 283 additions and 7 deletions
|
|
@ -45,6 +45,14 @@
|
|||
# KIMI_BASE_URL=https://api.moonshot.cn/v1 # For Moonshot China keys
|
||||
# KIMI_CN_API_KEY= # Dedicated Moonshot China key
|
||||
|
||||
# =============================================================================
|
||||
# LLM PROVIDER (Arcee AI)
|
||||
# =============================================================================
|
||||
# Arcee AI provides access to Trinity models (trinity-mini, trinity-large-*)
|
||||
# Get an Arcee key at: https://chat.arcee.ai/
|
||||
# ARCEEAI_API_KEY=
|
||||
# ARCEE_BASE_URL= # Override default base URL
|
||||
|
||||
# =============================================================================
|
||||
# LLM PROVIDER (MiniMax)
|
||||
# =============================================================================
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ _PROVIDER_PREFIXES: frozenset[str] = frozenset({
|
|||
"opencode-zen", "opencode-go", "ai-gateway", "kilocode", "alibaba",
|
||||
"qwen-oauth",
|
||||
"xiaomi",
|
||||
"arcee",
|
||||
"custom", "local",
|
||||
# Common aliases
|
||||
"google", "google-gemini", "google-ai-studio",
|
||||
|
|
@ -34,6 +35,7 @@ _PROVIDER_PREFIXES: frozenset[str] = frozenset({
|
|||
"github-models", "kimi", "moonshot", "kimi-cn", "moonshot-cn", "claude", "deep-seek",
|
||||
"opencode", "zen", "go", "vercel", "kilo", "dashscope", "aliyun", "qwen",
|
||||
"mimo", "xiaomi-mimo",
|
||||
"arcee-ai", "arceeai",
|
||||
"qwen-portal",
|
||||
})
|
||||
|
||||
|
|
@ -212,6 +214,7 @@ _URL_TO_PROVIDER: Dict[str, str] = {
|
|||
"api.moonshot.ai": "kimi-coding",
|
||||
"api.moonshot.cn": "kimi-coding-cn",
|
||||
"api.kimi.com": "kimi-coding",
|
||||
"api.arcee.ai": "arcee",
|
||||
"api.minimax": "minimax",
|
||||
"dashscope.aliyuncs.com": "alibaba",
|
||||
"dashscope-intl.aliyuncs.com": "alibaba",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ model:
|
|||
# "minimax-cn" - MiniMax China (requires: MINIMAX_CN_API_KEY)
|
||||
# "huggingface" - Hugging Face Inference (requires: HF_TOKEN)
|
||||
# "xiaomi" - Xiaomi MiMo (requires: XIAOMI_API_KEY)
|
||||
# "arcee" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)
|
||||
# "kilocode" - KiloCode gateway (requires: KILOCODE_API_KEY)
|
||||
# "ai-gateway" - Vercel AI Gateway (requires: AI_GATEWAY_API_KEY)
|
||||
#
|
||||
|
|
|
|||
|
|
@ -167,6 +167,14 @@ PROVIDER_REGISTRY: Dict[str, ProviderConfig] = {
|
|||
inference_base_url="https://api.moonshot.cn/v1",
|
||||
api_key_env_vars=("KIMI_CN_API_KEY",),
|
||||
),
|
||||
"arcee": ProviderConfig(
|
||||
id="arcee",
|
||||
name="Arcee AI",
|
||||
auth_type="api_key",
|
||||
inference_base_url="https://api.arcee.ai/api/v1",
|
||||
api_key_env_vars=("ARCEEAI_API_KEY",),
|
||||
base_url_env_var="ARCEE_BASE_URL",
|
||||
),
|
||||
"minimax": ProviderConfig(
|
||||
id="minimax",
|
||||
name="MiniMax",
|
||||
|
|
@ -900,6 +908,7 @@ def resolve_provider(
|
|||
"google": "gemini", "google-gemini": "gemini", "google-ai-studio": "gemini",
|
||||
"kimi": "kimi-coding", "kimi-for-coding": "kimi-coding", "moonshot": "kimi-coding",
|
||||
"kimi-cn": "kimi-coding-cn", "moonshot-cn": "kimi-coding-cn",
|
||||
"arcee-ai": "arcee", "arceeai": "arcee",
|
||||
"minimax-china": "minimax-cn", "minimax_cn": "minimax-cn",
|
||||
"claude": "anthropic", "claude-code": "anthropic",
|
||||
"github": "copilot", "github-copilot": "copilot",
|
||||
|
|
|
|||
|
|
@ -824,6 +824,22 @@ OPTIONAL_ENV_VARS = {
|
|||
"category": "provider",
|
||||
"advanced": True,
|
||||
},
|
||||
"ARCEEAI_API_KEY": {
|
||||
"description": "Arcee AI API key",
|
||||
"prompt": "Arcee AI API key",
|
||||
"url": "https://chat.arcee.ai/",
|
||||
"password": True,
|
||||
"category": "provider",
|
||||
"advanced": True,
|
||||
},
|
||||
"ARCEE_BASE_URL": {
|
||||
"description": "Arcee AI base URL override",
|
||||
"prompt": "Arcee base URL (leave empty for default)",
|
||||
"url": None,
|
||||
"password": False,
|
||||
"category": "provider",
|
||||
"advanced": True,
|
||||
},
|
||||
"MINIMAX_API_KEY": {
|
||||
"description": "MiniMax API key (international)",
|
||||
"prompt": "MiniMax API key",
|
||||
|
|
|
|||
|
|
@ -722,6 +722,7 @@ def run_doctor(args):
|
|||
("Z.AI / GLM", ("GLM_API_KEY", "ZAI_API_KEY", "Z_AI_API_KEY"), "https://api.z.ai/api/paas/v4/models", "GLM_BASE_URL", True),
|
||||
("Kimi / Moonshot", ("KIMI_API_KEY",), "https://api.moonshot.ai/v1/models", "KIMI_BASE_URL", True),
|
||||
("Kimi / Moonshot (China)", ("KIMI_CN_API_KEY",), "https://api.moonshot.cn/v1/models", None, True),
|
||||
("Arcee AI", ("ARCEEAI_API_KEY",), "https://api.arcee.ai/api/v1/models", "ARCEE_BASE_URL", True),
|
||||
("DeepSeek", ("DEEPSEEK_API_KEY",), "https://api.deepseek.com/v1/models", "DEEPSEEK_BASE_URL", True),
|
||||
("Hugging Face", ("HF_TOKEN",), "https://router.huggingface.co/v1/models", "HF_BASE_URL", True),
|
||||
("Alibaba/DashScope", ("DASHSCOPE_API_KEY",), "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/models", "DASHSCOPE_BASE_URL", True),
|
||||
|
|
|
|||
|
|
@ -1165,7 +1165,7 @@ def select_provider_and_model(args=None):
|
|||
_model_flow_anthropic(config, current_model)
|
||||
elif selected_provider == "kimi-coding":
|
||||
_model_flow_kimi(config, current_model)
|
||||
elif selected_provider in ("gemini", "deepseek", "xai", "zai", "kimi-coding-cn", "minimax", "minimax-cn", "kilocode", "opencode-zen", "opencode-go", "ai-gateway", "alibaba", "huggingface", "xiaomi"):
|
||||
elif selected_provider in ("gemini", "deepseek", "xai", "zai", "kimi-coding-cn", "minimax", "minimax-cn", "kilocode", "opencode-zen", "opencode-go", "ai-gateway", "alibaba", "huggingface", "xiaomi", "arcee"):
|
||||
_model_flow_api_key_provider(config, selected_provider, current_model)
|
||||
|
||||
# ── Post-switch cleanup: clear stale OPENAI_BASE_URL ──────────────
|
||||
|
|
@ -4585,7 +4585,7 @@ For more help on a command:
|
|||
)
|
||||
chat_parser.add_argument(
|
||||
"--provider",
|
||||
choices=["auto", "openrouter", "nous", "openai-codex", "copilot-acp", "copilot", "anthropic", "gemini", "huggingface", "zai", "kimi-coding", "kimi-coding-cn", "minimax", "minimax-cn", "kilocode", "xiaomi"],
|
||||
choices=["auto", "openrouter", "nous", "openai-codex", "copilot-acp", "copilot", "anthropic", "gemini", "huggingface", "zai", "kimi-coding", "kimi-coding-cn", "minimax", "minimax-cn", "kilocode", "xiaomi", "arcee"],
|
||||
default=None,
|
||||
help="Inference provider (default: auto)"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ _VENDOR_PREFIXES: dict[str, str] = {
|
|||
"grok": "x-ai",
|
||||
"qwen": "qwen",
|
||||
"mimo": "xiaomi",
|
||||
"trinity": "arcee-ai",
|
||||
"nemotron": "nvidia",
|
||||
"llama": "meta-llama",
|
||||
"step": "stepfun",
|
||||
|
|
@ -94,6 +95,7 @@ _MATCHING_PREFIX_STRIP_PROVIDERS: frozenset[str] = frozenset({
|
|||
"alibaba",
|
||||
"qwen-oauth",
|
||||
"xiaomi",
|
||||
"arcee",
|
||||
"custom",
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,11 @@ _PROVIDER_MODELS: dict[str, list[str]] = {
|
|||
"mimo-v2-omni",
|
||||
"mimo-v2-flash",
|
||||
],
|
||||
"arcee": [
|
||||
"trinity-large-thinking",
|
||||
"trinity-large-preview",
|
||||
"trinity-mini",
|
||||
],
|
||||
"opencode-zen": [
|
||||
"gpt-5.4-pro",
|
||||
"gpt-5.4",
|
||||
|
|
@ -529,6 +534,7 @@ CANONICAL_PROVIDERS: list[ProviderEntry] = [
|
|||
ProviderEntry("ai-gateway", "AI Gateway", "extended", "AI Gateway (Vercel — 200+ models, pay-per-use)"),
|
||||
ProviderEntry("alibaba", "Alibaba Cloud (DashScope)","extended", "Alibaba Cloud / DashScope Coding (Qwen + multi-provider)"),
|
||||
ProviderEntry("xiaomi", "Xiaomi MiMo", "extended", "Xiaomi MiMo (MiMo-V2 models — pro, omni, flash)"),
|
||||
ProviderEntry("arcee", "Arcee AI", "extended", "Arcee AI (Trinity models — direct API)"),
|
||||
]
|
||||
|
||||
# Derived dicts — used throughout the codebase
|
||||
|
|
@ -553,6 +559,8 @@ _PROVIDER_ALIASES = {
|
|||
"moonshot": "kimi-coding",
|
||||
"kimi-cn": "kimi-coding-cn",
|
||||
"moonshot-cn": "kimi-coding-cn",
|
||||
"arcee-ai": "arcee",
|
||||
"arceeai": "arcee",
|
||||
"minimax-china": "minimax-cn",
|
||||
"minimax_cn": "minimax-cn",
|
||||
"claude": "anthropic",
|
||||
|
|
|
|||
|
|
@ -136,6 +136,11 @@ HERMES_OVERLAYS: Dict[str, HermesOverlay] = {
|
|||
transport="openai_chat",
|
||||
base_url_env_var="XIAOMI_BASE_URL",
|
||||
),
|
||||
"arcee": HermesOverlay(
|
||||
transport="openai_chat",
|
||||
base_url_override="https://api.arcee.ai/api/v1",
|
||||
base_url_env_var="ARCEE_BASE_URL",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -231,6 +236,10 @@ ALIASES: Dict[str, str] = {
|
|||
"mimo": "xiaomi",
|
||||
"xiaomi-mimo": "xiaomi",
|
||||
|
||||
# arcee
|
||||
"arcee-ai": "arcee",
|
||||
"arceeai": "arcee",
|
||||
|
||||
# Local server aliases → virtual "local" concept (resolved via user config)
|
||||
"lmstudio": "lmstudio",
|
||||
"lm-studio": "lmstudio",
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ _DEFAULT_PROVIDER_MODELS = {
|
|||
"zai": ["glm-5.1", "glm-5", "glm-4.7", "glm-4.5", "glm-4.5-flash"],
|
||||
"kimi-coding": ["kimi-k2.5", "kimi-k2-thinking", "kimi-k2-turbo-preview"],
|
||||
"kimi-coding-cn": ["kimi-k2.5", "kimi-k2-thinking", "kimi-k2-turbo-preview"],
|
||||
"arcee": ["trinity-large-thinking", "trinity-large-preview", "trinity-mini"],
|
||||
"minimax": ["MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"],
|
||||
"minimax-cn": ["MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"],
|
||||
"ai-gateway": ["anthropic/claude-opus-4.6", "anthropic/claude-sonnet-4.6", "openai/gpt-5", "google/gemini-3-flash"],
|
||||
|
|
|
|||
207
tests/hermes_cli/test_arcee_provider.py
Normal file
207
tests/hermes_cli/test_arcee_provider.py
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
"""Tests for Arcee AI provider support — standard direct API provider."""
|
||||
|
||||
import sys
|
||||
import types
|
||||
|
||||
import pytest
|
||||
|
||||
if "dotenv" not in sys.modules:
|
||||
fake_dotenv = types.ModuleType("dotenv")
|
||||
fake_dotenv.load_dotenv = lambda *args, **kwargs: None
|
||||
sys.modules["dotenv"] = fake_dotenv
|
||||
|
||||
from hermes_cli.auth import (
|
||||
PROVIDER_REGISTRY,
|
||||
resolve_provider,
|
||||
get_api_key_provider_status,
|
||||
resolve_api_key_provider_credentials,
|
||||
)
|
||||
|
||||
|
||||
_OTHER_PROVIDER_KEYS = (
|
||||
"OPENAI_API_KEY", "ANTHROPIC_API_KEY", "DEEPSEEK_API_KEY",
|
||||
"GOOGLE_API_KEY", "GEMINI_API_KEY", "DASHSCOPE_API_KEY",
|
||||
"XAI_API_KEY", "KIMI_API_KEY", "KIMI_CN_API_KEY",
|
||||
"MINIMAX_API_KEY", "MINIMAX_CN_API_KEY", "AI_GATEWAY_API_KEY",
|
||||
"KILOCODE_API_KEY", "HF_TOKEN", "GLM_API_KEY", "ZAI_API_KEY",
|
||||
"XIAOMI_API_KEY", "COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN",
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Provider Registry
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestArceeProviderRegistry:
|
||||
def test_registered(self):
|
||||
assert "arcee" in PROVIDER_REGISTRY
|
||||
|
||||
def test_name(self):
|
||||
assert PROVIDER_REGISTRY["arcee"].name == "Arcee AI"
|
||||
|
||||
def test_auth_type(self):
|
||||
assert PROVIDER_REGISTRY["arcee"].auth_type == "api_key"
|
||||
|
||||
def test_inference_base_url(self):
|
||||
assert PROVIDER_REGISTRY["arcee"].inference_base_url == "https://api.arcee.ai/api/v1"
|
||||
|
||||
def test_api_key_env_vars(self):
|
||||
assert PROVIDER_REGISTRY["arcee"].api_key_env_vars == ("ARCEEAI_API_KEY",)
|
||||
|
||||
def test_base_url_env_var(self):
|
||||
assert PROVIDER_REGISTRY["arcee"].base_url_env_var == "ARCEE_BASE_URL"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Aliases
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestArceeAliases:
|
||||
@pytest.mark.parametrize("alias", ["arcee", "arcee-ai", "arceeai"])
|
||||
def test_alias_resolves(self, alias, monkeypatch):
|
||||
for key in _OTHER_PROVIDER_KEYS + ("OPENROUTER_API_KEY",):
|
||||
monkeypatch.delenv(key, raising=False)
|
||||
monkeypatch.setenv("ARCEEAI_API_KEY", "arc-test-12345")
|
||||
assert resolve_provider(alias) == "arcee"
|
||||
|
||||
def test_normalize_provider_models_py(self):
|
||||
from hermes_cli.models import normalize_provider
|
||||
assert normalize_provider("arcee-ai") == "arcee"
|
||||
assert normalize_provider("arceeai") == "arcee"
|
||||
|
||||
def test_normalize_provider_providers_py(self):
|
||||
from hermes_cli.providers import normalize_provider
|
||||
assert normalize_provider("arcee-ai") == "arcee"
|
||||
assert normalize_provider("arceeai") == "arcee"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Credentials
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestArceeCredentials:
|
||||
def test_status_configured(self, monkeypatch):
|
||||
monkeypatch.setenv("ARCEEAI_API_KEY", "arc-test")
|
||||
status = get_api_key_provider_status("arcee")
|
||||
assert status["configured"]
|
||||
|
||||
def test_status_not_configured(self, monkeypatch):
|
||||
monkeypatch.delenv("ARCEEAI_API_KEY", raising=False)
|
||||
status = get_api_key_provider_status("arcee")
|
||||
assert not status["configured"]
|
||||
|
||||
def test_openrouter_key_does_not_make_arcee_configured(self, monkeypatch):
|
||||
"""OpenRouter users should NOT see arcee as configured."""
|
||||
monkeypatch.delenv("ARCEEAI_API_KEY", raising=False)
|
||||
monkeypatch.setenv("OPENROUTER_API_KEY", "sk-or-test")
|
||||
status = get_api_key_provider_status("arcee")
|
||||
assert not status["configured"]
|
||||
|
||||
def test_resolve_credentials(self, monkeypatch):
|
||||
monkeypatch.setenv("ARCEEAI_API_KEY", "arc-direct-key")
|
||||
monkeypatch.delenv("ARCEE_BASE_URL", raising=False)
|
||||
creds = resolve_api_key_provider_credentials("arcee")
|
||||
assert creds["api_key"] == "arc-direct-key"
|
||||
assert creds["base_url"] == "https://api.arcee.ai/api/v1"
|
||||
|
||||
def test_custom_base_url_override(self, monkeypatch):
|
||||
monkeypatch.setenv("ARCEEAI_API_KEY", "arc-x")
|
||||
monkeypatch.setenv("ARCEE_BASE_URL", "https://custom.arcee.example/v1")
|
||||
creds = resolve_api_key_provider_credentials("arcee")
|
||||
assert creds["base_url"] == "https://custom.arcee.example/v1"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Model catalog
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestArceeModelCatalog:
|
||||
def test_static_model_list(self):
|
||||
from hermes_cli.models import _PROVIDER_MODELS
|
||||
assert "arcee" in _PROVIDER_MODELS
|
||||
models = _PROVIDER_MODELS["arcee"]
|
||||
assert "trinity-large-thinking" in models
|
||||
assert "trinity-large-preview" in models
|
||||
assert "trinity-mini" in models
|
||||
|
||||
def test_canonical_provider_entry(self):
|
||||
from hermes_cli.models import CANONICAL_PROVIDERS
|
||||
slugs = [p.slug for p in CANONICAL_PROVIDERS]
|
||||
assert "arcee" in slugs
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Model normalization
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestArceeNormalization:
|
||||
def test_in_matching_prefix_strip_set(self):
|
||||
from hermes_cli.model_normalize import _MATCHING_PREFIX_STRIP_PROVIDERS
|
||||
assert "arcee" in _MATCHING_PREFIX_STRIP_PROVIDERS
|
||||
|
||||
def test_strips_prefix(self):
|
||||
from hermes_cli.model_normalize import normalize_model_for_provider
|
||||
assert normalize_model_for_provider("arcee/trinity-mini", "arcee") == "trinity-mini"
|
||||
|
||||
def test_bare_name_unchanged(self):
|
||||
from hermes_cli.model_normalize import normalize_model_for_provider
|
||||
assert normalize_model_for_provider("trinity-mini", "arcee") == "trinity-mini"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# URL mapping
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestArceeURLMapping:
|
||||
def test_url_to_provider(self):
|
||||
from agent.model_metadata import _URL_TO_PROVIDER
|
||||
assert _URL_TO_PROVIDER.get("api.arcee.ai") == "arcee"
|
||||
|
||||
def test_provider_prefixes(self):
|
||||
from agent.model_metadata import _PROVIDER_PREFIXES
|
||||
assert "arcee" in _PROVIDER_PREFIXES
|
||||
assert "arcee-ai" in _PROVIDER_PREFIXES
|
||||
assert "arceeai" in _PROVIDER_PREFIXES
|
||||
|
||||
def test_trajectory_compressor_detects_arcee(self):
|
||||
import trajectory_compressor as tc
|
||||
comp = tc.TrajectoryCompressor.__new__(tc.TrajectoryCompressor)
|
||||
comp.config = types.SimpleNamespace(base_url="https://api.arcee.ai/api/v1")
|
||||
assert comp._detect_provider() == "arcee"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# providers.py overlay + aliases
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestArceeProvidersModule:
|
||||
def test_overlay_exists(self):
|
||||
from hermes_cli.providers import HERMES_OVERLAYS
|
||||
assert "arcee" in HERMES_OVERLAYS
|
||||
overlay = HERMES_OVERLAYS["arcee"]
|
||||
assert overlay.transport == "openai_chat"
|
||||
assert overlay.base_url_env_var == "ARCEE_BASE_URL"
|
||||
assert not overlay.is_aggregator
|
||||
|
||||
def test_label(self):
|
||||
from hermes_cli.models import _PROVIDER_LABELS
|
||||
assert _PROVIDER_LABELS["arcee"] == "Arcee AI"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Auxiliary client — main-model-first design
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestArceeAuxiliary:
|
||||
def test_main_model_first_design(self):
|
||||
"""Arcee uses main-model-first — no entry in _API_KEY_PROVIDER_AUX_MODELS."""
|
||||
from agent.auxiliary_client import _API_KEY_PROVIDER_AUX_MODELS
|
||||
assert "arcee" not in _API_KEY_PROVIDER_AUX_MODELS
|
||||
|
|
@ -417,6 +417,8 @@ class TrajectoryCompressor:
|
|||
return "zai"
|
||||
if "moonshot.ai" in url or "moonshot.cn" in url or "api.kimi.com" in url:
|
||||
return "kimi-coding"
|
||||
if "arcee.ai" in url:
|
||||
return "arcee"
|
||||
if "minimaxi.com" in url:
|
||||
return "minimax-cn"
|
||||
if "minimax.io" in url:
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ hermes setup # Or configure everything at once
|
|||
| **Z.AI** | GLM / Zhipu-hosted models | Set `GLM_API_KEY` / `ZAI_API_KEY` |
|
||||
| **Kimi / Moonshot** | Moonshot-hosted coding and chat models | Set `KIMI_API_KEY` |
|
||||
| **Kimi / Moonshot China** | China-region Moonshot endpoint | Set `KIMI_CN_API_KEY` |
|
||||
| **Arcee AI** | Trinity models | Set `ARCEEAI_API_KEY` |
|
||||
| **MiniMax** | International MiniMax endpoint | Set `MINIMAX_API_KEY` |
|
||||
| **MiniMax China** | China-region MiniMax endpoint | Set `MINIMAX_CN_API_KEY` |
|
||||
| **Alibaba Cloud** | Qwen models via DashScope | Set `DASHSCOPE_API_KEY` |
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ You need at least one way to connect to an LLM. Use `hermes model` to switch pro
|
|||
| **z.ai / GLM** | `GLM_API_KEY` in `~/.hermes/.env` (provider: `zai`) |
|
||||
| **Kimi / Moonshot** | `KIMI_API_KEY` in `~/.hermes/.env` (provider: `kimi-coding`) |
|
||||
| **Kimi / Moonshot (China)** | `KIMI_CN_API_KEY` in `~/.hermes/.env` (provider: `kimi-coding-cn`; aliases: `kimi-cn`, `moonshot-cn`) |
|
||||
| **Arcee AI** | `ARCEEAI_API_KEY` in `~/.hermes/.env` (provider: `arcee`; aliases: `arcee-ai`, `arceeai`) |
|
||||
| **MiniMax** | `MINIMAX_API_KEY` in `~/.hermes/.env` (provider: `minimax`) |
|
||||
| **MiniMax China** | `MINIMAX_CN_API_KEY` in `~/.hermes/.env` (provider: `minimax-cn`) |
|
||||
| **Alibaba Cloud** | `DASHSCOPE_API_KEY` in `~/.hermes/.env` (provider: `alibaba`, aliases: `dashscope`, `qwen`) |
|
||||
|
|
@ -167,12 +168,16 @@ hermes chat --provider alibaba --model qwen3.5-plus
|
|||
# Xiaomi MiMo
|
||||
hermes chat --provider xiaomi --model mimo-v2-pro
|
||||
# Requires: XIAOMI_API_KEY in ~/.hermes/.env
|
||||
|
||||
# Arcee AI (Trinity models)
|
||||
hermes chat --provider arcee --model trinity-large-thinking
|
||||
# Requires: ARCEEAI_API_KEY in ~/.hermes/.env
|
||||
```
|
||||
|
||||
Or set the provider permanently in `config.yaml`:
|
||||
```yaml
|
||||
model:
|
||||
provider: "zai" # or: kimi-coding, kimi-coding-cn, minimax, minimax-cn, alibaba, xiaomi
|
||||
provider: "zai" # or: kimi-coding, kimi-coding-cn, minimax, minimax-cn, alibaba, xiaomi, arcee
|
||||
default: "glm-5"
|
||||
```
|
||||
|
||||
|
|
@ -934,7 +939,7 @@ fallback_model:
|
|||
|
||||
When activated, the fallback swaps the model and provider mid-session without losing your conversation. It fires **at most once** per session.
|
||||
|
||||
Supported providers: `openrouter`, `nous`, `openai-codex`, `copilot`, `copilot-acp`, `anthropic`, `huggingface`, `zai`, `kimi-coding`, `kimi-coding-cn`, `minimax`, `minimax-cn`, `deepseek`, `ai-gateway`, `opencode-zen`, `opencode-go`, `kilocode`, `xiaomi`, `alibaba`, `custom`.
|
||||
Supported providers: `openrouter`, `nous`, `openai-codex`, `copilot`, `copilot-acp`, `anthropic`, `huggingface`, `zai`, `kimi-coding`, `kimi-coding-cn`, `minimax`, `minimax-cn`, `deepseek`, `ai-gateway`, `opencode-zen`, `opencode-go`, `kilocode`, `xiaomi`, `arcee`, `alibaba`, `custom`.
|
||||
|
||||
:::tip
|
||||
Fallback is configured exclusively through `config.yaml` — there are no environment variables for it. For full details on when it triggers, supported providers, and how it interacts with auxiliary tasks and delegation, see [Fallback Providers](/docs/user-guide/features/fallback-providers).
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ Common options:
|
|||
| `-q`, `--query "..."` | One-shot, non-interactive prompt. |
|
||||
| `-m`, `--model <model>` | Override the model for this run. |
|
||||
| `-t`, `--toolsets <csv>` | Enable a comma-separated set of toolsets. |
|
||||
| `--provider <provider>` | Force a provider: `auto`, `openrouter`, `nous`, `openai-codex`, `copilot-acp`, `copilot`, `anthropic`, `gemini`, `huggingface`, `zai`, `kimi-coding`, `minimax`, `minimax-cn`, `kilocode`, `xiaomi`. |
|
||||
| `--provider <provider>` | Force a provider: `auto`, `openrouter`, `nous`, `openai-codex`, `copilot-acp`, `copilot`, `anthropic`, `gemini`, `huggingface`, `zai`, `kimi-coding`, `minimax`, `minimax-cn`, `kilocode`, `xiaomi`, `arcee`. |
|
||||
| `-s`, `--skills <name>` | Preload one or more skills for the session (can be repeated or comma-separated). |
|
||||
| `-v`, `--verbose` | Verbose output. |
|
||||
| `-Q`, `--quiet` | Programmatic mode: suppress banner/spinner/tool previews. |
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ All variables go in `~/.hermes/.env`. You can also set them with `hermes config
|
|||
| `KIMI_API_KEY` | Kimi / Moonshot AI API key ([moonshot.ai](https://platform.moonshot.ai)) |
|
||||
| `KIMI_BASE_URL` | Override Kimi base URL (default: `https://api.moonshot.ai/v1`) |
|
||||
| `KIMI_CN_API_KEY` | Kimi / Moonshot China API key ([moonshot.cn](https://platform.moonshot.cn)) |
|
||||
| `ARCEEAI_API_KEY` | Arcee AI API key ([chat.arcee.ai](https://chat.arcee.ai/)) |
|
||||
| `ARCEE_BASE_URL` | Override Arcee base URL (default: `https://api.arcee.ai/api/v1`) |
|
||||
| `MINIMAX_API_KEY` | MiniMax API key — global endpoint ([minimax.io](https://www.minimax.io)) |
|
||||
| `MINIMAX_BASE_URL` | Override MiniMax base URL (default: `https://api.minimax.io/v1`) |
|
||||
| `MINIMAX_CN_API_KEY` | MiniMax API key — China endpoint ([minimaxi.com](https://www.minimaxi.com)) |
|
||||
|
|
@ -68,7 +70,7 @@ For native Anthropic auth, Hermes prefers Claude Code's own credential files whe
|
|||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `HERMES_INFERENCE_PROVIDER` | Override provider selection: `auto`, `openrouter`, `nous`, `openai-codex`, `copilot`, `copilot-acp`, `anthropic`, `huggingface`, `zai`, `kimi-coding`, `kimi-coding-cn`, `minimax`, `minimax-cn`, `kilocode`, `xiaomi`, `alibaba`, `deepseek`, `opencode-zen`, `opencode-go`, `ai-gateway` (default: `auto`) |
|
||||
| `HERMES_INFERENCE_PROVIDER` | Override provider selection: `auto`, `openrouter`, `nous`, `openai-codex`, `copilot`, `copilot-acp`, `anthropic`, `huggingface`, `zai`, `kimi-coding`, `kimi-coding-cn`, `minimax`, `minimax-cn`, `kilocode`, `xiaomi`, `arcee`, `alibaba`, `deepseek`, `opencode-zen`, `opencode-go`, `ai-gateway` (default: `auto`) |
|
||||
| `HERMES_PORTAL_BASE_URL` | Override Nous Portal URL (for development/testing) |
|
||||
| `NOUS_INFERENCE_BASE_URL` | Override Nous inference API URL |
|
||||
| `HERMES_NOUS_MIN_KEY_TTL_SECONDS` | Min agent key TTL before re-mint (default: 1800 = 30min) |
|
||||
|
|
|
|||
|
|
@ -601,7 +601,7 @@ Every model slot in Hermes — auxiliary tasks, compression, fallback — uses t
|
|||
|
||||
When `base_url` is set, Hermes ignores the provider and calls that endpoint directly (using `api_key` or `OPENAI_API_KEY` for auth). When only `provider` is set, Hermes uses that provider's built-in auth and base URL.
|
||||
|
||||
Available providers for auxiliary tasks: `auto`, `openrouter`, `nous`, `codex`, `copilot`, `anthropic`, `main`, `zai`, `kimi-coding`, `kimi-coding-cn`, `minimax`, any provider registered in the [provider registry](/docs/reference/environment-variables), or any named custom provider from your `custom_providers` list (e.g. `provider: "beans"`).
|
||||
Available providers for auxiliary tasks: `auto`, `openrouter`, `nous`, `codex`, `copilot`, `anthropic`, `main`, `zai`, `kimi-coding`, `kimi-coding-cn`, `arcee`, `minimax`, any provider registered in the [provider registry](/docs/reference/environment-variables), or any named custom provider from your `custom_providers` list (e.g. `provider: "beans"`).
|
||||
|
||||
:::warning `"main"` is for auxiliary tasks only
|
||||
The `"main"` provider option means "use whatever provider my main agent uses" — it's only valid inside `auxiliary:`, `compression:`, and `fallback_model:` configs. It is **not** a valid value for your top-level `model.provider` setting. If you use a custom OpenAI-compatible endpoint, set `provider: custom` in your `model:` section. See [AI Providers](/docs/integrations/providers) for all main model provider options.
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ Both `provider` and `model` are **required**. If either is missing, the fallback
|
|||
| OpenCode Go | `opencode-go` | `OPENCODE_GO_API_KEY` |
|
||||
| Kilo Code | `kilocode` | `KILOCODE_API_KEY` |
|
||||
| Xiaomi MiMo | `xiaomi` | `XIAOMI_API_KEY` |
|
||||
| Arcee AI | `arcee` | `ARCEEAI_API_KEY` |
|
||||
| Alibaba / DashScope | `alibaba` | `DASHSCOPE_API_KEY` |
|
||||
| Hugging Face | `huggingface` | `HF_TOKEN` |
|
||||
| Custom endpoint | `custom` | `base_url` + `api_key_env` (see below) |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue