From ac5c5ee302729a9833277e99b74afbef7948b51e Mon Sep 17 00:00:00 2001 From: Quentin Machu Date: Mon, 26 Jan 2026 17:42:56 -0500 Subject: [PATCH] fix: Search tools not found when using per-request routers **Root Cause:** When API keys or teams have router_settings configured, the proxy creates per-request Router instances from user_config. These new routers were missing search_tools from the main router, causing "search tool not found" errors despite search_tools being configured. **The Fix:** 1. **common_request_processing.py (lines 554-556):** Pass search_tools from main router to user_config so per-request routers inherit them 2. **proxy_server.py (line 3171):** Remove `if len(_model_list) > 0` check to allow router creation with empty model list (needed for search-tools-only use case) 3. **proxy_server.py (line 746):** Remove redundant search_tools loading code (already handled by _init_search_tools_in_db() called during startup) --- litellm/proxy/common_request_processing.py | 7 ++++++- litellm/proxy/proxy_server.py | 23 +++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index 0d3e61b75c7..4f0c59474f1 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -547,9 +547,14 @@ async def common_processing_pre_call_logic( # Get model_list from current router model_list = llm_router.get_model_list() if model_list is not None: - # Create user_config with model_list and router_settings + # Create user_config with model_list, search_tools, and router_settings # This creates a per-request router with the hierarchical settings user_config = {"model_list": model_list, **router_settings} + + # Include search_tools from main router so per-request router has them + if hasattr(llm_router, "search_tools") and llm_router.search_tools: + user_config["search_tools"] = llm_router.search_tools + self.data["user_config"] = user_config if "messages" in self.data and self.data["messages"]: diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 16cdd9da64b..056367243f9 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -3173,17 +3173,18 @@ async def _update_llm_router( _model_list: list = self.decrypt_model_list_from_db( new_models=models_list ) - if len(_model_list) > 0: - verbose_proxy_logger.debug(f"_model_list: {_model_list}") - llm_router = litellm.Router( - model_list=_model_list, - router_general_settings=RouterGeneralSettings( - async_only_mode=True # only init async clients - ), - search_tools=search_tools, - ignore_invalid_deployments=True, - ) - verbose_proxy_logger.debug(f"updated llm_router: {llm_router}") + # Create router even with empty model list to support search_tools + # Router can function with model_list=[] and only search_tools + verbose_proxy_logger.debug(f"_model_list: {_model_list}") + llm_router = litellm.Router( + model_list=_model_list, + router_general_settings=RouterGeneralSettings( + async_only_mode=True # only init async clients + ), + search_tools=search_tools, + ignore_invalid_deployments=True, + ) + verbose_proxy_logger.debug(f"updated llm_router: {llm_router}") else: verbose_proxy_logger.debug(f"len new_models: {len(models_list)}") ## DELETE MODEL LOGIC