fix(proxy): preserve DB settings during config reload (#49735)

* fix(proxy): preserve DB settings during config reload

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): mark reload settings variable as final

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Mateo Wang 2026-11-02 23:55:10 +00:00 • committed by GitHub
parent 9601740d05
commit a5fe912ecf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 27 additions and 1 deletions

View file

@ -5717,7 +5717,6 @@ def _with_config_file_pass_through_endpoints(
def _reload_settings_store(section: Section, store: SettingsStore, section_config: object) -> None:
serving_pass_throughs: Final = store.get("pass_through_endpoints")
store.load_yaml(_as_settings_mapping(section_config))
store.apply_db_row(section, _EMPTY_SETTINGS_MAPPING)
if is_resource_list(section, "pass_through_endpoints") and serving_pass_throughs is not None:
store["pass_through_endpoints"] = serving_pass_throughs
@ -6540,6 +6539,9 @@ class ProxyConfig:
prisma_client=prisma_client,
store_model_in_db=store_model_in_db,
)
else:
for section, store in self._settings_stores.items():
store.apply_db_row(section, _EMPTY_SETTINGS_MAPPING)
config = self._config_with_resolved_settings(config)
@ -8623,12 +8625,24 @@ class ProxyConfig:
environment_read: Final = next(self._environment_read_numbers)
sections: Final = tuple(self._settings_stores)
responses: Final = await asyncio.gather(*(get_config_param(prisma_client, section) for section in sections))
general_settings_response: Final = responses[sections.index("general_settings")]
general_settings_value: Final[Mapping[str, SettingsJsonValue]] = (
self._prepared_db_settings_values("general_settings", general_settings_response.param_value)
if general_settings_response is not None and general_settings_response.param_value is not None
else _EMPTY_SETTINGS_MAPPING
)
self._settings_stores["general_settings"].apply_db_row("general_settings", general_settings_value)
response: _ConfigParamValueRow | None
for section, response in zip(sections, responses):
if section == "general_settings":
continue
if response is None:
self._settings_stores[section].apply_db_row(section, _EMPTY_SETTINGS_MAPPING)
continue
param_value: JsonValue = getattr(response, "param_value", None)
if param_value is None:
self._settings_stores[section].apply_db_row(section, _EMPTY_SETTINGS_MAPPING)
continue
verbose_proxy_logger.debug(
@ -8639,6 +8653,7 @@ class ProxyConfig:
if section == "litellm_settings":
self._apply_litellm_settings_db_values(self._prepared_db_settings_values(section, param_value))
elif section == "environment_variables":
self._settings_stores[section].apply_db_row(section, _EMPTY_SETTINGS_MAPPING)
await self._apply_db_environment_variables_row(
prisma_client, response, environment_before_db, environment_read
)

View file

@ -32,11 +32,13 @@ import litellm
from litellm._logging import turn_on_json
from litellm.proxy._types import CommonProxyErrors, ConfigGeneralSettings
from litellm.proxy.common_utils.encrypt_decrypt_utils import encrypt_value_helper
from litellm.proxy.config_resolvers.settings_store import SettingsStore
from litellm.router_api import ApplyResult, RouterSettings
from litellm.types.router import Deployment, LiteLLM_Params
from litellm.proxy.proxy_server import (
ProxyConfig,
_is_remote_module_url,
_reload_settings_store,
_scrub_db_overlay_remote_module_loads,
_scrub_guardrail_inner,
resolve_complexity_router_plugins,
@ -70,6 +72,15 @@ def _db_model(model_id: str, model_name: str = "gpt-3.5-turbo") -> SimpleNamespa
)
def test_yaml_settings_reload_keeps_db_general_settings_until_sync() -> None:
settings: Final = SettingsStore("general_settings")
settings.apply_db_row("general_settings", {"max_file_downloads_per_minute": 2})
_reload_settings_store("general_settings", settings, {})
assert settings["max_file_downloads_per_minute"] == 2
@pytest.mark.asyncio
async def test_proxy_config_adds_and_deletes_stale_deployment(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
from litellm.proxy import proxy_server