mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-12 08:42:18 +00:00
fix(proxy): skip a non-object general_settings row on reload and remove a stored copy of a config-owned litellm_settings field
This commit is contained in:
parent
f700a8867b
commit
edc32593e9
3 changed files with 56 additions and 9 deletions
|
|
@ -5926,6 +5926,10 @@ class ProxyConfig:
|
|||
store.apply_db_row(cast(DbRow, section_name), wrote_section)
|
||||
await invalidate_config_param(section_name)
|
||||
|
||||
def config_owns(self, *, section_name: Section, key: str) -> bool:
|
||||
store: Final = self._settings_stores.get(section_name)
|
||||
return store is not None and store.owned_by_config(key)
|
||||
|
||||
def reject_config_owned_deletes(self, *, section_name: str, keys: tuple[str, ...]) -> None:
|
||||
"""Refuse a delete of a setting the config file owns; unlike a write, the value never makes it allowed."""
|
||||
store: Final = self._settings_stores.get(cast(Section, section_name))
|
||||
|
|
@ -8211,9 +8215,9 @@ class ProxyConfig:
|
|||
general_settings.get("maximum_spend_logs_retention_interval", "1d"),
|
||||
)
|
||||
|
||||
async def _update_general_settings(self, db_general_settings: Mapping[str, SettingsJsonValue] | None) -> None:
|
||||
async def _update_general_settings(self, db_general_settings: Mapping[str, SettingsJsonValue] | str | None) -> None:
|
||||
global general_settings
|
||||
if db_general_settings is None:
|
||||
if not isinstance(db_general_settings, Mapping):
|
||||
return
|
||||
if not isinstance(general_settings, SettingsStore):
|
||||
self.settings.load_yaml(_as_settings_mapping(general_settings))
|
||||
|
|
@ -20639,11 +20643,13 @@ async def delete_config_general_settings(
|
|||
)
|
||||
|
||||
if data.field_name in _GENERAL_SETTINGS_UI_LITELLM_FIELDS:
|
||||
reset: Final = await _reset_general_settings_ui_litellm_field(data.field_name, user_api_key_dict)
|
||||
stored_copy: Final = await _stored_general_settings()
|
||||
if stored_copy is not None and data.field_name in stored_copy:
|
||||
await _remove_stored_general_setting(data.field_name, stored_copy, user_api_key_dict)
|
||||
return reset
|
||||
if stored_copy is None or data.field_name not in stored_copy:
|
||||
return await _reset_general_settings_ui_litellm_field(data.field_name, user_api_key_dict)
|
||||
removed: Final = await _remove_stored_general_setting(data.field_name, stored_copy, user_api_key_dict)
|
||||
if proxy_config.config_owns(section_name="litellm_settings", key=data.field_name):
|
||||
return removed
|
||||
return await _reset_general_settings_ui_litellm_field(data.field_name, user_api_key_dict)
|
||||
|
||||
stored: Final = await _stored_general_settings()
|
||||
|
||||
|
|
|
|||
|
|
@ -4851,9 +4851,6 @@ async def test_ProxyConfig__update_general_settings_none_input_noop():
|
|||
# None input returns early.
|
||||
result = await pc._update_general_settings(db_general_settings=None)
|
||||
assert result is None
|
||||
# Error-style: dict() will fail on non-mapping non-None input.
|
||||
with pytest.raises(TypeError):
|
||||
await pc._update_general_settings(db_general_settings=12345) # type: ignore[arg-type]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
|
|
@ -3756,6 +3756,22 @@ async def test_db_reload_keeps_the_resolved_value_of_a_config_owned_env_referenc
|
|||
assert proxy_server_module.store_model_in_db is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_db_reload_of_a_json_string_general_settings_row_reports_no_ignored_keys(monkeypatch):
|
||||
from litellm.proxy import proxy_server as proxy_server_module
|
||||
from litellm.proxy.proxy_server import ProxyConfig
|
||||
|
||||
monkeypatch.setattr(proxy_server_module, "user_api_key_cache", MagicMock(), raising=False)
|
||||
proxy_config: Final = ProxyConfig()
|
||||
proxy_config.settings.load_yaml({})
|
||||
monkeypatch.setattr(proxy_server_module, "general_settings", proxy_config.settings, raising=False)
|
||||
|
||||
await proxy_config._update_general_settings('{"budget_rollover": true, "master_key": "sk-stored"}')
|
||||
|
||||
assert proxy_config.settings.unmanaged_db_keys() == ()
|
||||
assert proxy_config.settings.source("master_key") != "db"
|
||||
|
||||
|
||||
def test_max_ui_session_budget_default_is_one_dollar():
|
||||
"""LIT-4662: the dashboard session budget default is a product decision; the
|
||||
old 0.25 default locked admins out of auto router Test Connection and the
|
||||
|
|
@ -15511,6 +15527,34 @@ async def test_delete_config_general_settings_removes_a_stored_general_settings_
|
|||
assert pc.settings.unmanaged_db_keys() == ()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_config_general_settings_removes_a_stored_copy_of_a_ui_litellm_field_the_config_file_sets(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
from litellm.proxy._types import ConfigFieldDelete
|
||||
from litellm.proxy.proxy_server import ProxyConfig, delete_config_general_settings
|
||||
|
||||
pc: Final = ProxyConfig()
|
||||
pc._load_yaml_settings_stores({"general_settings": {}, "litellm_settings": {"budget_rollover": True}})
|
||||
stored_row: Final = {"budget_rollover": True, "store_prompts_in_spend_logs": True}
|
||||
pc.settings.apply_db_row("general_settings", stored_row)
|
||||
fake_prisma: Final = _fake_prisma_with_config(stored_row)
|
||||
monkeypatch.setattr(proxy_server_module, "proxy_config", pc)
|
||||
monkeypatch.setattr(proxy_server_module, "prisma_client", fake_prisma)
|
||||
monkeypatch.setattr(litellm, "budget_rollover", True)
|
||||
|
||||
admin: Final = UserAPIKeyAuth(api_key="hashed-admin", user_id="admin-1", user_role=LitellmUserRoles.PROXY_ADMIN)
|
||||
await delete_config_general_settings(
|
||||
data=ConfigFieldDelete(field_name="budget_rollover", config_type="general_settings"),
|
||||
user_api_key_dict=admin,
|
||||
)
|
||||
|
||||
written: Final = json.loads(fake_prisma.db.litellm_config.upsert.await_args.kwargs["data"]["update"]["param_value"])
|
||||
assert written == {"store_prompts_in_spend_logs": True}
|
||||
assert litellm.budget_rollover is True
|
||||
assert pc.settings.unmanaged_db_keys() == ()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_config_general_settings_refuses_an_unknown_key_when_the_stored_row_is_a_json_string(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue