diff --git a/routes/model_routes.py b/routes/model_routes.py index ab1126d3f..fcf9e1634 100644 --- a/routes/model_routes.py +++ b/routes/model_routes.py @@ -1351,14 +1351,14 @@ def _legacy_visible_api_models(ep) -> List[str]: def _picker_models_for_endpoint(ep, base_url: str, kind: str): """Return model IDs that should appear in the picker for an endpoint. - API providers expose remote inventory from /v1/models. Treat that cache as - inventory, not approval: only manually pinned API models should appear in - the picker. Local/self-hosted endpoints keep the older hide-list behavior. + API providers expose remote inventory from /v1/models. Default to that + visible inventory until an explicit pinned-model allow-list is saved. + Local/self-hosted endpoints keep the older hide-list behavior. """ pinned = _normalize_model_ids(getattr(ep, "pinned_models", None)) if _picker_requires_pinning(base_url, kind): if not _has_explicit_pinned_models(ep): - pinned = _legacy_visible_api_models(ep) if _hidden_model_ids(ep) else [] + pinned = _legacy_visible_api_models(ep) return pinned, pinned return _visible_models( _cached_model_ids(ep), diff --git a/tests/test_model_routes.py b/tests/test_model_routes.py index 3c0e7c599..d5a5b0fde 100644 --- a/tests/test_model_routes.py +++ b/tests/test_model_routes.py @@ -1107,7 +1107,7 @@ def test_get_api_models_marks_picker_as_pinned_only(monkeypatch): assert by_id["anthropic/claude-sonnet-4"]["is_pinned"] is False -def test_get_fresh_api_models_does_not_mark_cached_inventory_as_pinned(monkeypatch): +def test_get_fresh_api_models_marks_cached_inventory_as_pinned(monkeypatch): ep = _make_endpoint( base_url="https://api.example.test/v1", cached_models=json.dumps(["openai/gpt-image-1", "anthropic/claude-sonnet-4"]), @@ -1125,7 +1125,7 @@ def test_get_fresh_api_models_does_not_mark_cached_inventory_as_pinned(monkeypat "anthropic/claude-sonnet-4", ] assert all(row["picker_requires_pinning"] is True for row in result) - assert all(row["is_pinned"] is False for row in result) + assert all(row["is_pinned"] is True for row in result) def test_get_legacy_api_models_preserves_hidden_selection_as_pinned(monkeypatch): @@ -1307,7 +1307,7 @@ def test_list_model_endpoints_returns_key_fingerprint(monkeypatch): assert result[1]["api_key_fingerprint"] == "" -def test_list_api_endpoint_reports_inventory_count_when_none_pinned(monkeypatch): +def test_list_api_endpoint_defaults_inventory_to_visible_when_none_pinned(monkeypatch): ep = _make_endpoint( base_url="https://api.example.test/v1", cached_models=json.dumps(["openai/gpt-image-1", "anthropic/claude-sonnet-4"]), @@ -1320,12 +1320,31 @@ def test_list_api_endpoint_reports_inventory_count_when_none_pinned(monkeypatch) result = endpoint(_PinnedFakeRequest()) - assert result[0]["models"] == [] + assert result[0]["models"] == [ + "openai/gpt-image-1", + "anthropic/claude-sonnet-4", + ] + assert result[0]["pinned_models"] == result[0]["models"] assert result[0]["model_count"] == 2 assert result[0]["picker_requires_pinning"] is True assert result[0]["status"] == "online" +def test_api_picker_preserves_explicit_empty_selection(): + ep = _make_endpoint( + base_url="https://api.example.test/v1", + cached_models=json.dumps(["openai/gpt-image-1", "anthropic/claude-sonnet-4"]), + pinned_models=json.dumps([]), + ) + + models, pinned = model_routes._picker_models_for_endpoint( + ep, ep.base_url, ep.endpoint_kind + ) + + assert models == [] + assert pinned == [] + + def test_list_api_endpoint_returns_pinned_picker_models(monkeypatch): ep = _make_endpoint( base_url="https://api.example.test/v1", @@ -1808,7 +1827,7 @@ def test_api_models_openrouter_uses_pinned_models_not_hidden(monkeypatch): assert result["items"][0]["models"] == ["openai/gpt-image-1"] -def test_api_models_openrouter_derives_legacy_visible_models(monkeypatch): +def test_api_models_openrouter_defaults_cached_models_visible(monkeypatch): row = _route_ep( "openrouter", "https://openrouter.ai/api/v1", @@ -1816,7 +1835,6 @@ def test_api_models_openrouter_derives_legacy_visible_models(monkeypatch): pinned_models=None, api_key="fake-key", ) - row.hidden_models = json.dumps(["m2"]) db = _RouteDb([row]) router = model_routes.setup_model_routes(model_discovery=None) @@ -1829,7 +1847,7 @@ def test_api_models_openrouter_derives_legacy_visible_models(monkeypatch): result = _route_endpoint(router, "/api/models")(_route_request()) assert result["items"][0]["endpoint_name"] == "openrouter" - assert result["items"][0]["models"] == ["m1", "m3"] + assert result["items"][0]["models"] == ["m1", "m2", "m3"] @pytest.mark.asyncio