Merge pull request #3025

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
nesquena-hermes 2026-05-28 01:02:57 +00:00
commit 43cdd7cc37
3 changed files with 27 additions and 1 deletions

View file

@ -6,6 +6,7 @@
### Fixed
- Dark-mode panel header save buttons now use a theme-aware foreground token, keeping workspace and other detail-pane check icons visible on the default gold accent. (#2998)
- Custom provider `/v1/models` discovery now uses a short per-endpoint timeout and gracefully skips slow or unreachable providers, reducing cold `/api/models` cache rebuild latency. (#3024)
## [v0.51.145] — 2026-05-26 — Release DQ (stage-batch27 — sidebar running-state preservation)

View file

@ -70,6 +70,12 @@ PROJECTS_FILE = STATE_DIR / "projects.json"
logger = logging.getLogger(__name__)
# Keep custom provider /v1/models probes below the frontend's generic request
# timeout even when one upstream is slow or unreachable. The models cache rebuild
# path probes configured custom endpoints serially, so each provider needs a
# short hard cap and graceful degradation.
CUSTOM_MODELS_ENDPOINT_TIMEOUT_SECONDS = 5.0
def _env_mb_bytes(name: str, default_mb: int) -> int:
"""Parse an optional megabyte environment variable into bytes.
@ -3653,7 +3659,7 @@ def get_available_models() -> dict:
req.add_header("User-Agent", "OpenAI/Python 1.0")
for k, v in headers.items():
req.add_header(k, v)
with urllib.request.urlopen(req, timeout=10) as response: # nosec B310
with urllib.request.urlopen(req, timeout=CUSTOM_MODELS_ENDPOINT_TIMEOUT_SECONDS) as response: # nosec B310
data = json.loads(response.read().decode("utf-8"))
return _extract_model_entries_from_payload(data, provider), None
except urllib.error.HTTPError as exc:

View file

@ -112,6 +112,25 @@ def test_named_custom_provider_models_endpoint_5xx_preserves_status(monkeypatch,
assert error["code"] == 502
assert "returned 502" in error["message"]
def test_named_custom_provider_models_endpoint_network_error_uses_short_timeout(monkeypatch, tmp_path):
observed_timeouts = []
def fake_urlopen(req, timeout=10):
observed_timeouts.append(timeout)
raise urllib.error.URLError("timed out")
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 observed_timeouts == [config.CUSTOM_MODELS_ENDPOINT_TIMEOUT_SECONDS]
assert max(observed_timeouts) <= 5.0
def test_frontend_model_picker_renders_provider_endpoint_hint():
ui = open("static/ui.js", encoding="utf-8").read()