From f7fe8ef441be08d2d87fbc1413d4f00289ab0b4b Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sun, 2 Aug 2026 05:05:02 +0000 Subject: [PATCH] test(proxy): pin that reconciliation survives search-tool parse failures parse_search_tools operates on the dict get_config() already returned and swallows per-tool errors itself, so a raise from it leaves config_data a fully valid config with litellm_settings.callbacks untouched. Reconciling against it is correct. Deferring config_loaded until after that call looks safer but is not: a persistently malformed search_tools section would then skip reconciliation on every poll, so a revoked interception callback would keep serving paid searches indefinitely. This pins the ordering so it cannot be quietly reversed. --- .../proxy/proxy_server/test_proxy_config.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py index 20647b47071..e326791ebb8 100644 --- a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py +++ b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py @@ -2073,6 +2073,33 @@ async def test_ProxyConfig__update_llm_router_keeps_logger_when_config_load_fail assert snapshot == {"installed_before": 1, "remaining": 1} +@pytest.mark.asyncio +async def test_ProxyConfig__update_llm_router_reconciles_when_search_tool_parsing_fails(monkeypatch): + from litellm.integrations.websearch_interception.handler import ( + WebSearchInterceptionLogger, + ) + + _reset_callback_lists(monkeypatch) + pc = ProxyConfig() + pc._add_callbacks_from_db_config(_websearch_db_config("tavily-search", ["bedrock"])) + installed_before = [cb for cb in litellm.callbacks if isinstance(cb, WebSearchInterceptionLogger)] + + class _MalformedSearchToolsProxyConfig: + async def get_config(self) -> dict: + return {"litellm_settings": {}, "search_tools": "not-a-list"} + + monkeypatch.setattr(litellm.proxy.proxy_server, "proxy_config", _MalformedSearchToolsProxyConfig()) + + with pytest.raises(AttributeError): + pc.parse_search_tools({"search_tools": "not-a-list"}) + + await pc._update_llm_router(new_models=[], proxy_logging_obj=MagicMock()) + + remaining = [cb for cb in litellm.callbacks if isinstance(cb, WebSearchInterceptionLogger)] + snapshot = {"installed_before": len(installed_before), "remaining": len(remaining)} + assert snapshot == {"installed_before": 1, "remaining": 0} + + def test_ProxyConfig__add_callbacks_from_db_config_bad_config_raises(): pc = ProxyConfig() with pytest.raises(AttributeError):