From 0a4cf5b3e16e88a474dbe47711f6e8b1e0c6b0f4 Mon Sep 17 00:00:00 2001 From: arthurbr11 Date: Mon, 13 Apr 2026 17:16:43 -0700 Subject: [PATCH] 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. --- .env.example | 8 + agent/model_metadata.py | 3 + cli-config.yaml.example | 1 + hermes_cli/auth.py | 9 + hermes_cli/config.py | 16 ++ hermes_cli/doctor.py | 1 + hermes_cli/main.py | 4 +- hermes_cli/model_normalize.py | 2 + hermes_cli/models.py | 8 + hermes_cli/providers.py | 9 + hermes_cli/setup.py | 1 + tests/hermes_cli/test_arcee_provider.py | 207 ++++++++++++++++++ trajectory_compressor.py | 2 + website/docs/getting-started/quickstart.md | 1 + website/docs/integrations/providers.md | 9 +- website/docs/reference/cli-commands.md | 2 +- .../docs/reference/environment-variables.md | 4 +- website/docs/user-guide/configuration.md | 2 +- .../user-guide/features/fallback-providers.md | 1 + 19 files changed, 283 insertions(+), 7 deletions(-) create mode 100644 tests/hermes_cli/test_arcee_provider.py diff --git a/.env.example b/.env.example index f2c5769c6..0317296ba 100644 --- a/.env.example +++ b/.env.example @@ -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) # ============================================================================= diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 7d4e9338a..98bb9543f 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -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", diff --git a/cli-config.yaml.example b/cli-config.yaml.example index 637e45f13..789c5481a 100644 --- a/cli-config.yaml.example +++ b/cli-config.yaml.example @@ -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) # diff --git a/hermes_cli/auth.py b/hermes_cli/auth.py index fb75f7a8d..9d1d82e8c 100644 --- a/hermes_cli/auth.py +++ b/hermes_cli/auth.py @@ -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", diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 657d02c2c..64a5bd1a9 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -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", diff --git a/hermes_cli/doctor.py b/hermes_cli/doctor.py index a01690cba..19c332b35 100644 --- a/hermes_cli/doctor.py +++ b/hermes_cli/doctor.py @@ -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), diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 2a2143874..03890eaab 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -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)" ) diff --git a/hermes_cli/model_normalize.py b/hermes_cli/model_normalize.py index c391b0715..40afe003b 100644 --- a/hermes_cli/model_normalize.py +++ b/hermes_cli/model_normalize.py @@ -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", }) diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 239116126..d8223e86c 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -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", diff --git a/hermes_cli/providers.py b/hermes_cli/providers.py index ee4beebe0..6fb940d31 100644 --- a/hermes_cli/providers.py +++ b/hermes_cli/providers.py @@ -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", diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index a67303c99..aadf369f5 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -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"], diff --git a/tests/hermes_cli/test_arcee_provider.py b/tests/hermes_cli/test_arcee_provider.py new file mode 100644 index 000000000..33266588a --- /dev/null +++ b/tests/hermes_cli/test_arcee_provider.py @@ -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 diff --git a/trajectory_compressor.py b/trajectory_compressor.py index f05fca881..4c0de4029 100644 --- a/trajectory_compressor.py +++ b/trajectory_compressor.py @@ -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: diff --git a/website/docs/getting-started/quickstart.md b/website/docs/getting-started/quickstart.md index 983cedd80..880c01cb2 100644 --- a/website/docs/getting-started/quickstart.md +++ b/website/docs/getting-started/quickstart.md @@ -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` | diff --git a/website/docs/integrations/providers.md b/website/docs/integrations/providers.md index 3ded8d10b..a44483a00 100644 --- a/website/docs/integrations/providers.md +++ b/website/docs/integrations/providers.md @@ -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). diff --git a/website/docs/reference/cli-commands.md b/website/docs/reference/cli-commands.md index a4b589379..2e054482f 100644 --- a/website/docs/reference/cli-commands.md +++ b/website/docs/reference/cli-commands.md @@ -83,7 +83,7 @@ Common options: | `-q`, `--query "..."` | One-shot, non-interactive prompt. | | `-m`, `--model ` | Override the model for this run. | | `-t`, `--toolsets ` | Enable a comma-separated set of toolsets. | -| `--provider ` | Force a provider: `auto`, `openrouter`, `nous`, `openai-codex`, `copilot-acp`, `copilot`, `anthropic`, `gemini`, `huggingface`, `zai`, `kimi-coding`, `minimax`, `minimax-cn`, `kilocode`, `xiaomi`. | +| `--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 ` | 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. | diff --git a/website/docs/reference/environment-variables.md b/website/docs/reference/environment-variables.md index 177f87ab9..907391128 100644 --- a/website/docs/reference/environment-variables.md +++ b/website/docs/reference/environment-variables.md @@ -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) | diff --git a/website/docs/user-guide/configuration.md b/website/docs/user-guide/configuration.md index 2383cb640..a27884e0c 100644 --- a/website/docs/user-guide/configuration.md +++ b/website/docs/user-guide/configuration.md @@ -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. diff --git a/website/docs/user-guide/features/fallback-providers.md b/website/docs/user-guide/features/fallback-providers.md index 0caa2f0ec..1e2b2a803 100644 --- a/website/docs/user-guide/features/fallback-providers.md +++ b/website/docs/user-guide/features/fallback-providers.md @@ -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) |