fix(proxy): preserve config pass-through and retention reloads

This commit is contained in:
Yuneng Jiang 2026-09-18 00:23:24 -07:00
parent d1cd869012
commit 0524745510
No known key found for this signature in database
3 changed files with 41 additions and 12 deletions

View file

@ -33,6 +33,9 @@ class SettingsStore(MutableMapping[str, JsonValue]):
self._yaml_values = MappingProxyType(dict(mapping))
self._clear_runtime()
def config_value(self, key: str) -> JsonValue:
return self._yaml_values.get(key)
def apply_db_row(self, row: DbRow, db_row: Mapping[str, JsonValue]) -> None:
previous_row: Final = self._database_rows.get(row, _EMPTY_VALUES)
self._database_rows = MappingProxyType({**self._database_rows, row: MappingProxyType(dict(db_row))})

View file

@ -4858,9 +4858,16 @@ class ProxyConfig:
)
def _load_yaml_settings_stores(self, config: Mapping[str, object]) -> None:
global config_passthrough_endpoints
for section, store in self._settings_stores.items():
store.load_yaml(_as_settings_mapping(config.get(section)))
store.apply_db_row(section, _EMPTY_SETTINGS_MAPPING)
yaml_endpoints: Final = self.settings.config_value("pass_through_endpoints")
config_passthrough_endpoints = (
[dict(endpoint) for endpoint in yaml_endpoints if isinstance(endpoint, dict)]
if isinstance(yaml_endpoints, list)
else None
)
def _config_with_resolved_settings(self, config: Mapping[str, object]) -> dict[str, object]:
return { # mutable-ok: get_config preserves the mutable mapping contract used by existing loaders
@ -6161,7 +6168,6 @@ class ProxyConfig:
## pass through endpoints
if general_settings.get("pass_through_endpoints", None) is not None:
config_passthrough_endpoints = general_settings["pass_through_endpoints"]
await initialize_pass_through_endpoints(
pass_through_endpoints=general_settings["pass_through_endpoints"],
config_file_path=config_file_path,
@ -7265,17 +7271,7 @@ class ProxyConfig:
db_values: Mapping[str, SettingsJsonValue],
previous_retention_values: tuple[SettingsJsonValue | None, ...],
) -> None:
if (
any(
key in db_values
for key in (
"maximum_spend_logs_retention_period",
"maximum_autorouter_session_retention_period",
"maximum_health_check_retention_period",
)
)
and previous_retention_values != self._resolved_retention_values()
):
if previous_retention_values != self._resolved_retention_values():
await self._reschedule_spend_log_cleanup_job()
async def _apply_ssrf_settings(self, db_values: Mapping[str, SettingsJsonValue]) -> None:

View file

@ -3775,6 +3775,23 @@ async def test_ProxyConfig__update_general_settings_skips_redundant_retention_re
reschedule.assert_not_awaited()
@pytest.mark.asyncio
async def test_ProxyConfig__update_general_settings_reschedules_after_retention_key_deletion(monkeypatch):
from litellm.proxy import proxy_server
pc = ProxyConfig()
reschedule: Final = AsyncMock()
monkeypatch.setattr(proxy_server, "general_settings", {})
monkeypatch.setattr(pc, "_reschedule_spend_log_cleanup_job", reschedule)
await pc._update_general_settings({"maximum_health_check_retention_period": "30d"})
reschedule.reset_mock()
await pc._update_general_settings({})
reschedule.assert_awaited_once()
@pytest.mark.asyncio
async def test_ProxyConfig__update_general_settings_dispatches_every_side_effect_handler(monkeypatch):
pc = ProxyConfig()
@ -3870,6 +3887,19 @@ async def test_ProxyConfig__update_config_from_db_resolves_through_settings_stor
assert pc.settings.source("max_parallel_requests") == "db"
def test_ProxyConfig_load_yaml_settings_stores_keeps_db_endpoints_out_of_config_baseline():
from litellm.proxy import proxy_server
pc = ProxyConfig()
config_endpoint: Final = {"path": "/config", "target": "https://config.example"}
db_endpoint: Final = {"id": "db-endpoint", "path": "/db", "target": "https://db.example"}
pc._load_yaml_settings_stores({"general_settings": {"pass_through_endpoints": [config_endpoint]}})
pc.settings.apply_db_row("general_settings", {"pass_through_endpoints": [db_endpoint]})
assert proxy_server.config_passthrough_endpoints == [config_endpoint]
@pytest.mark.asyncio
async def test_ProxyConfig_add_deployment_continues_after_null_pass_through_endpoints(monkeypatch):
from litellm.proxy import proxy_server