fix: follow-up for salvaged PR #8952

- Rename provider_contracts.py -> volcengine_byteplus.py for explicitness
- Consolidate duplicate host-to-provider mappings: provider_for_base_url()
  now uses the canonical _URL_TO_PROVIDER from model_metadata.py instead of
  maintaining a separate 20-entry dict
- Add volcengine/byteplus to runtime_provider.py model-dependent base URL
  resolution (kimi-style special case) so manually-edited configs resolve
  the coding-plan base URL correctly
- Remove volcengine/byteplus from _API_KEY_PROVIDER_AUX_MODELS — the
  main-model-first design in _resolve_auto() handles these providers
  already; entries were dead code in the normal flow
- Add VOLCENGINE_API_KEY and BYTEPLUS_API_KEY to OPTIONAL_ENV_VARS in
  config.py so they appear in hermes setup
- Update docs: environment-variables.md, fallback-providers.md,
  configuration.md
This commit is contained in:
kshitijk4poor
2026-04-22 22:41:38 +05:30
parent ccde71a6ab
commit 8aaefec231
14 changed files with 40 additions and 43 deletions
-3
View File
@@ -47,7 +47,6 @@ from openai import OpenAI
from agent.credential_pool import load_pool
from hermes_cli.config import get_hermes_home
from hermes_cli.provider_contracts import PROVIDER_AUX_MODELS
from hermes_constants import OPENROUTER_BASE_URL
from utils import base_url_host_matches, base_url_hostname, normalize_proxy_env_vars
@@ -149,8 +148,6 @@ _API_KEY_PROVIDER_AUX_MODELS: Dict[str, str] = {
"opencode-go": "glm-5",
"kilocode": "google/gemini-3-flash-preview",
"ollama-cloud": "nemotron-3-nano:30b",
"volcengine": PROVIDER_AUX_MODELS["volcengine"],
"byteplus": PROVIDER_AUX_MODELS["byteplus"],
}
# Vision-specific model overrides for direct providers.
+1 -1
View File
@@ -14,7 +14,7 @@ from urllib.parse import urlparse
import requests
import yaml
from hermes_cli.provider_contracts import model_context_window
from hermes_cli.volcengine_byteplus import model_context_window
from utils import base_url_host_matches, base_url_hostname
from hermes_constants import OPENROUTER_MODELS_URL
+1 -1
View File
@@ -39,7 +39,7 @@ import httpx
import yaml
from hermes_cli.config import get_hermes_home, get_config_path, read_raw_config
from hermes_cli.provider_contracts import (
from hermes_cli.volcengine_byteplus import (
VOLCENGINE_PROVIDER,
BYTEPLUS_PROVIDER,
VOLCENGINE_STANDARD_BASE_URL,
+14
View File
@@ -1281,6 +1281,20 @@ OPTIONAL_ENV_VARS = {
"category": "provider",
"advanced": True,
},
"VOLCENGINE_API_KEY": {
"description": "Volcengine API key for Doubao / Seed models (standard + Coding Plan catalogs)",
"prompt": "Volcengine API Key",
"url": "https://www.volcengine.com/product/ark",
"password": True,
"category": "provider",
},
"BYTEPLUS_API_KEY": {
"description": "BytePlus API key for Seed / Dola models (standard + Coding Plan catalogs)",
"prompt": "BytePlus API Key",
"url": "https://www.byteplus.com/en/product/modelark",
"password": True,
"category": "provider",
},
"AWS_REGION": {
"description": "AWS region for Bedrock API calls (e.g. us-east-1, eu-central-1)",
"prompt": "AWS Region",
+1 -1
View File
@@ -2945,7 +2945,7 @@ def _model_flow_named_custom(config, provider_info):
# Curated model lists for direct API-key providers — single source in models.py
from hermes_cli.models import _PROVIDER_MODELS
from hermes_cli.provider_contracts import (
from hermes_cli.volcengine_byteplus import (
base_url_for_provider_model,
provider_models,
)
+11 -26
View File
@@ -22,7 +22,7 @@ from hermes_cli import __version__ as _HERMES_VERSION
# Check (error 1010) don't reject the default ``Python-urllib/*`` signature.
_HERMES_USER_AGENT = f"hermes-cli/{_HERMES_VERSION}"
from hermes_cli.provider_contracts import (
from hermes_cli.volcengine_byteplus import (
BYTEPLUS_PROVIDER,
VOLCENGINE_PROVIDER,
provider_models,
@@ -1337,36 +1337,21 @@ def _get_custom_base_url() -> str:
def provider_for_base_url(base_url: str) -> Optional[str]:
"""Return a known built-in provider for a configured base URL, if any."""
"""Return a known built-in provider for a configured base URL, if any.
Uses the canonical _URL_TO_PROVIDER mapping from model_metadata plus
additional entries for providers not in that dict.
"""
normalized = str(base_url or "").strip().rstrip("/")
if not normalized or "openrouter.ai" in normalized.lower():
return None
url_lower = normalized.lower()
host_to_provider = {
"ark.cn-beijing.volces.com": VOLCENGINE_PROVIDER,
"ark.ap-southeast.bytepluses.com": BYTEPLUS_PROVIDER,
"api.z.ai": "zai",
"api.moonshot.ai": "kimi-coding",
"api.kimi.com": "kimi-coding",
"api.minimax.io": "minimax",
"api.minimaxi.com": "minimax-cn",
"dashscope.aliyuncs.com": "alibaba",
"dashscope-intl.aliyuncs.com": "alibaba",
"portal.qwen.ai": "qwen-oauth",
"router.huggingface.co": "huggingface",
"generativelanguage.googleapis.com": "gemini",
"api.deepseek.com": "deepseek",
"api.githubcopilot.com": "copilot",
"models.github.ai": "copilot",
"opencode.ai": "opencode-go",
"api.x.ai": "xai",
"api.xiaomimimo.com": "xiaomi",
"xiaomimimo.com": "xiaomi",
"api.anthropic.com": "anthropic",
"inference-api.nousresearch.com": "nous",
}
for host, provider_id in host_to_provider.items():
# Primary source — shared with context-length resolution
from agent.model_metadata import _URL_TO_PROVIDER
for host, provider_id in _URL_TO_PROVIDER.items():
if host in url_lower:
canonical = normalize_provider(provider_id)
if canonical in _PROVIDER_LABELS and canonical != "custom":
+1 -1
View File
@@ -23,7 +23,7 @@ import logging
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Tuple
from hermes_cli.provider_contracts import (
from hermes_cli.volcengine_byteplus import (
BYTEPLUS_PROVIDER,
BYTEPLUS_STANDARD_BASE_URL,
VOLCENGINE_PROVIDER,
+1 -1
View File
@@ -643,7 +643,7 @@ def _resolve_explicit_runtime(
base_url = explicit_base_url
if not base_url:
if provider in ("kimi-coding", "kimi-coding-cn"):
if provider in ("kimi-coding", "kimi-coding-cn", "volcengine", "byteplus"):
creds = resolve_api_key_provider_credentials(provider)
base_url = creds.get("base_url", "").rstrip("/")
else:
@@ -68,11 +68,6 @@ PROVIDER_MODEL_CATALOGS: Dict[str, Tuple[str, ...]] = {
BYTEPLUS_PROVIDER: BYTEPLUS_STANDARD_MODEL_REFS + BYTEPLUS_CODING_PLAN_MODEL_REFS,
}
PROVIDER_AUX_MODELS: Dict[str, str] = {
VOLCENGINE_PROVIDER: "volcengine/doubao-seed-2-0-lite-260215",
BYTEPLUS_PROVIDER: "byteplus/seed-2-0-lite-260228",
}
MODEL_CONTEXT_WINDOWS: Dict[str, int] = {
"doubao-seed-2-0-pro-260215": 256000,
"doubao-seed-2-0-lite-260215": 256000,
+1
View File
@@ -105,6 +105,7 @@ AUTHOR_MAP = {
"134848055+UNLINEARITY@users.noreply.github.com": "UNLINEARITY",
"ben.burtenshaw@gmail.com": "burtenshaw",
"roopaknijhara@gmail.com": "rnijhara",
"Maaannnn@users.noreply.github.com": "Maaannnn",
# contributors (manual mapping from git names)
"ahmedsherif95@gmail.com": "asheriif",
"liujinkun@bytedance.com": "liujinkun2025",
+4 -3
View File
@@ -805,11 +805,12 @@ class TestModelDefaultElimination:
assert isinstance(model, str) and model.strip(), \
f"{provider_id} should have a non-empty model string"
def test_contract_providers_have_aux_models(self):
def test_volcengine_byteplus_use_main_model_first(self):
"""Volcengine/BytePlus use main-model-first — no entry in _API_KEY_PROVIDER_AUX_MODELS."""
from agent.auxiliary_client import _API_KEY_PROVIDER_AUX_MODELS
assert _API_KEY_PROVIDER_AUX_MODELS["volcengine"] == "volcengine/doubao-seed-2-0-lite-260215"
assert _API_KEY_PROVIDER_AUX_MODELS["byteplus"] == "byteplus/seed-2-0-lite-260228"
assert "volcengine" not in _API_KEY_PROVIDER_AUX_MODELS
assert "byteplus" not in _API_KEY_PROVIDER_AUX_MODELS
class TestContractProviderAliases:
@@ -44,6 +44,8 @@ All variables go in `~/.hermes/.env`. You can also set them with `hermes config
| `KILOCODE_BASE_URL` | Override Kilo Code base URL (default: `https://api.kilo.ai/api/gateway`) |
| `XIAOMI_API_KEY` | Xiaomi MiMo API key ([platform.xiaomimimo.com](https://platform.xiaomimimo.com)) |
| `XIAOMI_BASE_URL` | Override Xiaomi MiMo base URL (default: `https://api.xiaomimimo.com/v1`) |
| `VOLCENGINE_API_KEY` | Volcengine API key for Doubao / Seed models ([volcengine.com/product/ark](https://www.volcengine.com/product/ark)) |
| `BYTEPLUS_API_KEY` | BytePlus API key for Seed / Dola models ([byteplus.com/en/product/modelark](https://www.byteplus.com/en/product/modelark)) |
| `HF_TOKEN` | Hugging Face token for Inference Providers ([huggingface.co/settings/tokens](https://huggingface.co/settings/tokens)) |
| `HF_BASE_URL` | Override Hugging Face base URL (default: `https://router.huggingface.co/v1`) |
| `GOOGLE_API_KEY` | Google AI Studio API key ([aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey)) |
+1 -1
View File
@@ -628,7 +628,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`, `main`, plus any provider in the [provider registry](/docs/reference/environment-variables) — `openrouter`, `nous`, `openai-codex`, `copilot`, `copilot-acp`, `anthropic`, `gemini`, `google-gemini-cli`, `qwen-oauth`, `zai`, `kimi-coding`, `kimi-coding-cn`, `minimax`, `minimax-cn`, `deepseek`, `nvidia`, `xai`, `ollama-cloud`, `alibaba`, `bedrock`, `huggingface`, `arcee`, `xiaomi`, `kilocode`, `opencode-zen`, `opencode-go`, `ai-gateway` — or any named custom provider from your `custom_providers` list (e.g. `provider: "beans"`).
Available providers for auxiliary tasks: `auto`, `main`, plus any provider in the [provider registry](/docs/reference/environment-variables) — `openrouter`, `nous`, `openai-codex`, `copilot`, `copilot-acp`, `anthropic`, `gemini`, `google-gemini-cli`, `qwen-oauth`, `zai`, `kimi-coding`, `kimi-coding-cn`, `minimax`, `minimax-cn`, `deepseek`, `nvidia`, `xai`, `ollama-cloud`, `alibaba`, `bedrock`, `huggingface`, `arcee`, `xiaomi`, `volcengine`, `byteplus`, `kilocode`, `opencode-zen`, `opencode-go`, `ai-gateway` — 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.
@@ -58,6 +58,8 @@ 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` |
| Volcengine | `volcengine` | `VOLCENGINE_API_KEY` |
| BytePlus | `byteplus` | `BYTEPLUS_API_KEY` |
| Arcee AI | `arcee` | `ARCEEAI_API_KEY` |
| Alibaba / DashScope | `alibaba` | `DASHSCOPE_API_KEY` |
| Hugging Face | `huggingface` | `HF_TOKEN` |