feat(proxy): show model_list_return_wildcard_routes in the admin UI, add e2e

Adding the field to _GENERAL_SETTINGS_CONFIG_LIST_FIELD_TYPES makes /config/list
return it, so the General Settings page renders it as a toggle that saves through
/config/field/update. The e2e test flips it on through that route, checks every
replica lists the wildcard route in /v1/models and still leaves it out for
return_wildcard_routes=false, then deletes it and checks the listing goes back.
This commit is contained in:
shrey kharbanda 2026-09-28 13:40:56 -07:00
parent b841d9a6c2
commit 811f1510ec
4 changed files with 130 additions and 0 deletions

View file

@ -18038,6 +18038,7 @@ _GENERAL_SETTINGS_CONFIG_LIST_FIELD_TYPES: Final[Mapping[str, str]] = MappingPro
"apply_user_budget_to_team_keys": "Boolean",
"user_api_key_cache_max_size": "Integer",
"transcribe_media_buckets": "List",
"model_list_return_wildcard_routes": "Boolean",
}
)

View file

@ -72,6 +72,7 @@
- {id: mgmt.callback.list.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "callback_management_endpoints.py", rationale: "Callback config (smoke)"}
- {id: mgmt.cost_tracking.estimate.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "cost_tracking_settings.py", rationale: "Cost estimate (smoke)"}
- {id: mgmt.router_settings.update.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "router_settings_endpoints.py", rationale: "Router config (smoke)"}
- {id: mgmt.general_settings.model_list_return_wildcard_routes.persists, module: mgmt, tier: P1, surface: api, assertions: [persists], source: "proxy_server.py:model_list", rationale: "The admin-set default lists wildcard routes in /v1/models on every replica, and return_wildcard_routes=false still leaves them out"}
- {id: mgmt.jwt_key_mapping.new.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "jwt_key_mapping_endpoints.py", rationale: "JWT->key mapping (smoke)"}
- {id: mgmt.compliance.gdpr.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "compliance_endpoints.py", rationale: "GDPR ops (smoke)"}
- {id: mgmt.tool_management.list.happy_path, module: mgmt, tier: P2, surface: api, assertions: [happy_path], source: "tool_management_endpoints.py", rationale: "Tool inventory (smoke)"}

View file

@ -0,0 +1,95 @@
"""Live e2e: `general_settings.model_list_return_wildcard_routes`, the proxy-wide
default for whether GET /v1/models lists a wildcard route such as `openai/*` next to
the models it expands to.
The setting is written through /config/field/update, the route behind the admin UI's
General Settings toggle, and deleted on teardown so the shared proxy goes back to
leaving wildcard routes out. Every other listing the suites run passes
return_wildcard_routes explicitly, so the window with the setting on changes none of
them. The wildcard deployment gets a unique prefix instead of `openai/*`, which would
claim every `openai/...` request the other suites send.
"""
from __future__ import annotations
from typing import Final
import pytest
from pydantic import BaseModel
from e2e_config import unique_marker
from e2e_http import NoBody, Success, unwrap
from lifecycle import ResourceManager
from management_client import ManagementClient
from models import LiteLLMParamsBody, ModelsListParams, ModelsListResponse
pytestmark = pytest.mark.e2e
_SETTING: Final = "model_list_return_wildcard_routes"
_DUMMY_API_KEY: Final = "e2e-dummy-key"
class ConfigFieldUpdateBody(BaseModel):
field_name: str
field_value: bool
config_type: str = "general_settings"
class ConfigFieldDeleteBody(BaseModel):
field_name: str
config_type: str = "general_settings"
def _write_setting(client: ManagementClient, enabled: bool) -> None:
_ = unwrap(
client.proxy.transport.post(
"/config/field/update",
headers=client.proxy.transport.master,
json=ConfigFieldUpdateBody(field_name=_SETTING, field_value=enabled),
response_type=NoBody,
)
)
def _delete_setting(client: ManagementClient) -> None:
_ = unwrap(
client.proxy.transport.post(
"/config/field/delete",
headers=client.proxy.transport.master,
json=ConfigFieldDeleteBody(field_name=_SETTING),
response_type=NoBody,
)
)
def _await_listing(client: ManagementClient, pattern: str, query: BaseModel, *, listed: bool) -> None:
"""Poll /v1/models under `query` on every replica until each one lists `pattern`,
or each one leaves it out, per `listed`."""
_ = client.proxy.read_back_everywhere(
"/v1/models",
params=query,
response_type=ModelsListResponse,
converged=lambda result: (
isinstance(result, Success) and any(entry.id == pattern for entry in result.data.data) is listed
),
)
class TestModelListWildcardRoutesSetting:
@pytest.mark.covers("mgmt.general_settings.model_list_return_wildcard_routes.persists")
def test_setting_lists_wildcard_route_unless_request_opts_out(
self, client: ManagementClient, resources: ResourceManager
) -> None:
pattern = f"e2e-wildcard-{unique_marker()}/*"
model_id = client.proxy.create_model(pattern, LiteLLMParamsBody(model="openai/*", api_key=_DUMMY_API_KEY))
resources.defer(lambda: client.proxy.delete_model(model_id))
_await_listing(client, pattern, NoBody(), listed=False)
resources.defer(lambda: _delete_setting(client))
_write_setting(client, True)
_await_listing(client, pattern, NoBody(), listed=True)
_await_listing(client, pattern, ModelsListParams(return_wildcard_routes=False), listed=False)
_delete_setting(client)
_await_listing(client, pattern, NoBody(), listed=False)

View file

@ -3169,3 +3169,36 @@ async def test_model_list_leaves_out_wildcard_routes_unless_enabled(
assert "gpt-4o" in listed, listed
assert "openai/*" not in listed, listed
@pytest.mark.asyncio
async def test_model_list_return_wildcard_routes_is_an_admin_ui_toggle(openai_wildcard_router, monkeypatch):
stored_general_settings: Final = {"model_list_return_wildcard_routes": True}
config_table: Final = MagicMock()
config_table.find_first = AsyncMock(
side_effect=lambda where: (
MagicMock(param_value=stored_general_settings) if where["param_name"] == "general_settings" else None
)
)
ui_prisma_client: Final = MagicMock()
ui_prisma_client.db.litellm_config = config_table
monkeypatch.setattr(litellm.proxy.proxy_server, "general_settings", {})
monkeypatch.setattr(litellm.proxy.proxy_server, "proxy_config", ProxyConfig())
await litellm.proxy.proxy_server.proxy_config._update_general_settings(stored_general_settings)
monkeypatch.setattr(litellm.proxy.proxy_server, "prisma_client", ui_prisma_client)
ui_fields: Final = {
field.field_name: field
for field in await litellm.proxy.proxy_server.get_config_list(
config_type="general_settings",
user_api_key_dict=UserAPIKeyAuth(api_key="sk-test", user_role=LitellmUserRoles.PROXY_ADMIN),
)
}
monkeypatch.setattr(litellm.proxy.proxy_server, "prisma_client", None)
response: Final = await litellm.proxy.proxy_server.model_list(
user_api_key_dict=UserAPIKeyAuth(api_key="sk-test", user_role=LitellmUserRoles.INTERNAL_USER)
)
toggle: Final = ui_fields["model_list_return_wildcard_routes"]
assert (toggle.field_type, toggle.field_value, toggle.stored_in_db) == ("Boolean", True, True)
assert "openai/*" in [model["id"] for model in response["data"]]