From aac98120c8c54aa154240ab69f95e10f55108bc4 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:50:04 +0100 Subject: [PATCH] perf: batch fetch filter functions to eliminate N+1 queries (#21018) --- backend/open_webui/models/functions.py | 20 ++++++++++++++++++++ backend/open_webui/utils/chat.py | 8 ++------ backend/open_webui/utils/middleware.py | 8 ++------ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/backend/open_webui/models/functions.py b/backend/open_webui/models/functions.py index 6d94a90c8..c41b32831 100644 --- a/backend/open_webui/models/functions.py +++ b/backend/open_webui/models/functions.py @@ -195,6 +195,26 @@ class FunctionsTable: except Exception: return None + def get_functions_by_ids( + self, ids: list[str], db: Optional[Session] = None + ) -> list[FunctionModel]: + """ + Batch fetch multiple functions by their IDs in a single query. + Returns functions in the same order as the input IDs (None entries filtered out). + """ + if not ids: + return [] + try: + with get_db_context(db) as db: + functions = db.query(Function).filter(Function.id.in_(ids)).all() + # Create a dict for O(1) lookup + func_dict = {f.id: FunctionModel.model_validate(f) for f in functions} + # Return in original order, filtering out any not found + return [func_dict[id] for id in ids if id in func_dict] + except Exception: + return [] + + def get_functions( self, active_only=False, include_valves=False, db: Optional[Session] = None ) -> list[FunctionModel | FunctionWithValvesModel]: diff --git a/backend/open_webui/utils/chat.py b/backend/open_webui/utils/chat.py index be700dda7..1ed34b6ca 100644 --- a/backend/open_webui/utils/chat.py +++ b/backend/open_webui/utils/chat.py @@ -341,12 +341,8 @@ async def chat_completed(request: Request, form_data: dict, user: Any): } try: - filter_functions = [ - Functions.get_function_by_id(filter_id) - for filter_id in get_sorted_filter_ids( - request, model, metadata.get("filter_ids", []) - ) - ] + filter_ids = get_sorted_filter_ids(request, model, metadata.get("filter_ids", [])) + filter_functions = Functions.get_functions_by_ids(filter_ids) result, _ = await process_filter_functions( request=request, diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 028a4fa7b..81e07df94 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -1570,12 +1570,8 @@ async def process_chat_payload(request, form_data, user, metadata, model): raise e try: - filter_functions = [ - Functions.get_function_by_id(filter_id) - for filter_id in get_sorted_filter_ids( - request, model, metadata.get("filter_ids", []) - ) - ] + filter_ids = get_sorted_filter_ids(request, model, metadata.get("filter_ids", [])) + filter_functions = Functions.get_functions_by_ids(filter_ids) form_data, flags = await process_filter_functions( request=request,