feat(providers): post-filter picker by ``enabled: false`` for built-ins

Sections 1-2 of ``list_authenticated_providers`` emit rows directly
from ``PROVIDER_REGISTRY`` (auth-driven built-ins) before reaching the
per-section gate I added for section 3 (user-config providers). That
means flipping ``providers.openrouter.enabled: false`` hid OpenRouter
from a user-config block but the built-in OpenRouter row still showed
because its row came from section 1's auth-status path.

Add a single post-filter at the end of ``list_authenticated_providers``
that drops every row whose ``provider_id`` or ``slug`` matches a
disabled name in ``providers``. Same source of truth, applied once at
the end, covers all four sections in one pass.

Wrapped in ``try/except`` so a degraded config can't break the picker —
if anything fails reading the config, the filter no-ops and the picker
shows the un-filtered list (same as before this PR).
This commit is contained in:
nnnet 2026-06-02 14:47:21 +03:00 committed by Teknium
parent 305ecac8b2
commit 523a64a726
1 changed files with 22 additions and 0 deletions

View File

@ -2701,6 +2701,28 @@ def list_authenticated_providers(
seen_slugs.add(slug.lower())
_section4_emitted_slugs.add(slug.lower())
# Apply final ``providers.<name>.enabled: false`` post-filter — covers
# built-in PROVIDER_REGISTRY rows (sections 1-2) which would otherwise
# bypass the per-section gate. Indexed by lowercase slug AND by
# ``provider_id`` so PROVIDER_REGISTRY entries that match user-config
# blocks are filtered consistently.
try:
from hermes_cli.config import is_provider_enabled
if isinstance(user_providers, dict):
_disabled_slugs = {
str(name).strip().lower()
for name, cfg in user_providers.items()
if isinstance(cfg, dict) and not is_provider_enabled(cfg)
}
if _disabled_slugs:
results = [
r for r in results
if str(r.get("provider_id", "")).strip().lower() not in _disabled_slugs
and str(r.get("slug", "")).strip().lower() not in _disabled_slugs
]
except Exception:
pass
# Surface a custom / uncurated model the user selected via the CLI.
# Each row's model list is its curated/live catalog, so a model the user set
# with `/model <provider>/<uncurated-name>` would otherwise be invisible in