From c3eafa34f84a1145ed8061bf0ff67af4d7a9edd7 Mon Sep 17 00:00:00 2001 From: Michael Lam Date: Wed, 20 May 2026 03:09:01 -0700 Subject: [PATCH] fix: surface custom provider model endpoint errors --- CHANGELOG.md | 3 + api/config.py | 75 ++++++++--- .../2540/model-endpoint-error-hint.png | Bin 0 -> 55998 bytes static/style.css | 1 + static/ui.js | 30 ++++- tests/test_issue2540_models_endpoint_error.py | 124 ++++++++++++++++++ 6 files changed, 215 insertions(+), 18 deletions(-) create mode 100644 docs/pr-media/2540/model-endpoint-error-hint.png create mode 100644 tests/test_issue2540_models_endpoint_error.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 185f15f1..51ed4b39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Fixed + +- **PR #2626** by @Michaelyklam (closes #2540) — Surface named custom-provider `/models` endpoint failures in the model picker instead of silently showing an empty provider group. `/api/models` now carries a per-provider `models_endpoint_error` hint for auth, network, and HTTP failures while preserving configured fallback models. ## [v0.51.95] — 2026-05-20 — Release BS (stage-388 — 5-PR batch — live tool callback event dedup + browser-only dashboard links + messaging transcript merge alignment + Geist Contrast skin + SSE runtime diagnostics) diff --git a/api/config.py b/api/config.py index 9edc12bb..225c8e06 100644 --- a/api/config.py +++ b/api/config.py @@ -3169,18 +3169,45 @@ def get_available_models() -> dict: models.append({"id": model_id, "label": label}) return models + def _custom_endpoint_error( + provider: str, + exc: Exception, + *, + code: int | None = None, + ) -> dict: + provider_label = str(provider or "custom").replace("custom:", "") + status_code = code if code is not None else getattr(exc, "code", None) + if status_code in (401, 403): + return { + "kind": "auth", + "code": int(status_code), + "message": f"Models endpoint returned {status_code} — check the API key for {provider_label}.", + } + if isinstance(status_code, int): + return { + "kind": "http", + "code": int(status_code), + "message": f"Models endpoint returned {status_code} for {provider_label}; see logs.", + } + return { + "kind": "network", + "code": None, + "message": f"Models endpoint unreachable for {provider_label}; verify base_url.", + } + def _read_custom_endpoint_models( base_url: object, provider: str, *, api_key: object = "", trusted_base_urls: tuple[object, ...] = (), - ) -> list[dict]: + ) -> tuple[list[dict], dict | None]: base = str(base_url or "").strip() if not base: - return [] + return [], None try: import ipaddress + import urllib.error import urllib.request import socket @@ -3226,10 +3253,15 @@ def get_available_models() -> dict: req.add_header(k, v) with urllib.request.urlopen(req, timeout=10) as response: # nosec B310 data = json.loads(response.read().decode("utf-8")) - return _extract_model_entries_from_payload(data, provider) - except Exception: - logger.debug("Custom endpoint unreachable or misconfigured for provider: %s", provider) - return [] + return _extract_model_entries_from_payload(data, provider), None + except urllib.error.HTTPError as exc: + error = _custom_endpoint_error(provider, exc, code=getattr(exc, "code", None)) + logger.debug("Custom endpoint models fetch failed for provider %s: %s", provider, error) + return [], error + except Exception as exc: + error = _custom_endpoint_error(provider, exc) + logger.debug("Custom endpoint unreachable or misconfigured for provider %s: %s", provider, error) + return [], error # 4. Fetch models from custom endpoint if base_url is configured auto_detected_models = [] @@ -3300,12 +3332,13 @@ def get_available_models() -> dict: for _cp in _custom_providers_for_trust if isinstance(_cp, dict) and _cp.get("base_url") ) - for auto_model in _read_custom_endpoint_models( + _active_endpoint_models, _active_endpoint_error = _read_custom_endpoint_models( base_url, provider, api_key=api_key, trusted_base_urls=tuple(_trusted_custom_bases), - ): + ) + for auto_model in _active_endpoint_models: auto_detected_models.append(auto_model) provider_key = provider.lower() auto_detected_models_by_provider.setdefault(provider_key, []).append(auto_model) @@ -3313,6 +3346,7 @@ def get_available_models() -> dict: _custom_providers_cfg = cfg.get("custom_providers", []) _named_custom_groups: dict = {} + _named_custom_errors: dict[str, dict] = {} if isinstance(_custom_providers_cfg, list): _seen_custom_ids = set() for _cp in _custom_providers_cfg: @@ -3330,12 +3364,18 @@ def get_available_models() -> dict: _cp_key_env = str(_cp.get("key_env") or "").strip() if _cp_key_env: _cp_api_key = str(os.getenv(_cp_key_env) or "").strip() - _live_models = auto_detected_models_by_provider.get(_slug) or _read_custom_endpoint_models( - _cp_base_url, - _slug, - api_key=_cp_api_key, - trusted_base_urls=(_cp_base_url,), - ) + _live_models = auto_detected_models_by_provider.get(_slug) + _live_error = None + if _live_models is None: + _live_models, _live_error = _read_custom_endpoint_models( + _cp_base_url, + _slug, + api_key=_cp_api_key, + trusted_base_urls=(_cp_base_url,), + ) + if _live_error: + _named_custom_errors[_slug] = _live_error + detected_providers.add(_slug) for _live_model in _live_models: _live_id = str(_live_model.get("id") or "").strip() if not _live_id: @@ -3471,8 +3511,11 @@ def get_available_models() -> dict: # changes (e.g. supporting model-less custom_providers entries). if not _nc_models: _nc_models = auto_detected_models_by_provider.get(pid, []) - if _nc_models: - groups.append({"provider": _nc_display, "provider_id": pid, "models": _nc_models}) + if _nc_models or pid in _named_custom_errors: + group = {"provider": _nc_display, "provider_id": pid, "models": _nc_models} + if pid in _named_custom_errors: + group["models_endpoint_error"] = _named_custom_errors[pid] + groups.append(group) continue provider_name = _PROVIDER_DISPLAY.get(pid, pid.title()) if pid == "openrouter": diff --git a/docs/pr-media/2540/model-endpoint-error-hint.png b/docs/pr-media/2540/model-endpoint-error-hint.png new file mode 100644 index 0000000000000000000000000000000000000000..65c459d9bafeb205aaa7bbbfc5a7f9af38cafb78 GIT binary patch literal 55998 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 const badgeMap=window._configuredModelBadges||{}; @@ -964,6 +967,11 @@ async function populateModelDropdown(){ const og=document.createElement('optgroup'); og.label=g.provider; if(g.provider_id) og.dataset.provider=g.provider_id; + if(g.models_endpoint_error){ + const errorKey=g.provider_id||g.provider||''; + og.dataset.modelsEndpointError=JSON.stringify(g.models_endpoint_error); + if(errorKey) window._modelEndpointErrors[errorKey]=g.models_endpoint_error; + } for(const m of (Array.isArray(g.models)?g.models:[])){ const opt=document.createElement('option'); opt.value=m.id; @@ -1218,12 +1226,19 @@ function renderModelDropdown(){ for(const child of Array.from(sel.children)){ if(child.tagName==='OPTGROUP'){ const providerId=child.dataset&&child.dataset.provider?child.dataset.provider:''; + let modelsEndpointError=null; + if(child.dataset&&child.dataset.modelsEndpointError){ + try{ modelsEndpointError=JSON.parse(child.dataset.modelsEndpointError); }catch(_e){ modelsEndpointError=null; } + } for(const opt of Array.from(child.children)){ const rawValue=String(opt.value||''); const displayName=rawValue.startsWith('@custom:') ? getModelLabel(rawValue) : (opt.textContent||getModelLabel(rawValue)); - _modelData.push({value:opt.value,name:esc(displayName),id:esc(opt.value),group:child.label||'',badge:_getConfiguredModelBadge(opt.value,_badgeMap,providerId)}); + _modelData.push({value:opt.value,name:esc(displayName),id:esc(opt.value),group:child.label||'',providerId,modelsEndpointError,badge:_getConfiguredModelBadge(opt.value,_badgeMap,providerId)}); + } + if(modelsEndpointError && !child.children.length){ + _modelData.push({value:`__models_endpoint_error__:${providerId||child.label||''}`,name:'',id:'',group:child.label||'',providerId,modelsEndpointError,endpointErrorOnly:true}); } } if(child.tagName==='OPTION'){ @@ -1362,8 +1377,17 @@ function renderModelDropdown(){ const _groupCounts={}; for(const m of _modelData){ if(configuredIds.has(m.value)) continue; - if(m.group) _groupCounts[m.group]=(_groupCounts[m.group]||0)+1; + if(m.group&&!m.endpointErrorOnly) _groupCounts[m.group]=(_groupCounts[m.group]||0)+1; } + const _renderProviderEndpointHint=(groupName)=>{ + if(!groupName) return; + const entry=_modelData.find(m=>m.group===groupName&&m.modelsEndpointError); + if(!entry||!entry.modelsEndpointError) return; + const hint=document.createElement('div'); + hint.className='model-provider-hint'; + hint.textContent=entry.modelsEndpointError.message||'Models endpoint could not be reached for this provider.'; + dd.appendChild(hint); + }; for(const m of _modelData){ if(configuredIds.has(m.value)||!matches(m)) continue; if(m.group&&m.group!==_lastGroup){ @@ -1372,8 +1396,10 @@ function renderModelDropdown(){ const count=_groupCounts[m.group]||0; heading.textContent=count>1?`${m.group} (${count})`:m.group; dd.appendChild(heading); + _renderProviderEndpointHint(m.group); _lastGroup=m.group; } + if(m.endpointErrorOnly) continue; const row=document.createElement('div'); row.className='model-opt'+(m.value===sel.value?' active':''); const badgeHtml=m.badge?`${esc(m.badge.label||'Configured')}`:''; diff --git a/tests/test_issue2540_models_endpoint_error.py b/tests/test_issue2540_models_endpoint_error.py new file mode 100644 index 00000000..89dce0a0 --- /dev/null +++ b/tests/test_issue2540_models_endpoint_error.py @@ -0,0 +1,124 @@ +import urllib.error +import urllib.request +from email.message import Message + +from api import config + + +class _ConfigState: + def __enter__(self): + self.old_cfg = config.cfg + self.old_mtime = config._cfg_mtime + self.old_cache = config._available_models_cache + self.old_cache_ts = config._available_models_cache_ts + self.old_cache_fp = config._available_models_cache_source_fingerprint + config._cfg_mtime = 0.0 + config._available_models_cache = None + config._available_models_cache_ts = 0.0 + config._available_models_cache_source_fingerprint = None + return self + + def __exit__(self, exc_type, exc, tb): + config.cfg = self.old_cfg + config._cfg_mtime = self.old_mtime + config._available_models_cache = self.old_cache + config._available_models_cache_ts = self.old_cache_ts + config._available_models_cache_source_fingerprint = self.old_cache_fp + return False + + +def _configure_named_custom_provider(tmp_path, monkeypatch, *, model=None): + monkeypatch.setattr(config, "_models_cache_path", tmp_path / "models_cache.json") + monkeypatch.setattr(config, "_get_auth_store_path", lambda: tmp_path / "auth.json") + entry = { + "name": "Broken Proxy", + "base_url": "https://broken.example/v1", + "api_key": "bad-key", + } + if model: + entry["model"] = model + config.cfg = { + "model": {"provider": "openai-codex", "default": "gpt-5.5"}, + "providers": {}, + "fallback_providers": [], + "custom_providers": [entry], + } + + +def _groups_by_provider(data): + return {group["provider_id"]: group for group in data["groups"]} + + +def test_named_custom_provider_models_endpoint_401_surfaces_error(monkeypatch, tmp_path): + def fake_urlopen(req, timeout=10): + raise urllib.error.HTTPError( + getattr(req, "full_url", "https://broken.example/v1/models"), + 401, + "Unauthorized", + hdrs=Message(), + fp=None, + ) + + monkeypatch.setattr(urllib.request, "urlopen", fake_urlopen) + + with _ConfigState(): + _configure_named_custom_provider(tmp_path, monkeypatch, model="broken/manual") + data = config.get_available_models() + + group = _groups_by_provider(data)["custom:broken-proxy"] + error = group["models_endpoint_error"] + assert error["kind"] == "auth" + assert error["code"] == 401 + assert "check the API key" in error["message"] + assert "broken-proxy" in error["message"] + assert "@custom:broken-proxy:broken/manual" in {m["id"] for m in group["models"]} + + +def test_named_custom_provider_models_endpoint_network_error_surfaces_empty_group(monkeypatch, tmp_path): + def fake_urlopen(req, timeout=10): + raise urllib.error.URLError("connection refused") + + monkeypatch.setattr(urllib.request, "urlopen", fake_urlopen) + + with _ConfigState(): + _configure_named_custom_provider(tmp_path, monkeypatch) + data = config.get_available_models() + + group = _groups_by_provider(data)["custom:broken-proxy"] + assert group["models"] == [] + assert group["models_endpoint_error"]["kind"] == "network" + assert group["models_endpoint_error"]["code"] is None + assert "verify base_url" in group["models_endpoint_error"]["message"] + + +def test_named_custom_provider_models_endpoint_5xx_preserves_status(monkeypatch, tmp_path): + def fake_urlopen(req, timeout=10): + raise urllib.error.HTTPError( + getattr(req, "full_url", "https://broken.example/v1/models"), + 502, + "Bad Gateway", + hdrs=Message(), + fp=None, + ) + + monkeypatch.setattr(urllib.request, "urlopen", fake_urlopen) + + with _ConfigState(): + _configure_named_custom_provider(tmp_path, monkeypatch, model="broken/manual") + data = config.get_available_models() + + error = _groups_by_provider(data)["custom:broken-proxy"]["models_endpoint_error"] + assert error["kind"] == "http" + assert error["code"] == 502 + assert "returned 502" in error["message"] + + +def test_frontend_model_picker_renders_provider_endpoint_hint(): + ui = open("static/ui.js", encoding="utf-8").read() + css = open("static/style.css", encoding="utf-8").read() + + assert "models_endpoint_error" in ui + assert "dataset.modelsEndpointError" in ui + assert "model-provider-hint" in ui + assert "entry.modelsEndpointError.message" in ui + assert ".model-provider-hint" in css