mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
refactor: remove provider tier system — flat picker in hermes model (#9303)
Remove the two-tier (top/extended) provider picker that hid most providers behind a 'More providers...' submenu. All providers now appear in a single flat list. - Remove tier field from ProviderEntry namedtuple - Remove tier values from all CANONICAL_PROVIDERS entries - Flatten the hermes model picker (no more 'More...' submenu) - Move 'Custom endpoint' to the bottom of the main list
This commit is contained in:
parent
f324222b79
commit
f6626fccee
2 changed files with 32 additions and 62 deletions
|
|
@ -1044,10 +1044,8 @@ def select_provider_and_model(args=None):
|
||||||
print(f" Active provider: {active_label}")
|
print(f" Active provider: {active_label}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# Step 1: Provider selection — top providers shown first, rest behind "More..."
|
# Step 1: Provider selection — flat list from CANONICAL_PROVIDERS
|
||||||
# Derived from CANONICAL_PROVIDERS (single source of truth)
|
all_providers = [(p.slug, p.tui_desc) for p in CANONICAL_PROVIDERS]
|
||||||
top_providers = [(p.slug, p.tui_desc) for p in CANONICAL_PROVIDERS if p.tier == "top"]
|
|
||||||
extended_providers = [(p.slug, p.tui_desc) for p in CANONICAL_PROVIDERS if p.tier == "extended"]
|
|
||||||
|
|
||||||
def _named_custom_provider_map(cfg) -> dict[str, dict[str, str]]:
|
def _named_custom_provider_map(cfg) -> dict[str, dict[str, str]]:
|
||||||
custom_provider_map = {}
|
custom_provider_map = {}
|
||||||
|
|
@ -1084,29 +1082,22 @@ def select_provider_and_model(args=None):
|
||||||
short_url = base_url.replace("https://", "").replace("http://", "").rstrip("/")
|
short_url = base_url.replace("https://", "").replace("http://", "").rstrip("/")
|
||||||
saved_model = provider_info.get("model", "")
|
saved_model = provider_info.get("model", "")
|
||||||
model_hint = f" — {saved_model}" if saved_model else ""
|
model_hint = f" — {saved_model}" if saved_model else ""
|
||||||
top_providers.append((key, f"{name} ({short_url}){model_hint}"))
|
all_providers.append((key, f"{name} ({short_url}){model_hint}"))
|
||||||
|
|
||||||
top_keys = {k for k, _ in top_providers}
|
# Build the menu
|
||||||
extended_keys = {k for k, _ in extended_providers}
|
|
||||||
|
|
||||||
# If the active provider is in the extended list, promote it into top
|
|
||||||
if active and active in extended_keys:
|
|
||||||
promoted = [(k, l) for k, l in extended_providers if k == active]
|
|
||||||
extended_providers = [(k, l) for k, l in extended_providers if k != active]
|
|
||||||
top_providers = promoted + top_providers
|
|
||||||
top_keys.add(active)
|
|
||||||
|
|
||||||
# Build the primary menu
|
|
||||||
ordered = []
|
ordered = []
|
||||||
default_idx = 0
|
default_idx = 0
|
||||||
for key, label in top_providers:
|
for key, label in all_providers:
|
||||||
if active and key == active:
|
if active and key == active:
|
||||||
ordered.append((key, f"{label} ← currently active"))
|
ordered.append((key, f"{label} ← currently active"))
|
||||||
default_idx = len(ordered) - 1
|
default_idx = len(ordered) - 1
|
||||||
else:
|
else:
|
||||||
ordered.append((key, label))
|
ordered.append((key, label))
|
||||||
|
|
||||||
ordered.append(("more", "More providers..."))
|
ordered.append(("custom", "Custom endpoint (enter URL manually)"))
|
||||||
|
_has_saved_custom_list = isinstance(config.get("custom_providers"), list) and bool(config.get("custom_providers"))
|
||||||
|
if _has_saved_custom_list:
|
||||||
|
ordered.append(("remove-custom", "Remove a saved custom provider"))
|
||||||
ordered.append(("cancel", "Cancel"))
|
ordered.append(("cancel", "Cancel"))
|
||||||
|
|
||||||
provider_idx = _prompt_provider_choice(
|
provider_idx = _prompt_provider_choice(
|
||||||
|
|
@ -1118,23 +1109,6 @@ def select_provider_and_model(args=None):
|
||||||
|
|
||||||
selected_provider = ordered[provider_idx][0]
|
selected_provider = ordered[provider_idx][0]
|
||||||
|
|
||||||
# "More providers..." — show the extended list
|
|
||||||
if selected_provider == "more":
|
|
||||||
ext_ordered = list(extended_providers)
|
|
||||||
ext_ordered.append(("custom", "Custom endpoint (enter URL manually)"))
|
|
||||||
_has_saved_custom_list = isinstance(config.get("custom_providers"), list) and bool(config.get("custom_providers"))
|
|
||||||
if _has_saved_custom_list:
|
|
||||||
ext_ordered.append(("remove-custom", "Remove a saved custom provider"))
|
|
||||||
ext_ordered.append(("cancel", "Cancel"))
|
|
||||||
|
|
||||||
ext_idx = _prompt_provider_choice(
|
|
||||||
[label for _, label in ext_ordered], default=0,
|
|
||||||
)
|
|
||||||
if ext_idx is None or ext_ordered[ext_idx][0] == "cancel":
|
|
||||||
print("No change.")
|
|
||||||
return
|
|
||||||
selected_provider = ext_ordered[ext_idx][0]
|
|
||||||
|
|
||||||
# Step 2: Provider-specific setup + model selection
|
# Step 2: Provider-specific setup + model selection
|
||||||
if selected_provider == "openrouter":
|
if selected_provider == "openrouter":
|
||||||
_model_flow_openrouter(config, current_model)
|
_model_flow_openrouter(config, current_model)
|
||||||
|
|
|
||||||
|
|
@ -498,43 +498,39 @@ def check_nous_free_tier() -> bool:
|
||||||
# Fields:
|
# Fields:
|
||||||
# slug — internal provider ID (used in config.yaml, --provider flag)
|
# slug — internal provider ID (used in config.yaml, --provider flag)
|
||||||
# label — short display name
|
# label — short display name
|
||||||
# tier — "top" (shown first) or "extended" (behind "More...")
|
|
||||||
# tui_desc — longer description for the `hermes model` interactive picker
|
# tui_desc — longer description for the `hermes model` interactive picker
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
class ProviderEntry(NamedTuple):
|
class ProviderEntry(NamedTuple):
|
||||||
slug: str
|
slug: str
|
||||||
label: str
|
label: str
|
||||||
tier: str # "top" or "extended"
|
|
||||||
tui_desc: str # detailed description for `hermes model` TUI
|
tui_desc: str # detailed description for `hermes model` TUI
|
||||||
|
|
||||||
|
|
||||||
CANONICAL_PROVIDERS: list[ProviderEntry] = [
|
CANONICAL_PROVIDERS: list[ProviderEntry] = [
|
||||||
# -- Top tier (shown by default) --
|
ProviderEntry("nous", "Nous Portal", "Nous Portal (Nous Research subscription)"),
|
||||||
ProviderEntry("nous", "Nous Portal", "top", "Nous Portal (Nous Research subscription)"),
|
ProviderEntry("openrouter", "OpenRouter", "OpenRouter (100+ models, pay-per-use)"),
|
||||||
ProviderEntry("openrouter", "OpenRouter", "top", "OpenRouter (100+ models, pay-per-use)"),
|
ProviderEntry("anthropic", "Anthropic", "Anthropic (Claude models — API key or Claude Code)"),
|
||||||
ProviderEntry("anthropic", "Anthropic", "top", "Anthropic (Claude models — API key or Claude Code)"),
|
ProviderEntry("openai-codex", "OpenAI Codex", "OpenAI Codex"),
|
||||||
ProviderEntry("openai-codex", "OpenAI Codex", "top", "OpenAI Codex"),
|
ProviderEntry("qwen-oauth", "Qwen OAuth (Portal)", "Qwen OAuth (reuses local Qwen CLI login)"),
|
||||||
ProviderEntry("qwen-oauth", "Qwen OAuth (Portal)", "top", "Qwen OAuth (reuses local Qwen CLI login)"),
|
ProviderEntry("copilot", "GitHub Copilot", "GitHub Copilot (uses GITHUB_TOKEN or gh auth token)"),
|
||||||
ProviderEntry("copilot", "GitHub Copilot", "top", "GitHub Copilot (uses GITHUB_TOKEN or gh auth token)"),
|
ProviderEntry("copilot-acp", "GitHub Copilot ACP", "GitHub Copilot ACP (spawns `copilot --acp --stdio`)"),
|
||||||
ProviderEntry("huggingface", "Hugging Face", "top", "Hugging Face Inference Providers (20+ open models)"),
|
ProviderEntry("huggingface", "Hugging Face", "Hugging Face Inference Providers (20+ open models)"),
|
||||||
# -- Extended tier (behind "More..." in hermes model) --
|
ProviderEntry("gemini", "Google AI Studio", "Google AI Studio (Gemini models — OpenAI-compatible endpoint)"),
|
||||||
ProviderEntry("copilot-acp", "GitHub Copilot ACP", "extended", "GitHub Copilot ACP (spawns `copilot --acp --stdio`)"),
|
ProviderEntry("deepseek", "DeepSeek", "DeepSeek (DeepSeek-V3, R1, coder — direct API)"),
|
||||||
ProviderEntry("gemini", "Google AI Studio", "extended", "Google AI Studio (Gemini models — OpenAI-compatible endpoint)"),
|
ProviderEntry("xai", "xAI", "xAI (Grok models — direct API)"),
|
||||||
ProviderEntry("deepseek", "DeepSeek", "extended", "DeepSeek (DeepSeek-V3, R1, coder — direct API)"),
|
ProviderEntry("zai", "Z.AI / GLM", "Z.AI / GLM (Zhipu AI direct API)"),
|
||||||
ProviderEntry("xai", "xAI", "extended", "xAI (Grok models — direct API)"),
|
ProviderEntry("kimi-coding", "Kimi / Moonshot", "Kimi / Moonshot (Moonshot AI direct API)"),
|
||||||
ProviderEntry("zai", "Z.AI / GLM", "extended", "Z.AI / GLM (Zhipu AI direct API)"),
|
ProviderEntry("kimi-coding-cn", "Kimi / Moonshot (China)", "Kimi / Moonshot China (Moonshot CN direct API)"),
|
||||||
ProviderEntry("kimi-coding", "Kimi / Moonshot", "extended", "Kimi / Moonshot (Moonshot AI direct API)"),
|
ProviderEntry("minimax", "MiniMax", "MiniMax (global direct API)"),
|
||||||
ProviderEntry("kimi-coding-cn", "Kimi / Moonshot (China)", "extended", "Kimi / Moonshot China (Moonshot CN direct API)"),
|
ProviderEntry("minimax-cn", "MiniMax (China)", "MiniMax China (domestic direct API)"),
|
||||||
ProviderEntry("minimax", "MiniMax", "extended", "MiniMax (global direct API)"),
|
ProviderEntry("alibaba", "Alibaba Cloud (DashScope)","Alibaba Cloud / DashScope Coding (Qwen + multi-provider)"),
|
||||||
ProviderEntry("minimax-cn", "MiniMax (China)", "extended", "MiniMax China (domestic direct API)"),
|
ProviderEntry("xiaomi", "Xiaomi MiMo", "Xiaomi MiMo (MiMo-V2 models — pro, omni, flash)"),
|
||||||
ProviderEntry("kilocode", "Kilo Code", "extended", "Kilo Code (Kilo Gateway API)"),
|
ProviderEntry("arcee", "Arcee AI", "Arcee AI (Trinity models — direct API)"),
|
||||||
ProviderEntry("opencode-zen", "OpenCode Zen", "extended", "OpenCode Zen (35+ curated models, pay-as-you-go)"),
|
ProviderEntry("kilocode", "Kilo Code", "Kilo Code (Kilo Gateway API)"),
|
||||||
ProviderEntry("opencode-go", "OpenCode Go", "extended", "OpenCode Go (open models, $10/month subscription)"),
|
ProviderEntry("opencode-zen", "OpenCode Zen", "OpenCode Zen (35+ curated models, pay-as-you-go)"),
|
||||||
ProviderEntry("ai-gateway", "AI Gateway", "extended", "AI Gateway (Vercel — 200+ models, pay-per-use)"),
|
ProviderEntry("opencode-go", "OpenCode Go", "OpenCode Go (open models, $10/month subscription)"),
|
||||||
ProviderEntry("alibaba", "Alibaba Cloud (DashScope)","extended", "Alibaba Cloud / DashScope Coding (Qwen + multi-provider)"),
|
ProviderEntry("ai-gateway", "AI Gateway", "AI Gateway (Vercel — 200+ models, pay-per-use)"),
|
||||||
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
|
# Derived dicts — used throughout the codebase
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue