mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-03 07:23:33 +00:00
Merge f518cfb5fc into a392a39627
This commit is contained in:
commit
7907a67150
4 changed files with 86 additions and 7 deletions
|
|
@ -11517,6 +11517,15 @@ class ProxyStartupEvent:
|
|||
replace_existing=True,
|
||||
misfire_grace_time=APSCHEDULER_MISFIRE_GRACE_TIME,
|
||||
)
|
||||
scheduler.add_job( # pyright: ignore[reportUnknownMemberType] # apscheduler is untyped
|
||||
sync_ui_settings_to_general_settings,
|
||||
"interval",
|
||||
seconds=config_reload_interval_seconds,
|
||||
args=[prisma_client],
|
||||
id="sync_ui_settings_job",
|
||||
replace_existing=True,
|
||||
misfire_grace_time=APSCHEDULER_MISFIRE_GRACE_TIME,
|
||||
)
|
||||
# DB-backed MCP servers are live objects in every mode, so the registry refresh that
|
||||
# store_model_in_db=True deployments get via the add_deployment job must run here
|
||||
# too; without it, a server whose OAuth discovery failed at startup is rebuilt only
|
||||
|
|
|
|||
|
|
@ -1911,7 +1911,6 @@ async def update_ui_settings(
|
|||
from litellm.proxy.proxy_server import (
|
||||
create_config_audit_log,
|
||||
prisma_client,
|
||||
store_model_in_db,
|
||||
)
|
||||
|
||||
if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN:
|
||||
|
|
@ -1923,12 +1922,6 @@ async def update_ui_settings(
|
|||
detail={"error": "Database not connected. Please connect a database."},
|
||||
)
|
||||
|
||||
if store_model_in_db is not True:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail={"error": "Set `'STORE_MODEL_IN_DB='True'` in your env to enable this feature."},
|
||||
)
|
||||
|
||||
conflicting_keys: Final = sorted(
|
||||
key
|
||||
for key, value in settings_body.items()
|
||||
|
|
|
|||
|
|
@ -952,6 +952,54 @@ async def test_periodic_reload_job_scheduled_without_store_model_in_db(monkeypat
|
|||
scheduler.shutdown(wait=False)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_settings_resync_job_scheduled_without_store_model_in_db(monkeypatch):
|
||||
"""A UI setting saved through one pod must reach the others without a restart when store_model_in_db is off"""
|
||||
monkeypatch.delenv("DISABLE_PRISMA_SCHEMA_UPDATE", raising=False)
|
||||
monkeypatch.delenv("STORE_MODEL_IN_DB", raising=False)
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
|
||||
from litellm.proxy.proxy_server import ProxyStartupEvent
|
||||
from litellm.proxy.utils import ProxyLogging
|
||||
|
||||
mock_prisma_client: Final = MagicMock()
|
||||
mock_prisma_client.db.litellm_config.find_first = AsyncMock(return_value=None)
|
||||
mock_prisma_client.db.litellm_uisettings.find_unique = AsyncMock(return_value=None)
|
||||
mock_proxy_logging: Final = MagicMock(spec=ProxyLogging)
|
||||
mock_proxy_logging.slack_alerting_instance = MagicMock()
|
||||
mock_proxy_logging.db_spend_update_writer = MagicMock()
|
||||
general_settings: Final[dict[str, object]] = {}
|
||||
scheduler: Final = AsyncIOScheduler()
|
||||
|
||||
try:
|
||||
with (
|
||||
patch("litellm.proxy.proxy_server.proxy_config", _mock_scheduled_proxy_config()),
|
||||
patch("litellm.proxy.proxy_server.store_model_in_db", False),
|
||||
patch("litellm.proxy.proxy_server.get_secret_bool", return_value=False),
|
||||
patch("litellm.proxy.proxy_server.general_settings", general_settings),
|
||||
patch("litellm.proxy.proxy_server.AsyncIOScheduler", return_value=scheduler),
|
||||
):
|
||||
await ProxyStartupEvent.initialize_scheduled_background_jobs(
|
||||
general_settings={},
|
||||
prisma_client=mock_prisma_client,
|
||||
proxy_budget_rescheduler_min_time=1,
|
||||
proxy_budget_rescheduler_max_time=2,
|
||||
proxy_batch_write_at=5,
|
||||
proxy_logging_obj=mock_proxy_logging,
|
||||
)
|
||||
job: Final = scheduler.get_job("sync_ui_settings_job")
|
||||
assert job is not None, [j.id for j in scheduler.get_jobs()]
|
||||
|
||||
mock_prisma_client.db.litellm_uisettings.find_unique.return_value = types.SimpleNamespace(
|
||||
ui_settings={"disable_model_add_for_internal_users": True}
|
||||
)
|
||||
await job.func(*job.args)
|
||||
|
||||
assert general_settings == {"disable_model_add_for_internal_users": True}
|
||||
finally:
|
||||
scheduler.shutdown(wait=False)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_initialize_scheduled_jobs_registers_cleanup_when_retention_lives_only_in_the_db(monkeypatch):
|
||||
"""With no config file, the startup DB sync rebinds general_settings to a store holding the
|
||||
|
|
|
|||
|
|
@ -1663,6 +1663,35 @@ class TestProxySettingEndpoints:
|
|||
stored_settings = json.loads(create_data["ui_settings"])
|
||||
assert stored_settings["disable_model_add_for_internal_users"] is True
|
||||
|
||||
def test_update_ui_settings_works_without_store_model_in_db(self, monkeypatch):
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
|
||||
|
||||
app.dependency_overrides[user_api_key_auth] = lambda: UserAPIKeyAuth(
|
||||
user_id="test-user-123", user_role=LitellmUserRoles.PROXY_ADMIN
|
||||
)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False)
|
||||
mock_prisma: Final = MagicMock()
|
||||
mock_prisma.db.litellm_uisettings.upsert = AsyncMock()
|
||||
mock_prisma.db.litellm_uisettings.find_unique = AsyncMock(return_value=None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma)
|
||||
payload: Final = {
|
||||
"enabled_ui_pages_internal_users": ["api-keys"],
|
||||
"team_admin_editable_team_fields": ["max_budget"],
|
||||
}
|
||||
|
||||
try:
|
||||
response: Final = client.patch("/update/ui_settings", json=payload)
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json()["settings"] == payload
|
||||
upsert_data: Final = mock_prisma.db.litellm_uisettings.upsert.call_args.kwargs["data"]
|
||||
assert json.loads(upsert_data["create"]["ui_settings"]) == payload
|
||||
|
||||
def test_update_ui_settings_ignores_non_allowlisted_value(
|
||||
self, mock_auth, monkeypatch
|
||||
):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue