feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
"""
|
|
|
|
|
Single source of truth for provider identity in Hermes Agent.
|
|
|
|
|
|
|
|
|
|
Two data sources, merged at runtime:
|
|
|
|
|
|
|
|
|
|
1. **models.dev catalog** — 109+ providers with base URLs, env vars, display
|
|
|
|
|
names, and full model metadata (context, cost, capabilities). This is
|
|
|
|
|
the primary database.
|
|
|
|
|
|
|
|
|
|
2. **Hermes overlays** — transport type, auth patterns, aggregator flags,
|
|
|
|
|
and additional env vars that models.dev doesn't track. Small dict,
|
|
|
|
|
maintained here.
|
|
|
|
|
|
|
|
|
|
3. **User config** (``providers:`` section in config.yaml) — user-defined
|
|
|
|
|
endpoints and overrides. Merged on top of everything else.
|
|
|
|
|
|
|
|
|
|
Other modules import from this file. No parallel registries.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import logging
|
refactor: codebase-wide lint cleanup — unused imports, dead code, and inefficient patterns (#5821)
Comprehensive cleanup across 80 files based on automated (ruff, pyflakes, vulture)
and manual analysis of the entire codebase.
Changes by category:
Unused imports removed (~95 across 55 files):
- Removed genuinely unused imports from all major subsystems
- agent/, hermes_cli/, tools/, gateway/, plugins/, cron/
- Includes imports in try/except blocks that were truly unused
(vs availability checks which were left alone)
Unused variables removed (~25):
- Removed dead variables: connected, inner, channels, last_exc,
source, new_server_names, verify, pconfig, default_terminal,
result, pending_handled, temperature, loop
- Dropped unused argparse subparser assignments in hermes_cli/main.py
(12 instances of add_parser() where result was never used)
Dead code removed:
- run_agent.py: Removed dead ternary (None if False else None) and
surrounding unreachable branch in identity fallback
- run_agent.py: Removed write-only attribute _last_reported_tool
- hermes_cli/providers.py: Removed dead @property decorator on
module-level function (decorator has no effect outside a class)
- gateway/run.py: Removed unused MCP config load before reconnect
- gateway/platforms/slack.py: Removed dead SessionSource construction
Undefined name bugs fixed (would cause NameError at runtime):
- batch_runner.py: Added missing logger = logging.getLogger(__name__)
- tools/environments/daytona.py: Added missing Dict and Path imports
Unnecessary global statements removed (14):
- tools/terminal_tool.py: 5 functions declared global for dicts
they only mutated via .pop()/[key]=value (no rebinding)
- tools/browser_tool.py: cleanup thread loop only reads flag
- tools/rl_training_tool.py: 4 functions only do dict mutations
- tools/mcp_oauth.py: only reads the global
- hermes_time.py: only reads cached values
Inefficient patterns fixed:
- startswith/endswith tuple form: 15 instances of
x.startswith('a') or x.startswith('b') consolidated to
x.startswith(('a', 'b'))
- len(x)==0 / len(x)>0: 13 instances replaced with pythonic
truthiness checks (not x / bool(x))
- in dict.keys(): 5 instances simplified to in dict
- Redefined unused name: removed duplicate _strip_mdv2 import in
send_message_tool.py
Other fixes:
- hermes_cli/doctor.py: Replaced undefined logger.debug() with pass
- hermes_cli/config.py: Consolidated chained .endswith() calls
Test results: 3934 passed, 17 failed (all pre-existing on main),
19 skipped. Zero regressions.
2026-04-07 10:25:31 -07:00
|
|
|
from dataclasses import dataclass
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
from typing import Any, Dict, List, Optional, Tuple
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -- Hermes overlay ----------------------------------------------------------
|
|
|
|
|
# Hermes-specific metadata that models.dev doesn't provide.
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
class HermesOverlay:
|
|
|
|
|
"""Hermes-specific provider metadata layered on top of models.dev."""
|
|
|
|
|
|
|
|
|
|
transport: str = "openai_chat" # openai_chat | anthropic_messages | codex_responses
|
|
|
|
|
is_aggregator: bool = False
|
|
|
|
|
auth_type: str = "api_key" # api_key | oauth_device_code | oauth_external | external_process
|
|
|
|
|
extra_env_vars: Tuple[str, ...] = () # env vars models.dev doesn't list
|
|
|
|
|
base_url_override: str = "" # override if models.dev URL is wrong/missing
|
|
|
|
|
base_url_env_var: str = "" # env var for user-custom base URL
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HERMES_OVERLAYS: Dict[str, HermesOverlay] = {
|
|
|
|
|
"openrouter": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
is_aggregator=True,
|
|
|
|
|
extra_env_vars=("OPENAI_API_KEY",),
|
|
|
|
|
base_url_env_var="OPENROUTER_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"nous": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
auth_type="oauth_device_code",
|
|
|
|
|
base_url_override="https://inference-api.nousresearch.com/v1",
|
|
|
|
|
),
|
|
|
|
|
"openai-codex": HermesOverlay(
|
|
|
|
|
transport="codex_responses",
|
|
|
|
|
auth_type="oauth_external",
|
|
|
|
|
base_url_override="https://chatgpt.com/backend-api/codex",
|
|
|
|
|
),
|
feat(qwen): add Qwen OAuth provider with portal request support
Based on #6079 by @tunamitom with critical fixes and comprehensive tests.
Changes from #6079:
- Fix: sanitization overwrite bug — Qwen message prep now runs AFTER codex
field sanitization, not before (was silently discarding Qwen transforms)
- Fix: missing try/except AuthError in runtime_provider.py — stale Qwen
credentials now fall through to next provider on auto-detect
- Fix: 'qwen' alias conflict — bare 'qwen' stays mapped to 'alibaba'
(DashScope); use 'qwen-portal' or 'qwen-cli' for the OAuth provider
- Fix: hardcoded ['coder-model'] replaced with live API fetch + curated
fallback list (qwen3-coder-plus, qwen3-coder)
- Fix: extract _is_qwen_portal() helper + _qwen_portal_headers() to replace
5 inline 'portal.qwen.ai' string checks and share headers between init
and credential swap
- Fix: add Qwen branch to _apply_client_headers_for_base_url for mid-session
credential swaps
- Fix: remove suspicious TypeError catch blocks around _prompt_provider_choice
- Fix: handle bare string items in content lists (were silently dropped)
- Fix: remove redundant dict() copies after deepcopy in message prep
- Revert: unrelated ai-gateway test mock removal and model_switch.py comment deletion
New tests (30 test functions):
- _qwen_cli_auth_path, _read_qwen_cli_tokens (success + 3 error paths)
- _save_qwen_cli_tokens (roundtrip, parent creation, permissions)
- _qwen_access_token_is_expiring (5 edge cases: fresh, expired, within skew,
None, non-numeric)
- _refresh_qwen_cli_tokens (success, preserve old refresh, 4 error paths,
default expires_in, disk persistence)
- resolve_qwen_runtime_credentials (fresh, auto-refresh, force-refresh,
missing token, env override)
- get_qwen_auth_status (logged in, not logged in)
- Runtime provider resolution (direct, pool entry, alias)
- _build_api_kwargs (metadata, vl_high_resolution_images, message formatting,
max_tokens suppression)
2026-04-08 20:48:21 +05:30
|
|
|
"qwen-oauth": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
auth_type="oauth_external",
|
|
|
|
|
base_url_override="https://portal.qwen.ai/v1",
|
|
|
|
|
base_url_env_var="HERMES_QWEN_BASE_URL",
|
|
|
|
|
),
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
"copilot-acp": HermesOverlay(
|
|
|
|
|
transport="codex_responses",
|
|
|
|
|
auth_type="external_process",
|
|
|
|
|
base_url_override="acp://copilot",
|
|
|
|
|
base_url_env_var="COPILOT_ACP_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"github-copilot": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
extra_env_vars=("COPILOT_GITHUB_TOKEN", "GH_TOKEN"),
|
|
|
|
|
),
|
|
|
|
|
"anthropic": HermesOverlay(
|
|
|
|
|
transport="anthropic_messages",
|
|
|
|
|
extra_env_vars=("ANTHROPIC_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN"),
|
|
|
|
|
),
|
|
|
|
|
"zai": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
extra_env_vars=("GLM_API_KEY", "ZAI_API_KEY", "Z_AI_API_KEY"),
|
|
|
|
|
base_url_env_var="GLM_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"kimi-for-coding": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
base_url_env_var="KIMI_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"minimax": HermesOverlay(
|
fix: align MiniMax provider with official API docs
Aligns MiniMax provider with official API documentation. Fixes 6 bugs:
transport mismatch (openai_chat -> anthropic_messages), credential leak
in switch_model(), prompt caching sent to non-Anthropic endpoints,
dot-to-hyphen model name corruption, trajectory compressor URL routing,
and stale doctor health check.
Also corrects context window (204,800), thinking support (manual mode),
max output (131,072), and model catalog (M2 family only on /anthropic).
Source: https://platform.minimax.io/docs/api-reference/text-anthropic-api
Co-authored-by: kshitijk4poor <kshitijk4poor@users.noreply.github.com>
2026-04-10 03:53:18 -07:00
|
|
|
transport="anthropic_messages",
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
base_url_env_var="MINIMAX_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"minimax-cn": HermesOverlay(
|
fix: align MiniMax provider with official API docs
Aligns MiniMax provider with official API documentation. Fixes 6 bugs:
transport mismatch (openai_chat -> anthropic_messages), credential leak
in switch_model(), prompt caching sent to non-Anthropic endpoints,
dot-to-hyphen model name corruption, trajectory compressor URL routing,
and stale doctor health check.
Also corrects context window (204,800), thinking support (manual mode),
max output (131,072), and model catalog (M2 family only on /anthropic).
Source: https://platform.minimax.io/docs/api-reference/text-anthropic-api
Co-authored-by: kshitijk4poor <kshitijk4poor@users.noreply.github.com>
2026-04-10 03:53:18 -07:00
|
|
|
transport="anthropic_messages",
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
base_url_env_var="MINIMAX_CN_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"deepseek": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
base_url_env_var="DEEPSEEK_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"alibaba": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
base_url_env_var="DASHSCOPE_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"vercel": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
is_aggregator=True,
|
|
|
|
|
),
|
|
|
|
|
"opencode": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
is_aggregator=True,
|
|
|
|
|
base_url_env_var="OPENCODE_ZEN_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"opencode-go": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
is_aggregator=True,
|
|
|
|
|
base_url_env_var="OPENCODE_GO_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"kilo": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
is_aggregator=True,
|
|
|
|
|
base_url_env_var="KILOCODE_BASE_URL",
|
|
|
|
|
),
|
|
|
|
|
"huggingface": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
is_aggregator=True,
|
|
|
|
|
base_url_env_var="HF_BASE_URL",
|
|
|
|
|
),
|
2026-04-10 12:51:30 +04:00
|
|
|
"xai": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
base_url_override="https://api.x.ai/v1",
|
|
|
|
|
base_url_env_var="XAI_BASE_URL",
|
|
|
|
|
),
|
feat(xiaomi): add Xiaomi MiMo as first-class provider
Cherry-picked from PR #7702 by kshitijk4poor.
Adds Xiaomi MiMo as a direct provider (XIAOMI_API_KEY) with models:
- mimo-v2-pro (1M context), mimo-v2-omni (256K, multimodal), mimo-v2-flash (256K, cheapest)
Standard OpenAI-compatible provider checklist: auth.py, config.py, models.py,
main.py, providers.py, doctor.py, model_normalize.py, model_metadata.py,
models_dev.py, auxiliary_client.py, .env.example, cli-config.yaml.example.
Follow-up: vision tasks use mimo-v2-omni (multimodal) instead of the user's
main model. Non-vision aux uses the user's selected model. Added
_PROVIDER_VISION_MODELS dict for provider-specific vision model overrides.
On failure, falls back to aggregators (gemini flash) via existing fallback chain.
Corrects pre-existing context lengths: mimo-v2-pro 1048576→1000000,
mimo-v2-omni 1048576→256000, adds mimo-v2-flash 256000.
36 tests covering registry, aliases, auto-detect, credentials, models.dev,
normalization, URL mapping, providers module, doctor, aux client, vision
model override, and agent init.
2026-04-11 10:10:31 -07:00
|
|
|
"xiaomi": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
base_url_env_var="XIAOMI_BASE_URL",
|
|
|
|
|
),
|
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.
2026-04-13 17:16:43 -07:00
|
|
|
"arcee": HermesOverlay(
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
base_url_override="https://api.arcee.ai/api/v1",
|
|
|
|
|
base_url_env_var="ARCEE_BASE_URL",
|
|
|
|
|
),
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -- Resolved provider -------------------------------------------------------
|
|
|
|
|
# The merged result of models.dev + overlay + user config.
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
class ProviderDef:
|
|
|
|
|
"""Complete provider definition — merged from all sources."""
|
|
|
|
|
|
|
|
|
|
id: str
|
|
|
|
|
name: str
|
|
|
|
|
transport: str # openai_chat | anthropic_messages | codex_responses
|
|
|
|
|
api_key_env_vars: Tuple[str, ...] # all env vars to check for API key
|
|
|
|
|
base_url: str = ""
|
|
|
|
|
base_url_env_var: str = ""
|
|
|
|
|
is_aggregator: bool = False
|
|
|
|
|
auth_type: str = "api_key"
|
|
|
|
|
doc: str = ""
|
|
|
|
|
source: str = "" # "models.dev", "hermes", "user-config"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -- Aliases ------------------------------------------------------------------
|
|
|
|
|
# Maps human-friendly / legacy names to canonical provider IDs.
|
|
|
|
|
# Uses models.dev IDs where possible.
|
|
|
|
|
|
|
|
|
|
ALIASES: Dict[str, str] = {
|
|
|
|
|
# openrouter
|
|
|
|
|
"openai": "openrouter", # bare "openai" → route through aggregator
|
|
|
|
|
|
|
|
|
|
# zai
|
|
|
|
|
"glm": "zai",
|
|
|
|
|
"z-ai": "zai",
|
|
|
|
|
"z.ai": "zai",
|
|
|
|
|
"zhipu": "zai",
|
|
|
|
|
|
2026-04-10 12:51:30 +04:00
|
|
|
# xai
|
|
|
|
|
"x-ai": "xai",
|
|
|
|
|
"x.ai": "xai",
|
|
|
|
|
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
# kimi-for-coding (models.dev ID)
|
|
|
|
|
"kimi": "kimi-for-coding",
|
|
|
|
|
"kimi-coding": "kimi-for-coding",
|
2026-04-13 11:16:09 -07:00
|
|
|
"kimi-coding-cn": "kimi-for-coding",
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
"moonshot": "kimi-for-coding",
|
|
|
|
|
|
|
|
|
|
# minimax-cn
|
|
|
|
|
"minimax-china": "minimax-cn",
|
|
|
|
|
"minimax_cn": "minimax-cn",
|
|
|
|
|
|
|
|
|
|
# anthropic
|
|
|
|
|
"claude": "anthropic",
|
|
|
|
|
"claude-code": "anthropic",
|
|
|
|
|
|
|
|
|
|
# github-copilot (models.dev ID)
|
|
|
|
|
"copilot": "github-copilot",
|
|
|
|
|
"github": "github-copilot",
|
|
|
|
|
"github-copilot-acp": "copilot-acp",
|
|
|
|
|
|
|
|
|
|
# vercel (models.dev ID for AI Gateway)
|
|
|
|
|
"ai-gateway": "vercel",
|
|
|
|
|
"aigateway": "vercel",
|
|
|
|
|
"vercel-ai-gateway": "vercel",
|
|
|
|
|
|
|
|
|
|
# opencode (models.dev ID for OpenCode Zen)
|
|
|
|
|
"opencode-zen": "opencode",
|
|
|
|
|
"zen": "opencode",
|
|
|
|
|
|
|
|
|
|
# opencode-go
|
|
|
|
|
"go": "opencode-go",
|
|
|
|
|
"opencode-go-sub": "opencode-go",
|
|
|
|
|
|
|
|
|
|
# kilo (models.dev ID for KiloCode)
|
|
|
|
|
"kilocode": "kilo",
|
|
|
|
|
"kilo-code": "kilo",
|
|
|
|
|
"kilo-gateway": "kilo",
|
|
|
|
|
|
|
|
|
|
# deepseek
|
|
|
|
|
"deep-seek": "deepseek",
|
|
|
|
|
|
|
|
|
|
# alibaba
|
|
|
|
|
"dashscope": "alibaba",
|
|
|
|
|
"aliyun": "alibaba",
|
|
|
|
|
"qwen": "alibaba",
|
|
|
|
|
"alibaba-cloud": "alibaba",
|
|
|
|
|
|
|
|
|
|
# huggingface
|
|
|
|
|
"hf": "huggingface",
|
|
|
|
|
"hugging-face": "huggingface",
|
|
|
|
|
"huggingface-hub": "huggingface",
|
|
|
|
|
|
feat(xiaomi): add Xiaomi MiMo as first-class provider
Cherry-picked from PR #7702 by kshitijk4poor.
Adds Xiaomi MiMo as a direct provider (XIAOMI_API_KEY) with models:
- mimo-v2-pro (1M context), mimo-v2-omni (256K, multimodal), mimo-v2-flash (256K, cheapest)
Standard OpenAI-compatible provider checklist: auth.py, config.py, models.py,
main.py, providers.py, doctor.py, model_normalize.py, model_metadata.py,
models_dev.py, auxiliary_client.py, .env.example, cli-config.yaml.example.
Follow-up: vision tasks use mimo-v2-omni (multimodal) instead of the user's
main model. Non-vision aux uses the user's selected model. Added
_PROVIDER_VISION_MODELS dict for provider-specific vision model overrides.
On failure, falls back to aggregators (gemini flash) via existing fallback chain.
Corrects pre-existing context lengths: mimo-v2-pro 1048576→1000000,
mimo-v2-omni 1048576→256000, adds mimo-v2-flash 256000.
36 tests covering registry, aliases, auto-detect, credentials, models.dev,
normalization, URL mapping, providers module, doctor, aux client, vision
model override, and agent init.
2026-04-11 10:10:31 -07:00
|
|
|
# xiaomi
|
|
|
|
|
"mimo": "xiaomi",
|
|
|
|
|
"xiaomi-mimo": "xiaomi",
|
|
|
|
|
|
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.
2026-04-13 17:16:43 -07:00
|
|
|
# arcee
|
|
|
|
|
"arcee-ai": "arcee",
|
|
|
|
|
"arceeai": "arcee",
|
|
|
|
|
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
# Local server aliases → virtual "local" concept (resolved via user config)
|
|
|
|
|
"lmstudio": "lmstudio",
|
|
|
|
|
"lm-studio": "lmstudio",
|
|
|
|
|
"lm_studio": "lmstudio",
|
|
|
|
|
"ollama": "ollama-cloud",
|
|
|
|
|
"vllm": "local",
|
|
|
|
|
"llamacpp": "local",
|
|
|
|
|
"llama.cpp": "local",
|
|
|
|
|
"llama-cpp": "local",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -- Display labels -----------------------------------------------------------
|
|
|
|
|
# Built dynamically from models.dev + overlays. Fallback for providers
|
|
|
|
|
# not in the catalog.
|
|
|
|
|
|
|
|
|
|
_LABEL_OVERRIDES: Dict[str, str] = {
|
|
|
|
|
"nous": "Nous Portal",
|
|
|
|
|
"openai-codex": "OpenAI Codex",
|
|
|
|
|
"copilot-acp": "GitHub Copilot ACP",
|
feat(xiaomi): add Xiaomi MiMo as first-class provider
Cherry-picked from PR #7702 by kshitijk4poor.
Adds Xiaomi MiMo as a direct provider (XIAOMI_API_KEY) with models:
- mimo-v2-pro (1M context), mimo-v2-omni (256K, multimodal), mimo-v2-flash (256K, cheapest)
Standard OpenAI-compatible provider checklist: auth.py, config.py, models.py,
main.py, providers.py, doctor.py, model_normalize.py, model_metadata.py,
models_dev.py, auxiliary_client.py, .env.example, cli-config.yaml.example.
Follow-up: vision tasks use mimo-v2-omni (multimodal) instead of the user's
main model. Non-vision aux uses the user's selected model. Added
_PROVIDER_VISION_MODELS dict for provider-specific vision model overrides.
On failure, falls back to aggregators (gemini flash) via existing fallback chain.
Corrects pre-existing context lengths: mimo-v2-pro 1048576→1000000,
mimo-v2-omni 1048576→256000, adds mimo-v2-flash 256000.
36 tests covering registry, aliases, auto-detect, credentials, models.dev,
normalization, URL mapping, providers module, doctor, aux client, vision
model override, and agent init.
2026-04-11 10:10:31 -07:00
|
|
|
"xiaomi": "Xiaomi MiMo",
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
"local": "Local endpoint",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -- Transport → API mode mapping ---------------------------------------------
|
|
|
|
|
|
|
|
|
|
TRANSPORT_TO_API_MODE: Dict[str, str] = {
|
|
|
|
|
"openai_chat": "chat_completions",
|
|
|
|
|
"anthropic_messages": "anthropic_messages",
|
|
|
|
|
"codex_responses": "codex_responses",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -- Helper functions ---------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
def normalize_provider(name: str) -> str:
|
|
|
|
|
"""Resolve aliases and normalise casing to a canonical provider id.
|
|
|
|
|
|
|
|
|
|
Returns the canonical id string. Does *not* validate that the id
|
|
|
|
|
corresponds to a known provider.
|
|
|
|
|
"""
|
|
|
|
|
key = name.strip().lower()
|
|
|
|
|
return ALIASES.get(key, key)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_provider(name: str) -> Optional[ProviderDef]:
|
|
|
|
|
"""Look up a provider by id or alias, merging all data sources.
|
|
|
|
|
|
|
|
|
|
Resolution order:
|
|
|
|
|
1. Hermes overlays (for providers not in models.dev: nous, openai-codex, etc.)
|
|
|
|
|
2. models.dev catalog + Hermes overlay
|
|
|
|
|
3. User-defined providers from config (TODO: Phase 4)
|
|
|
|
|
|
|
|
|
|
Returns a fully-resolved ProviderDef or None.
|
|
|
|
|
"""
|
|
|
|
|
canonical = normalize_provider(name)
|
|
|
|
|
|
|
|
|
|
# Try to get models.dev data
|
|
|
|
|
try:
|
|
|
|
|
from agent.models_dev import get_provider_info as _mdev_provider
|
|
|
|
|
mdev_info = _mdev_provider(canonical)
|
|
|
|
|
except Exception:
|
|
|
|
|
mdev_info = None
|
|
|
|
|
|
|
|
|
|
overlay = HERMES_OVERLAYS.get(canonical)
|
|
|
|
|
|
|
|
|
|
if mdev_info is not None:
|
|
|
|
|
# Merge models.dev + overlay
|
|
|
|
|
transport = overlay.transport if overlay else "openai_chat"
|
|
|
|
|
is_agg = overlay.is_aggregator if overlay else False
|
|
|
|
|
auth = overlay.auth_type if overlay else "api_key"
|
|
|
|
|
base_url_env = overlay.base_url_env_var if overlay else ""
|
|
|
|
|
base_url_override = overlay.base_url_override if overlay else ""
|
|
|
|
|
|
|
|
|
|
# Combine env vars: models.dev env + hermes extra
|
|
|
|
|
env_vars = list(mdev_info.env)
|
|
|
|
|
if overlay and overlay.extra_env_vars:
|
|
|
|
|
for ev in overlay.extra_env_vars:
|
|
|
|
|
if ev not in env_vars:
|
|
|
|
|
env_vars.append(ev)
|
|
|
|
|
|
|
|
|
|
return ProviderDef(
|
|
|
|
|
id=canonical,
|
|
|
|
|
name=mdev_info.name,
|
|
|
|
|
transport=transport,
|
|
|
|
|
api_key_env_vars=tuple(env_vars),
|
|
|
|
|
base_url=base_url_override or mdev_info.api,
|
|
|
|
|
base_url_env_var=base_url_env,
|
|
|
|
|
is_aggregator=is_agg,
|
|
|
|
|
auth_type=auth,
|
|
|
|
|
doc=mdev_info.doc,
|
|
|
|
|
source="models.dev",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if overlay is not None:
|
|
|
|
|
# Hermes-only provider (not in models.dev)
|
|
|
|
|
return ProviderDef(
|
|
|
|
|
id=canonical,
|
|
|
|
|
name=_LABEL_OVERRIDES.get(canonical, canonical),
|
|
|
|
|
transport=overlay.transport,
|
|
|
|
|
api_key_env_vars=overlay.extra_env_vars,
|
|
|
|
|
base_url=overlay.base_url_override,
|
|
|
|
|
base_url_env_var=overlay.base_url_env_var,
|
|
|
|
|
is_aggregator=overlay.is_aggregator,
|
|
|
|
|
auth_type=overlay.auth_type,
|
|
|
|
|
source="hermes",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_label(provider_id: str) -> str:
|
|
|
|
|
"""Get a human-readable display name for a provider."""
|
|
|
|
|
canonical = normalize_provider(provider_id)
|
|
|
|
|
|
|
|
|
|
# Check label overrides first
|
|
|
|
|
if canonical in _LABEL_OVERRIDES:
|
|
|
|
|
return _LABEL_OVERRIDES[canonical]
|
|
|
|
|
|
|
|
|
|
# Try models.dev
|
|
|
|
|
pdef = get_provider(canonical)
|
|
|
|
|
if pdef:
|
|
|
|
|
return pdef.name
|
|
|
|
|
|
|
|
|
|
return canonical
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-10 12:51:30 +04:00
|
|
|
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
def is_aggregator(provider: str) -> bool:
|
|
|
|
|
"""Return True when the provider is a multi-model aggregator."""
|
|
|
|
|
pdef = get_provider(provider)
|
|
|
|
|
return pdef.is_aggregator if pdef else False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def determine_api_mode(provider: str, base_url: str = "") -> str:
|
|
|
|
|
"""Determine the API mode (wire protocol) for a provider/endpoint.
|
|
|
|
|
|
|
|
|
|
Resolution order:
|
|
|
|
|
1. Known provider → transport → TRANSPORT_TO_API_MODE.
|
|
|
|
|
2. URL heuristics for unknown / custom providers.
|
|
|
|
|
3. Default: 'chat_completions'.
|
|
|
|
|
"""
|
|
|
|
|
pdef = get_provider(provider)
|
|
|
|
|
if pdef is not None:
|
|
|
|
|
return TRANSPORT_TO_API_MODE.get(pdef.transport, "chat_completions")
|
|
|
|
|
|
|
|
|
|
# URL-based heuristics for custom / unknown providers
|
|
|
|
|
if base_url:
|
|
|
|
|
url_lower = base_url.rstrip("/").lower()
|
|
|
|
|
if url_lower.endswith("/anthropic") or "api.anthropic.com" in url_lower:
|
|
|
|
|
return "anthropic_messages"
|
|
|
|
|
if "api.openai.com" in url_lower:
|
|
|
|
|
return "codex_responses"
|
|
|
|
|
|
|
|
|
|
return "chat_completions"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -- Provider from user config ------------------------------------------------
|
|
|
|
|
|
|
|
|
|
def resolve_user_provider(name: str, user_config: Dict[str, Any]) -> Optional[ProviderDef]:
|
|
|
|
|
"""Resolve a provider from the user's config.yaml ``providers:`` section.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
name: Provider name as given by the user.
|
|
|
|
|
user_config: The ``providers:`` dict from config.yaml.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
ProviderDef if found, else None.
|
|
|
|
|
"""
|
|
|
|
|
if not user_config or not isinstance(user_config, dict):
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
entry = user_config.get(name)
|
|
|
|
|
if not isinstance(entry, dict):
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# Extract fields
|
|
|
|
|
display_name = entry.get("name", "") or name
|
|
|
|
|
api_url = entry.get("api", "") or entry.get("url", "") or entry.get("base_url", "") or ""
|
|
|
|
|
key_env = entry.get("key_env", "") or ""
|
|
|
|
|
transport = entry.get("transport", "openai_chat") or "openai_chat"
|
|
|
|
|
|
|
|
|
|
env_vars: List[str] = []
|
|
|
|
|
if key_env:
|
|
|
|
|
env_vars.append(key_env)
|
|
|
|
|
|
|
|
|
|
return ProviderDef(
|
|
|
|
|
id=name,
|
|
|
|
|
name=display_name,
|
|
|
|
|
transport=transport,
|
|
|
|
|
api_key_env_vars=tuple(env_vars),
|
|
|
|
|
base_url=api_url,
|
|
|
|
|
is_aggregator=False,
|
|
|
|
|
auth_type="api_key",
|
|
|
|
|
source="user-config",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-04-10 02:52:56 -07:00
|
|
|
def custom_provider_slug(display_name: str) -> str:
|
|
|
|
|
"""Build a canonical slug for a custom_providers entry.
|
|
|
|
|
|
|
|
|
|
Matches the convention used by runtime_provider and credential_pool
|
|
|
|
|
(``custom:<normalized-name>``). Centralised here so all call-sites
|
|
|
|
|
produce identical slugs.
|
|
|
|
|
"""
|
|
|
|
|
return "custom:" + display_name.strip().lower().replace(" ", "-")
|
|
|
|
|
|
|
|
|
|
|
fix: include custom_providers in /model command listings and resolution
Custom providers defined in config.yaml under were
completely invisible to the /model command in both gateway (Telegram,
Discord, etc.) and CLI. The provider listing skipped them and explicit
switching via --provider failed with "Unknown provider".
Root cause: gateway/run.py, cli.py, and model_switch.py only read the
dict from config, ignoring entirely.
Changes:
- providers.py: add resolve_custom_provider() and extend
resolve_provider_full() to check custom_providers after user_providers
- model_switch.py: propagate custom_providers through switch_model(),
list_authenticated_providers(), and get_authenticated_provider_slugs();
add custom provider section to provider listings
- gateway/run.py: read custom_providers from config, pass to all
model-switch calls
- cli.py: hoist config loading, pass custom_providers to listing and
switch calls
Tests: 4 new regression tests covering listing, resolution, and gateway
command handler. All 71 tests pass.
2026-04-09 22:33:34 +02:00
|
|
|
def resolve_custom_provider(
|
|
|
|
|
name: str,
|
|
|
|
|
custom_providers: Optional[List[Dict[str, Any]]],
|
|
|
|
|
) -> Optional[ProviderDef]:
|
|
|
|
|
"""Resolve a provider from the user's config.yaml ``custom_providers`` list."""
|
|
|
|
|
if not custom_providers or not isinstance(custom_providers, list):
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
requested = (name or "").strip().lower()
|
|
|
|
|
if not requested:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
for entry in custom_providers:
|
|
|
|
|
if not isinstance(entry, dict):
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
display_name = (entry.get("name") or "").strip()
|
|
|
|
|
api_url = (
|
|
|
|
|
entry.get("base_url", "")
|
|
|
|
|
or entry.get("url", "")
|
|
|
|
|
or entry.get("api", "")
|
|
|
|
|
or ""
|
|
|
|
|
).strip()
|
|
|
|
|
if not display_name or not api_url:
|
|
|
|
|
continue
|
|
|
|
|
|
2026-04-10 02:52:56 -07:00
|
|
|
slug = custom_provider_slug(display_name)
|
|
|
|
|
if requested not in {display_name.lower(), slug}:
|
fix: include custom_providers in /model command listings and resolution
Custom providers defined in config.yaml under were
completely invisible to the /model command in both gateway (Telegram,
Discord, etc.) and CLI. The provider listing skipped them and explicit
switching via --provider failed with "Unknown provider".
Root cause: gateway/run.py, cli.py, and model_switch.py only read the
dict from config, ignoring entirely.
Changes:
- providers.py: add resolve_custom_provider() and extend
resolve_provider_full() to check custom_providers after user_providers
- model_switch.py: propagate custom_providers through switch_model(),
list_authenticated_providers(), and get_authenticated_provider_slugs();
add custom provider section to provider listings
- gateway/run.py: read custom_providers from config, pass to all
model-switch calls
- cli.py: hoist config loading, pass custom_providers to listing and
switch calls
Tests: 4 new regression tests covering listing, resolution, and gateway
command handler. All 71 tests pass.
2026-04-09 22:33:34 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
return ProviderDef(
|
|
|
|
|
id=slug,
|
|
|
|
|
name=display_name,
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
api_key_env_vars=(),
|
|
|
|
|
base_url=api_url,
|
|
|
|
|
is_aggregator=False,
|
|
|
|
|
auth_type="api_key",
|
|
|
|
|
source="user-config",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
def resolve_provider_full(
|
|
|
|
|
name: str,
|
|
|
|
|
user_providers: Optional[Dict[str, Any]] = None,
|
fix: include custom_providers in /model command listings and resolution
Custom providers defined in config.yaml under were
completely invisible to the /model command in both gateway (Telegram,
Discord, etc.) and CLI. The provider listing skipped them and explicit
switching via --provider failed with "Unknown provider".
Root cause: gateway/run.py, cli.py, and model_switch.py only read the
dict from config, ignoring entirely.
Changes:
- providers.py: add resolve_custom_provider() and extend
resolve_provider_full() to check custom_providers after user_providers
- model_switch.py: propagate custom_providers through switch_model(),
list_authenticated_providers(), and get_authenticated_provider_slugs();
add custom provider section to provider listings
- gateway/run.py: read custom_providers from config, pass to all
model-switch calls
- cli.py: hoist config loading, pass custom_providers to listing and
switch calls
Tests: 4 new regression tests covering listing, resolution, and gateway
command handler. All 71 tests pass.
2026-04-09 22:33:34 +02:00
|
|
|
custom_providers: Optional[List[Dict[str, Any]]] = None,
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
) -> Optional[ProviderDef]:
|
|
|
|
|
"""Full resolution chain: built-in → models.dev → user config.
|
|
|
|
|
|
|
|
|
|
This is the main entry point for --provider flag resolution.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
name: Provider name or alias.
|
|
|
|
|
user_providers: The ``providers:`` dict from config.yaml (optional).
|
fix: include custom_providers in /model command listings and resolution
Custom providers defined in config.yaml under were
completely invisible to the /model command in both gateway (Telegram,
Discord, etc.) and CLI. The provider listing skipped them and explicit
switching via --provider failed with "Unknown provider".
Root cause: gateway/run.py, cli.py, and model_switch.py only read the
dict from config, ignoring entirely.
Changes:
- providers.py: add resolve_custom_provider() and extend
resolve_provider_full() to check custom_providers after user_providers
- model_switch.py: propagate custom_providers through switch_model(),
list_authenticated_providers(), and get_authenticated_provider_slugs();
add custom provider section to provider listings
- gateway/run.py: read custom_providers from config, pass to all
model-switch calls
- cli.py: hoist config loading, pass custom_providers to listing and
switch calls
Tests: 4 new regression tests covering listing, resolution, and gateway
command handler. All 71 tests pass.
2026-04-09 22:33:34 +02:00
|
|
|
custom_providers: The ``custom_providers:`` list from config.yaml (optional).
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
ProviderDef if found, else None.
|
|
|
|
|
"""
|
|
|
|
|
canonical = normalize_provider(name)
|
|
|
|
|
|
|
|
|
|
# 1. Built-in (models.dev + overlays)
|
|
|
|
|
pdef = get_provider(canonical)
|
|
|
|
|
if pdef is not None:
|
|
|
|
|
return pdef
|
|
|
|
|
|
|
|
|
|
# 2. User-defined providers from config
|
|
|
|
|
if user_providers:
|
|
|
|
|
# Try canonical name
|
|
|
|
|
user_pdef = resolve_user_provider(canonical, user_providers)
|
|
|
|
|
if user_pdef is not None:
|
|
|
|
|
return user_pdef
|
|
|
|
|
# Try original name (in case alias didn't match)
|
|
|
|
|
user_pdef = resolve_user_provider(name.strip().lower(), user_providers)
|
|
|
|
|
if user_pdef is not None:
|
|
|
|
|
return user_pdef
|
|
|
|
|
|
fix: include custom_providers in /model command listings and resolution
Custom providers defined in config.yaml under were
completely invisible to the /model command in both gateway (Telegram,
Discord, etc.) and CLI. The provider listing skipped them and explicit
switching via --provider failed with "Unknown provider".
Root cause: gateway/run.py, cli.py, and model_switch.py only read the
dict from config, ignoring entirely.
Changes:
- providers.py: add resolve_custom_provider() and extend
resolve_provider_full() to check custom_providers after user_providers
- model_switch.py: propagate custom_providers through switch_model(),
list_authenticated_providers(), and get_authenticated_provider_slugs();
add custom provider section to provider listings
- gateway/run.py: read custom_providers from config, pass to all
model-switch calls
- cli.py: hoist config loading, pass custom_providers to listing and
switch calls
Tests: 4 new regression tests covering listing, resolution, and gateway
command handler. All 71 tests pass.
2026-04-09 22:33:34 +02:00
|
|
|
# 2b. Saved custom providers from config
|
|
|
|
|
custom_pdef = resolve_custom_provider(name, custom_providers)
|
|
|
|
|
if custom_pdef is not None:
|
|
|
|
|
return custom_pdef
|
|
|
|
|
|
feat: /model command — models.dev primary database + --provider flag (#5181)
Full overhaul of the model/provider system.
## What changed
- models.dev (109 providers, 4000+ models) as primary database for provider identity AND model metadata
- --provider flag replaces colon syntax for explicit provider switching
- Full ModelInfo/ProviderInfo dataclasses with context, cost, capabilities, modalities
- HermesOverlay system merges models.dev + Hermes-specific transport/auth/aggregator flags
- User-defined endpoints via config.yaml providers: section
- /model (no args) lists authenticated providers with curated model catalog
- Rich metadata display: context window, max output, cost/M tokens, capabilities
- Config migration: custom_providers list → providers dict (v11→v12)
- AIAgent.switch_model() for in-place model swap preserving conversation
## Files
agent/models_dev.py, hermes_cli/providers.py, hermes_cli/model_switch.py,
hermes_cli/model_normalize.py, cli.py, gateway/run.py, run_agent.py,
hermes_cli/config.py, hermes_cli/commands.py
2026-04-05 01:04:44 -07:00
|
|
|
# 3. Try models.dev directly (for providers not in our ALIASES)
|
|
|
|
|
try:
|
|
|
|
|
from agent.models_dev import get_provider_info as _mdev_provider
|
|
|
|
|
mdev_info = _mdev_provider(canonical)
|
|
|
|
|
if mdev_info is not None:
|
|
|
|
|
return ProviderDef(
|
|
|
|
|
id=canonical,
|
|
|
|
|
name=mdev_info.name,
|
|
|
|
|
transport="openai_chat",
|
|
|
|
|
api_key_env_vars=mdev_info.env,
|
|
|
|
|
base_url=mdev_info.api,
|
|
|
|
|
source="models.dev",
|
|
|
|
|
)
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
return None
|