fix(model-picker): OpenAI shows curated models; OpenRouter no longer phantom-shows (#37404)
The model picker now matches `hermes model` for OpenAI, and OpenRouter
stops appearing as authenticated when only OPENAI_API_KEY is set.
- models.py: provider_model_ids() for the default api.openai.com endpoint
intersects the live /v1/models dump (120+ entries incl. embeddings,
whisper, tts, dall-e, moderation, legacy chat) with the curated agentic
list, preserving curated order. Custom OpenAI-compatible endpoints keep
the live list verbatim so discovery still works.
- providers.py: drop extra_env_vars=("OPENAI_API_KEY",) from the openrouter
overlay. list_authenticated_providers reads extra_env_vars to decide
whether a provider is authenticated, so any OpenAI user saw a phantom
OpenRouter row. Runtime OpenRouter credential resolution still falls back
to OPENAI_API_KEY (runtime_provider.py), independent of the overlay.
- Regression tests for both paths.
This commit is contained in:
@@ -2106,9 +2106,32 @@ def provider_model_ids(provider: Optional[str], *, force_refresh: bool = False)
|
||||
if api_key:
|
||||
base_raw = os.getenv("OPENAI_BASE_URL", "").strip().rstrip("/")
|
||||
base = base_raw or "https://api.openai.com/v1"
|
||||
# Custom OpenAI-compatible endpoints (proxies, gateways, self-hosted)
|
||||
# may serve a small curated catalog — use the live list verbatim so
|
||||
# discovery works. But the canonical api.openai.com /v1/models dump
|
||||
# is 120+ entries of embeddings, whisper, tts, dall-e, moderation and
|
||||
# legacy chat models — none of which belong in the agent model picker.
|
||||
# For the default endpoint, intersect the live list with our curated
|
||||
# agentic catalog so ``/model`` matches what ``hermes model`` shows.
|
||||
is_default_openai = base.rstrip("/") in (
|
||||
"https://api.openai.com/v1",
|
||||
"https://api.openai.com",
|
||||
)
|
||||
try:
|
||||
live = fetch_api_models(api_key, base)
|
||||
if live:
|
||||
if is_default_openai:
|
||||
live_lower = {m.lower() for m in live}
|
||||
curated = list(_PROVIDER_MODELS.get(normalized, []))
|
||||
# Keep curated order; only surface curated models the
|
||||
# account actually has access to.
|
||||
filtered = [m for m in curated if m.lower() in live_lower]
|
||||
if filtered:
|
||||
return filtered
|
||||
# Account serves none of the curated models (rare —
|
||||
# e.g. org without GPT-5 access). Fall back to curated
|
||||
# so the picker still offers sane defaults.
|
||||
return curated or live
|
||||
return live
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user