nesquena/hermes-webui

WebUI model picker doesn't show custom `providers:` entry that uses `key_env` (CLI `hermes model` does)

Open

#6,804 opened on Aug 6, 2026

 (1 comment) (0 reactions) (0 assignees)Python (2,386 forks)github user discovery
bugcli-parityhelp wantedux

Repository metrics

Stars
 (17,368 stars)
PR merge metrics
 (Avg merge 14h 31m) (314 merged PRs in 30d)

Description

Summary

A custom OpenAI-compatible provider configured via the providers: section in config.yaml with key_env (instead of an inline api_key) is invisible in the WebUI model picker, but appears correctly in the CLI hermes model picker. Adding a models: list to the config entry works around it.

Reproduction

config.yaml:

providers:
  synthetic:
    name: synthetic
    base_url: https://api.synthetic.new/openai/v1
    key_env: SYNTHETIC_API_KEY

.env:

SYNTHETIC_API_KEY=***
  • CLI: hermes model → synthetic appears as a picker row with its live model list. ✅
  • WebUI: Models page / composer model dropdown → synthetic does not appear. ❌

Root cause (two independent gaps)

1. api/providers.py::_provider_has_key() ignores key_env for providers.<id> entries.

The function checks (in order): known env var from _PROVIDER_ENV_VAR static table → model.api_key (only if active) → providers.<id>.api_key (inline only) → custom_providers[].api_key. It never resolves the key_env field on a providers: entry. So the provider reports has_key: false.

The custom_providers[] legacy list path does resolve ${VAR} env refs in api_key (line ~1148), but the providers: dict path (line ~1305-1310) only reads a bare api_key string — no key_env lookup.

2. api/config.py::get_available_models() only live-probes the active provider.

Non-active providers: entries with no models: list in config get zero models. The final group filter (config.py:~5440) drops any group with no models unless its provider_id starts with custom: or it's a plugin provider. A bare slug like synthetic is neither, so it's dropped.

The CLI path (hermes_cli/model_switch.py::list_authenticated_providers, section 3) probes /v1/models for every providers: entry — which is why the CLI picker works.

Workaround

Add a models: list to the provider entry:

providers:
  synthetic:
    name: synthetic
    base_url: https://api.synthetic.new/openai/v1
    key_env: SYNTHETIC_API_KEY
    models:
      - syn:large:text
      - hf:moonshotai/Kimi-K3
      # ...

The WebUI reads the configured list without a live probe, so the group surfaces. Confirmed working.

Suggested fix

Gap 1: In _provider_has_key() (api/providers.py), when checking providers.<id>, also resolve key_envos.environ / .env file, matching what the custom_providers[] path already does for ${VAR} refs.

Gap 2: In get_available_models() (api/config.py), live-probe /v1/models for non-active providers: entries that have a base_url but no models: list — or relax the final group filter to keep providers:-derived groups with zero models (matching how custom:* slugs are kept).

Environment

  • Hermes Agent: current main
  • Provider: custom OpenAI-compatible endpoint (https://api.synthetic.new/openai/v1)
  • Config shape: providers: dict with key_env (not legacy custom_providers: list, not inline api_key)

Copy-paste ready. The two gaps are independently fixable; gap 1 alone fixes the "invisible provider" symptom, gap 2 fixes the "no models until you hand-list them" symptom.

Contributor guide