refactor(hermes_cli): runtime_provider — _creds_fallback + _actual_local_key helpers, lazy rung generator for ladder tail; pack alias/label tables

This commit is contained in:
Teknium
2026-09-02 21:54:02 -07:00
parent 6740d264f1
commit d211f8562c
2 changed files with 62 additions and 87 deletions
+20 -49
View File
@@ -115,39 +115,25 @@ class ProviderDef:
# -- Aliases: human-friendly / legacy names grouped by canonical (models.dev where possible) id;
# ``ALIASES`` is the inverted lookup table. ---------------------------------------------------
_ALIAS_GROUPS: Dict[str, Tuple[str, ...]] = {
"openrouter": ("openai",),
"zai": ("glm", "z-ai", "z.ai", "zhipu"),
"xai": ("x-ai", "x.ai", "grok"),
"openrouter": ("openai",), "zai": ("glm", "z-ai", "z.ai", "zhipu"), "xai": ("x-ai", "x.ai", "grok"),
"xai-oauth": ("grok-oauth", "xai-oauth", "x-ai-oauth", "xai-grok-oauth"),
"nvidia": ("nim", "nvidia-nim", "build-nvidia", "nemotron"),
"kimi-for-coding": ("kimi", "kimi-coding", "kimi-coding-cn", "moonshot"),
"stepfun": ("step", "stepfun-coding-plan"),
"minimax-cn": ("minimax-china", "minimax_cn"),
"anthropic": ("claude", "claude-code"),
"github-copilot": ("copilot", "github"),
"copilot-acp": ("github-copilot-acp",),
"vercel": ("ai-gateway", "aigateway", "vercel-ai-gateway"),
"opencode": ("opencode-zen", "zen"),
"opencode-go": ("go", "opencode-go-sub"),
"opencode-free": ("free", "opencode_free"),
"kilo": ("kilocode", "kilo-code", "kilo-gateway"),
"deepseek": ("deep-seek",),
"alibaba": ("dashscope", "aliyun", "qwen", "alibaba-cloud"),
"stepfun": ("step", "stepfun-coding-plan"), "minimax-cn": ("minimax-china", "minimax_cn"),
"anthropic": ("claude", "claude-code"), "github-copilot": ("copilot", "github"),
"copilot-acp": ("github-copilot-acp",), "vercel": ("ai-gateway", "aigateway", "vercel-ai-gateway"),
"opencode": ("opencode-zen", "zen"), "opencode-go": ("go", "opencode-go-sub"),
"opencode-free": ("free", "opencode_free"), "kilo": ("kilocode", "kilo-code", "kilo-gateway"),
"deepseek": ("deep-seek",), "alibaba": ("dashscope", "aliyun", "qwen", "alibaba-cloud"),
"alibaba-coding-plan": ("alibaba_coding", "alibaba-coding", "alibaba_coding_plan"),
"huggingface": ("hf", "hugging-face", "huggingface-hub"),
"novita": ("novita-ai", "novitaai"),
"xiaomi": ("mimo", "xiaomi-mimo"),
"tencent-tokenhub": ("tencent", "tokenhub", "tencent-cloud", "tencentmaas"),
"huggingface": ("hf", "hugging-face", "huggingface-hub"), "novita": ("novita-ai", "novitaai"),
"xiaomi": ("mimo", "xiaomi-mimo"), "tencent-tokenhub": ("tencent", "tokenhub", "tencent-cloud", "tencentmaas"),
"tencent-tokenplan": ("tokenplan", "tencent-lkeap"),
"bedrock": ("aws", "aws-bedrock", "amazon-bedrock", "amazon"),
"arcee": ("arcee-ai", "arceeai"),
"gmi": ("gmi-cloud", "gmicloud"),
"fireworks": ("fireworks-ai", "fw"),
"upstage": ("solar",),
"bedrock": ("aws", "aws-bedrock", "amazon-bedrock", "amazon"), "arcee": ("arcee-ai", "arceeai"),
"gmi": ("gmi-cloud", "gmicloud"), "fireworks": ("fireworks-ai", "fw"), "upstage": ("solar",),
"actual": ("actual-computer", "actualcomputer", "aci"),
"nebius-token-factory": ("nebius", "nebius-tokenfactory", "nebius-tf", "token-factory", "tokenfactory"),
"lmstudio": ("lmstudio", "lm-studio", "lm_studio"),
"custom": ("ollama",),
"lmstudio": ("lmstudio", "lm-studio", "lm_studio"), "custom": ("ollama",),
"local": ("vllm", "llamacpp", "llama.cpp", "llama-cpp"),
}
ALIASES: Dict[str, str] = {alias: canon for canon, aliases in _ALIAS_GROUPS.items() for alias in aliases}
@@ -156,35 +142,20 @@ ALIASES: Dict[str, str] = {alias: canon for canon, aliases in _ALIAS_GROUPS.item
# -- Display labels for providers not in the models.dev catalog ---------------
_LABEL_OVERRIDES: Dict[str, str] = {
"moa": "Mixture of Agents",
"nous": "Nous Portal",
"openai-codex": "ChatGPT or Codex Subscription",
"copilot-acp": "GitHub Copilot ACP",
"stepfun": "StepFun Step Plan",
"xiaomi": "Xiaomi MiMo",
"gmi": "GMI Cloud",
"upstage": "Upstage Solar",
"actual": "Actual Computer",
"tencent-tokenhub": "Tencent TokenHub",
"nebius-token-factory": "Nebius Token Factory",
"tencent-tokenplan": "Tencent TokenPlan",
"lmstudio": "LM Studio",
"local": "Local endpoint",
"bedrock": "AWS Bedrock",
"vertex": "Google Vertex AI",
"ollama-cloud": "Ollama Cloud",
"xai-oauth": "xAI Grok OAuth (SuperGrok / Premium+)",
"opencode-free": "OpenCode Free",
"moa": "Mixture of Agents", "nous": "Nous Portal", "openai-codex": "ChatGPT or Codex Subscription",
"copilot-acp": "GitHub Copilot ACP", "stepfun": "StepFun Step Plan", "xiaomi": "Xiaomi MiMo", "gmi": "GMI Cloud",
"upstage": "Upstage Solar", "actual": "Actual Computer", "tencent-tokenhub": "Tencent TokenHub",
"nebius-token-factory": "Nebius Token Factory", "tencent-tokenplan": "Tencent TokenPlan", "lmstudio": "LM Studio",
"local": "Local endpoint", "bedrock": "AWS Bedrock", "vertex": "Google Vertex AI", "ollama-cloud": "Ollama Cloud",
"xai-oauth": "xAI Grok OAuth (SuperGrok / Premium+)", "opencode-free": "OpenCode Free",
}
# -- Transport → API mode mapping ---------------------------------------------
TRANSPORT_TO_API_MODE: Dict[str, str] = {
"openai_chat": "chat_completions",
"anthropic_messages": "anthropic_messages",
"codex_responses": "codex_responses",
"bedrock_converse": "bedrock_converse",
"openai_chat": "chat_completions", "anthropic_messages": "anthropic_messages",
"codex_responses": "codex_responses", "bedrock_converse": "bedrock_converse",
}
+42 -38
View File
@@ -594,14 +594,20 @@ def _explicit_anthropic(requested_provider, model_cfg, api_key, base_url, target
return _runtime("anthropic", "anthropic_messages", base_url, api_key, source="explicit", requested_provider=requested_provider)
def _creds_fallback(api_key, explicit_base_url, base_url, expiry, expiry_key, resolve):
"""When no explicit key was given, take api_key / expiry / base_url from stored credentials
(an explicit --base-url still wins over the stored one)."""
if api_key:
return api_key, base_url, expiry
creds = resolve()
return creds.get("api_key", ""), explicit_base_url or creds.get("base_url", "").rstrip("/") or base_url, creds.get(expiry_key)
def _explicit_codex(requested_provider, model_cfg, api_key, explicit_base_url, target_model):
base_url = explicit_base_url or DEFAULT_CODEX_BASE_URL
last_refresh = None
if not api_key:
creds = resolve_codex_runtime_credentials()
api_key = creds.get("api_key", "")
last_refresh = creds.get("last_refresh")
base_url = explicit_base_url or creds.get("base_url", "").rstrip("/") or base_url
api_key, base_url, last_refresh = _creds_fallback(
api_key, explicit_base_url, explicit_base_url or DEFAULT_CODEX_BASE_URL, None, "last_refresh",
resolve_codex_runtime_credentials,
)
return _runtime(
"openai-codex", "codex_responses", base_url, api_key,
source="explicit", last_refresh=last_refresh, requested_provider=requested_provider,
@@ -620,12 +626,10 @@ def _explicit_nous(requested_provider, model_cfg, api_key, explicit_base_url, ta
api_key = api_key or (
str(state.get("agent_key") or "").strip() if _agent_key_is_usable(state, _nous_min_key_ttl()) else ""
)
expires_at = state.get("agent_key_expires_at") or state.get("expires_at")
if not api_key:
creds = _resolve_nous_creds()
api_key = creds.get("api_key", "")
expires_at = creds.get("expires_at")
base_url = explicit_base_url or creds.get("base_url", "").rstrip("/") or base_url
api_key, base_url, expires_at = _creds_fallback(
api_key, explicit_base_url, base_url, state.get("agent_key_expires_at") or state.get("expires_at"), "expires_at",
_resolve_nous_creds,
)
return _runtime(
"nous", nous_api_mode(_effective_model(model_cfg, target_model)), base_url, api_key,
source="explicit", expires_at=expires_at, requested_provider=requested_provider,
@@ -638,6 +642,13 @@ def _explicit_azure_foundry(requested_provider, model_cfg, api_key, base_url, ta
)
def _actual_local_key(provider: str, api_key: str, base_url: str) -> str:
"""Actual Computer's loopback daemon speaks a no-auth local API — substitute the placeholder key."""
if provider == "actual" and not api_key and is_actual_local_base_url(base_url):
return ACTUAL_LOCAL_NOAUTH_PLACEHOLDER
return api_key
def _explicit_api_key_provider(provider, pconfig, requested_provider, model_cfg, api_key, base_url, target_model):
if not base_url:
if provider in {"kimi-coding", "kimi-coding-cn"}:
@@ -657,8 +668,7 @@ def _explicit_api_key_provider(provider, pconfig, requested_provider, model_cfg,
api_mode = _api_key_provider_api_mode(
provider, model_cfg, api_key, base_url, target_model or model_cfg.get("default", ""), opencode_by_model=False
)
if provider == "actual" and not api_key and is_actual_local_base_url(base_url):
api_key = ACTUAL_LOCAL_NOAUTH_PLACEHOLDER
api_key = _actual_local_key(provider, api_key, base_url)
return _runtime(provider, api_mode, base_url.rstrip("/"), api_key, source="explicit", requested_provider=requested_provider)
@@ -816,9 +826,7 @@ def _api_key_provider_runtime(provider, pconfig, requested_provider, model_cfg,
provider, model_cfg, creds.get("api_key", ""), base_url, target_model or model_cfg.get("default", ""), opencode_by_model=True
)
base_url = _finalize_base_url(provider, api_mode, base_url)
api_key = creds.get("api_key", "")
if provider == "actual" and not api_key and is_actual_local_base_url(base_url):
api_key = ACTUAL_LOCAL_NOAUTH_PLACEHOLDER
api_key = _actual_local_key(provider, creds.get("api_key", ""), base_url)
return _runtime(provider, api_mode, base_url, api_key, source=creds.get("source", "env"), requested_provider=requested_provider)
@@ -963,37 +971,33 @@ def resolve_runtime_provider(
if runtime:
return runtime
provider = resolve_provider(requested_provider, explicit_api_key=explicit_api_key, explicit_base_url=explicit_base_url)
return next(r for r in _provider_rungs(provider, requested_provider, explicit_api_key, explicit_base_url, target_model) if r)
def _provider_rungs(provider, requested_provider, explicit_api_key, explicit_base_url, target_model):
"""Rungs 5-8 of the ladder, yielded lazily so each is evaluated only when the previous one
returned nothing; the last rung (OpenRouter / bare-custom fallback) always yields a runtime."""
model_cfg = _get_model_config()
runtime = _opencode_free_runtime(provider, requested_provider, model_cfg, target_model)
if runtime is not None:
return runtime
runtime = _resolve_explicit_runtime(
yield _opencode_free_runtime(provider, requested_provider, model_cfg, target_model)
yield _resolve_explicit_runtime(
provider=provider, requested_provider=requested_provider, model_cfg=model_cfg,
explicit_api_key=explicit_api_key, explicit_base_url=explicit_base_url, target_model=target_model,
)
if runtime:
return runtime
runtime = _resolve_from_pool(provider, requested_provider, model_cfg, explicit_api_key, explicit_base_url, target_model)
if runtime:
return runtime
yield _resolve_from_pool(provider, requested_provider, model_cfg, explicit_api_key, explicit_base_url, target_model)
if provider in _OAUTH_RUNTIME_PROVIDERS:
runtime = _resolve_oauth_runtime(provider, requested_provider, model_cfg, target_model)
if runtime:
return runtime
yield _resolve_oauth_runtime(provider, requested_provider, model_cfg, target_model)
if provider == "minimax-oauth":
runtime = _minimax_oauth_runtime(provider, requested_provider)
if runtime:
return runtime
yield _minimax_oauth_runtime(provider, requested_provider)
if _is_external_process_provider(provider):
return _resolve_external_process_runtime(provider, requested_provider)
yield _resolve_external_process_runtime(provider, requested_provider)
if provider == "anthropic":
return _anthropic_env_runtime(requested_provider, model_cfg)
yield _anthropic_env_runtime(requested_provider, model_cfg)
if provider == "bedrock":
return _resolve_bedrock_runtime(requested_provider, model_cfg, target_model)
yield _resolve_bedrock_runtime(requested_provider, model_cfg, target_model)
pconfig = PROVIDER_REGISTRY.get(provider)
if pconfig and pconfig.auth_type == "api_key":
return _api_key_provider_runtime(provider, pconfig, requested_provider, model_cfg, target_model)
return _openrouter_fallback(requested_provider, explicit_api_key, explicit_base_url)
yield _api_key_provider_runtime(provider, pconfig, requested_provider, model_cfg, target_model)
yield _openrouter_fallback(requested_provider, explicit_api_key, explicit_base_url)
def format_runtime_provider_error(error: Exception) -> str: