fix(proxy): keep a config-owned key's resolved value across a database reload

Applying a database row dropped the runtime layer for every key the row
carried, including keys the config file owns. Those runtime entries hold
the env-resolved config values, so after a reload a key written as
os.environ/<NAME> read back as that literal string. The store now keeps
the runtime entry for a key the config owns and clears only the rest.

Visible as store_model_in_db silently turning itself off: the reload read
the raw reference, coerced it to False, and overwrote the resolved global.
This commit is contained in:
Yuneng Jiang 2026-09-18 22:58:08 -07:00
parent 3d2ec85215
commit c9158fcc12
No known key found for this signature in database
3 changed files with 33 additions and 4 deletions

View file

@ -117,12 +117,13 @@ class SettingsStore(MutableMapping[str, JsonValue]):
self._deleted_runtime_keys = frozenset()
def _clear_runtime_keys(self, keys: frozenset[str]) -> None:
if not keys:
stale: Final = frozenset(key for key in keys if not self.owned_by_config(key))
if not stale:
return
self._runtime_values = MappingProxyType(
{key: value for key, value in self._runtime_values.items() if key not in keys}
{key: value for key, value in self._runtime_values.items() if key not in stale}
)
self._deleted_runtime_keys = self._deleted_runtime_keys - keys
self._deleted_runtime_keys = self._deleted_runtime_keys - stale
def _keys(self) -> tuple[str, ...]:
return tuple(

View file

@ -86,7 +86,6 @@ def test_settings_store_keeps_unaffected_runtime_values_on_a_db_row_refresh() ->
def test_settings_store_keeps_a_config_owned_key_when_a_db_row_disagrees() -> None:
store: Final = SettingsStore("general_settings")
store.load_yaml({"changed": "config"})
store.apply_runtime_values({"changed": "resolved-config"})
store.apply_db_row("general_settings", {"changed": "database"})
@ -94,6 +93,17 @@ def test_settings_store_keeps_a_config_owned_key_when_a_db_row_disagrees() -> No
assert store.source("changed") == "config"
def test_settings_store_keeps_the_resolved_value_of_a_config_owned_key_across_a_db_row() -> None:
store: Final = SettingsStore("general_settings")
store.load_yaml({"changed": "os.environ/SETTING"})
store.apply_runtime_values({"changed": "resolved-config"})
store.apply_db_row("general_settings", {"changed": "database"})
assert store["changed"] == "resolved-config"
assert store.source("changed") == "config"
def test_settings_store_removes_only_runtime_values_affected_by_a_cleared_db_row() -> None:
store: Final = SettingsStore("general_settings")
store.load_yaml({"template": "os.environ/SETTING"})

View file

@ -3381,6 +3381,24 @@ async def test_db_reload_finishes_when_the_config_owns_a_setting_the_db_also_set
assert proxy_config.settings["user_api_key_cache_max_size"] == config_cache_size
@pytest.mark.asyncio
async def test_db_reload_keeps_the_resolved_value_of_a_config_owned_env_reference(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)
monkeypatch.setattr(proxy_server_module, "store_model_in_db", True, raising=False)
proxy_config: Final = ProxyConfig()
proxy_config.settings.load_yaml({"store_model_in_db": "os.environ/PROOF_STORE_FLAG"})
proxy_config.settings.apply_runtime_values({"store_model_in_db": True})
monkeypatch.setattr(proxy_server_module, "general_settings", proxy_config.settings, raising=False)
await proxy_config._update_general_settings({"store_model_in_db": True})
assert proxy_config.settings["store_model_in_db"] is True
assert proxy_server_module.store_model_in_db is True
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