mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 05:15:31 +00:00
feat(model-switch): excluded_providers config to hide providers from /model picker
This commit is contained in:
parent
1b56d0d1a2
commit
b239ee2123
7 changed files with 245 additions and 1 deletions
|
|
@ -52,6 +52,7 @@ class ConfigContext:
|
|||
current_base_url: str
|
||||
user_providers: dict
|
||||
custom_providers: list
|
||||
excluded_providers: list = None
|
||||
|
||||
def with_overrides(
|
||||
self,
|
||||
|
|
@ -96,12 +97,14 @@ def load_picker_context() -> ConfigContext:
|
|||
current_provider = ""
|
||||
current_base_url = ""
|
||||
raw = cfg.get("providers")
|
||||
excluded = cfg.get("model_catalog", {}).get("excluded_providers") or []
|
||||
return ConfigContext(
|
||||
current_provider=current_provider,
|
||||
current_model=current_model,
|
||||
current_base_url=current_base_url,
|
||||
user_providers=raw if isinstance(raw, dict) else {},
|
||||
custom_providers=get_compatible_custom_providers(cfg),
|
||||
excluded_providers=excluded if isinstance(excluded, list) else [],
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -179,6 +182,7 @@ def build_models_payload(
|
|||
refresh=refresh,
|
||||
probe_custom_providers=probe_custom_providers,
|
||||
probe_current_custom_provider=probe_current_custom_provider,
|
||||
excluded_providers=ctx.excluded_providers or [],
|
||||
)
|
||||
|
||||
moa_row = _moa_provider_row(ctx.current_provider)
|
||||
|
|
|
|||
|
|
@ -3167,6 +3167,7 @@ def select_provider_and_model(args=None):
|
|||
from hermes_cli.models import (
|
||||
CANONICAL_PROVIDERS,
|
||||
_PROVIDER_LABELS,
|
||||
_PROVIDER_ALIASES,
|
||||
group_providers,
|
||||
provider_group_for_slug,
|
||||
)
|
||||
|
|
@ -3190,7 +3191,30 @@ def select_provider_and_model(args=None):
|
|||
# resolves back to a concrete slug, so the dispatch chain below is
|
||||
# unchanged. Custom providers and the trailing actions stay flat.
|
||||
canonical_descs = {p.slug: p.tui_desc for p in CANONICAL_PROVIDERS}
|
||||
grouped_rows = group_providers([p.slug for p in CANONICAL_PROVIDERS])
|
||||
# Honor ``model_catalog.excluded_providers`` so the CLI ``hermes model``
|
||||
# picker hides the same providers the gateway/TUI pickers do. A canonical
|
||||
# provider is hidden if its slug OR any of its aliases appears in the
|
||||
# exclusion list (case-insensitive), matching list_authenticated_providers'
|
||||
# matching against hermes_id / alias / canonical slug.
|
||||
_cli_excluded = {
|
||||
str(p).strip().lower()
|
||||
for p in (config.get("model_catalog", {}) or {}).get("excluded_providers") or []
|
||||
if p
|
||||
}
|
||||
if _cli_excluded:
|
||||
_alias_to_canon = _PROVIDER_ALIASES
|
||||
_names_for: dict[str, set[str]] = {}
|
||||
for _p in CANONICAL_PROVIDERS:
|
||||
_names_for[_p.slug] = {_p.slug.lower()}
|
||||
for _alias, _canon in _alias_to_canon.items():
|
||||
_names_for.setdefault(_canon, {_canon.lower()}).add(_alias.lower())
|
||||
_visible_slugs = [
|
||||
p.slug for p in CANONICAL_PROVIDERS
|
||||
if not _names_for.get(p.slug, {p.slug.lower()}) & _cli_excluded
|
||||
]
|
||||
else:
|
||||
_visible_slugs = [p.slug for p in CANONICAL_PROVIDERS]
|
||||
grouped_rows = group_providers(_visible_slugs)
|
||||
|
||||
# The group/slug that should be pre-selected: the active provider's group
|
||||
# if it's grouped, otherwise the active slug itself.
|
||||
|
|
|
|||
|
|
@ -1628,6 +1628,7 @@ def prewarm_picker_cache_async() -> Optional["_threading.Thread"]:
|
|||
current_model=ctx.current_model,
|
||||
user_providers=ctx.user_providers,
|
||||
custom_providers=ctx.custom_providers,
|
||||
excluded_providers=ctx.excluded_providers or [],
|
||||
)
|
||||
except Exception:
|
||||
# Best-effort warmup — never surface errors into the session.
|
||||
|
|
@ -1651,6 +1652,7 @@ def list_authenticated_providers(
|
|||
probe_custom_providers: bool = True,
|
||||
probe_current_custom_provider: bool = False,
|
||||
for_picker: bool = False,
|
||||
excluded_providers: list | None = None,
|
||||
) -> List[dict]:
|
||||
"""Detect which providers have credentials and list their curated models.
|
||||
|
||||
|
|
@ -1722,6 +1724,11 @@ def list_authenticated_providers(
|
|||
def _can_probe_custom_provider(*, row_is_current: bool) -> bool:
|
||||
return bool(probe_custom_providers or (probe_current_custom_provider and row_is_current))
|
||||
|
||||
# Normalize the excluded-providers list once for fast membership checks.
|
||||
# Compared against hermes_id / mdev_id (section 1), pid / hermes_slug
|
||||
# (section 2) and canonical slug (section 2b) so a single entry like
|
||||
# ``copilot`` hides the provider regardless of which key it surfaces under.
|
||||
_excluded: set = {str(p).strip().lower() for p in (excluded_providers or []) if p}
|
||||
# Effective base URLs of every built-in row we emit (normalized lower+rstrip).
|
||||
# Section 4 uses this to hide ``custom_providers`` entries that point at the
|
||||
# same endpoint as a built-in (e.g. a user-defined "my-dashscope" on
|
||||
|
|
@ -1861,6 +1868,8 @@ def list_authenticated_providers(
|
|||
# The first one with valid credentials wins (#10526).
|
||||
if mdev_id in seen_mdev_ids:
|
||||
continue
|
||||
if hermes_id.lower() in _excluded or mdev_id.lower() in _excluded:
|
||||
continue
|
||||
pdata = data.get(mdev_id)
|
||||
if not isinstance(pdata, dict):
|
||||
continue
|
||||
|
|
@ -1962,6 +1971,8 @@ def list_authenticated_providers(
|
|||
hermes_slug = _mdev_to_hermes.get(pid, pid)
|
||||
if hermes_slug.lower() in seen_slugs:
|
||||
continue
|
||||
if pid.lower() in _excluded or hermes_slug.lower() in _excluded:
|
||||
continue
|
||||
|
||||
# Check if credentials exist
|
||||
has_creds = False
|
||||
|
|
@ -2131,6 +2142,8 @@ def list_authenticated_providers(
|
|||
for _cp in _canon_provs:
|
||||
if _cp.slug.lower() in seen_slugs:
|
||||
continue
|
||||
if _cp.slug.lower() in _excluded:
|
||||
continue
|
||||
|
||||
# Check credentials via PROVIDER_REGISTRY (auth.py)
|
||||
_cp_config = _auth_registry.get(_cp.slug)
|
||||
|
|
@ -2731,6 +2744,7 @@ def list_picker_providers(
|
|||
max_models: int | None = None,
|
||||
current_model: str = "",
|
||||
include_moa: bool = False,
|
||||
excluded_providers: list | None = None,
|
||||
) -> List[dict]:
|
||||
"""Interactive-picker variant of :func:`list_authenticated_providers`.
|
||||
|
||||
|
|
@ -2761,6 +2775,7 @@ def list_picker_providers(
|
|||
max_models=max_models,
|
||||
current_model=current_model,
|
||||
for_picker=True,
|
||||
excluded_providers=excluded_providers,
|
||||
)
|
||||
if include_moa:
|
||||
providers = _prepend_moa_picker_provider(providers, current_provider=current_provider)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue