From 91762ed8070ae213161153ba227a92b861b778e8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 23 Jun 2026 00:25:21 +0200 Subject: [PATCH] refac --- backend/open_webui/main.py | 2 +- backend/open_webui/routers/functions.py | 10 ++-- backend/open_webui/routers/images.py | 5 +- backend/open_webui/routers/models.py | 6 +- backend/open_webui/routers/tools.py | 37 ++++--------- backend/open_webui/utils/models.py | 9 +-- backend/open_webui/utils/plugin.py | 74 +++++++++++++++---------- backend/open_webui/utils/tools.py | 12 ++-- 8 files changed, 77 insertions(+), 78 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 7c8422a2ff..25dda87f78 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -839,7 +839,7 @@ async def get_models(request: Request, refresh: bool = False, user=Depends(get_v models.append(model) - model_order_list = await Config.get('models.order_list') + model_order_list = await Config.get('ui.model_order_list') if model_order_list: model_order_dict = {model_id: i for i, model_id in enumerate(model_order_list)} # Sort models by order list priority, with fallback for those not in the list diff --git a/backend/open_webui/routers/functions.py b/backend/open_webui/routers/functions.py index 58ec93657e..d6eab3e0bd 100644 --- a/backend/open_webui/routers/functions.py +++ b/backend/open_webui/routers/functions.py @@ -22,6 +22,7 @@ from open_webui.models.functions import ( ) from open_webui.utils.auth import get_admin_user, get_verified_user from open_webui.utils.plugin import ( + get_functions_cache, get_function_module_from_cache, load_function_module_by_id, replace_imports, @@ -205,7 +206,7 @@ async def create_new_function( ) form_data.meta.manifest = frontmatter - FUNCTIONS = request.app.state.FUNCTIONS + FUNCTIONS = get_functions_cache(request) FUNCTIONS[form_data.id] = function_module function = await Functions.insert_new_function(user.id, function_type, form_data, db=db) @@ -322,7 +323,7 @@ async def update_function_by_id( function_module, function_type, frontmatter = await load_function_module_by_id(id, content=form_data.content) form_data.meta.manifest = frontmatter - FUNCTIONS = request.app.state.FUNCTIONS + FUNCTIONS = get_functions_cache(request) FUNCTIONS[id] = function_module updated = {**form_data.model_dump(exclude={'id'}), 'type': function_type} @@ -363,9 +364,8 @@ async def delete_function_by_id( result = await Functions.delete_function_by_id(id, db=db) if result: - FUNCTIONS = request.app.state.FUNCTIONS - if id in FUNCTIONS: - del FUNCTIONS[id] + FUNCTIONS = get_functions_cache(request) + FUNCTIONS.pop(id, None) return result diff --git a/backend/open_webui/routers/images.py b/backend/open_webui/routers/images.py index 951fd905e7..17f1acb7e5 100644 --- a/backend/open_webui/routers/images.py +++ b/backend/open_webui/routers/images.py @@ -773,14 +773,15 @@ async def edit_images(request: Request, form_data: EditImageForm, user=Depends(g # global image-edit switch and the per-user image-generation permission. The internal # callers (edit_image tool, chat middleware) gate themselves and call image_edits() # directly, so they are unaffected by this wrapper. - if not request.app.state.config.ENABLE_IMAGE_EDIT: + image_config = await get_image_config() + if not image_config.ENABLE_IMAGE_EDIT: raise HTTPException( status_code=403, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, ) if user.role != 'admin' and not await has_permission( - user.id, 'features.image_generation', request.app.state.config.USER_PERMISSIONS + user.id, 'features.image_generation', image_config.USER_PERMISSIONS ): raise HTTPException( status_code=403, diff --git a/backend/open_webui/routers/models.py b/backend/open_webui/routers/models.py index 95b9e46bfe..ed4d221bc6 100644 --- a/backend/open_webui/routers/models.py +++ b/backend/open_webui/routers/models.py @@ -532,11 +532,7 @@ async def get_model_profile_image( # Fallback: check arena models stored in config (not in the DB) if not profile_image_url: - arena_models = getattr( - getattr(request.app.state, 'config', None), - 'EVALUATION_ARENA_MODELS', - [], - ) + arena_models = await Config.get('evaluation.arena.models', []) or [] for arena_model in arena_models: if arena_model.get('id') == id: profile_image_url = arena_model.get('meta', {}).get('profile_image_url') diff --git a/backend/open_webui/routers/tools.py b/backend/open_webui/routers/tools.py index abd49dd64f..0c3ee91c60 100644 --- a/backend/open_webui/routers/tools.py +++ b/backend/open_webui/routers/tools.py @@ -31,6 +31,7 @@ from open_webui.utils.access_control import ( ) from open_webui.utils.auth import get_admin_user, get_verified_user from open_webui.utils.plugin import ( + get_tools_cache, get_tool_module_from_cache, load_tool_module_by_id, replace_imports, @@ -70,8 +71,9 @@ async def get_tools( tools = [] # Local Tools + tools_cache = get_tools_cache(request) for tool in await Tools.get_tools(defer_content=True, db=db): - tool_module = request.app.state.TOOLS.get(tool.id) if hasattr(request.app.state, 'TOOLS') else None + tool_module = tools_cache.get(tool.id) tools.append( ToolUserResponse( **{ @@ -368,7 +370,7 @@ async def create_new_tools( tool_module, frontmatter = await load_tool_module_by_id(form_data.id, content=form_data.content) form_data.meta.manifest = frontmatter - TOOLS = request.app.state.TOOLS + TOOLS = get_tools_cache(request) TOOLS[form_data.id] = tool_module specs = get_tool_specs(TOOLS[form_data.id]) @@ -498,7 +500,7 @@ async def update_tools_by_id( tool_module, frontmatter = await load_tool_module_by_id(id, content=form_data.content) form_data.meta.manifest = frontmatter - TOOLS = request.app.state.TOOLS + TOOLS = get_tools_cache(request) TOOLS[id] = tool_module specs = get_tool_specs(TOOLS[id]) @@ -624,9 +626,8 @@ async def delete_tools_by_id( result = await Tools.delete_tool_by_id(id, db=db) if result: - TOOLS = request.app.state.TOOLS - if id in TOOLS: - del TOOLS[id] + TOOLS = get_tools_cache(request) + TOOLS.pop(id, None) return result @@ -708,11 +709,7 @@ async def get_tools_valves_spec_by_id( detail=ERROR_MESSAGES.ACCESS_PROHIBITED, ) - if id in request.app.state.TOOLS: - tools_module = request.app.state.TOOLS[id] - else: - tools_module, _ = await load_tool_module_by_id(id) - request.app.state.TOOLS[id] = tools_module + tools_module, _ = await get_tool_module_from_cache(request, id) if hasattr(tools_module, 'Valves'): Valves = tools_module.Valves @@ -759,11 +756,7 @@ async def update_tools_valves_by_id( detail=ERROR_MESSAGES.ACCESS_PROHIBITED, ) - if id in request.app.state.TOOLS: - tools_module = request.app.state.TOOLS[id] - else: - tools_module, _ = await load_tool_module_by_id(id) - request.app.state.TOOLS[id] = tools_module + tools_module, _ = await get_tool_module_from_cache(request, id) if not hasattr(tools_module, 'Valves'): raise HTTPException( @@ -858,11 +851,7 @@ async def get_tools_user_valves_spec_by_id( detail=ERROR_MESSAGES.ACCESS_PROHIBITED, ) - if id in request.app.state.TOOLS: - tools_module = request.app.state.TOOLS[id] - else: - tools_module, _ = await load_tool_module_by_id(id) - request.app.state.TOOLS[id] = tools_module + tools_module, _ = await get_tool_module_from_cache(request, id) if hasattr(tools_module, 'UserValves'): UserValves = tools_module.UserValves @@ -904,11 +893,7 @@ async def update_tools_user_valves_by_id( detail=ERROR_MESSAGES.ACCESS_PROHIBITED, ) - if id in request.app.state.TOOLS: - tools_module = request.app.state.TOOLS[id] - else: - tools_module, _ = await load_tool_module_by_id(id) - request.app.state.TOOLS[id] = tools_module + tools_module, _ = await get_tool_module_from_cache(request, id) if hasattr(tools_module, 'UserValves'): UserValves = tools_module.UserValves diff --git a/backend/open_webui/utils/models.py b/backend/open_webui/utils/models.py index f770249f89..ca8179ea37 100644 --- a/backend/open_webui/utils/models.py +++ b/backend/open_webui/utils/models.py @@ -21,8 +21,8 @@ from open_webui.routers import ollama, openai from open_webui.socket.utils import RedisDict from open_webui.utils.access_control import has_access, has_base_model_access from open_webui.utils.plugin import ( + get_functions_cache, get_function_module_from_cache, - load_function_module_by_id, ) logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL) @@ -322,10 +322,11 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None) # Batch-fetch all function valves in one query to avoid N+1 DB hits # inside get_action_priority (previously called per action × per model). all_function_valves = await Functions.get_function_valves_by_ids(list(all_function_ids)) + functions_cache = get_functions_cache(request) def get_action_priority(action_id): try: - function_module = request.app.state.FUNCTIONS.get(action_id) + function_module = functions_cache.get(action_id) if function_module and hasattr(function_module, 'Valves'): valves_db = all_function_valves.get(action_id) valves = function_module.Valves(**(valves_db if valves_db else {})) @@ -355,7 +356,7 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None) log.info(f'Action not found: {action_id}') continue - function_module = request.app.state.FUNCTIONS.get(action_id) + function_module = functions_cache.get(action_id) if function_module is None: log.info(f'Failed to load action module: {action_id}') continue @@ -368,7 +369,7 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None) log.info(f'Filter not found: {filter_id}') continue - function_module = request.app.state.FUNCTIONS.get(filter_id) + function_module = functions_cache.get(filter_id) if function_module is None: log.info(f'Failed to load filter module: {filter_id}') continue diff --git a/backend/open_webui/utils/plugin.py b/backend/open_webui/utils/plugin.py index d7aa0a0a39..6087824085 100644 --- a/backend/open_webui/utils/plugin.py +++ b/backend/open_webui/utils/plugin.py @@ -298,7 +298,33 @@ async def load_function_module_by_id(function_id: str, content: str | None = Non os.unlink(temp_file.name) +def _state_cache(request, name: str) -> dict: + if not hasattr(request.app.state, name): + setattr(request.app.state, name, {}) + return getattr(request.app.state, name) + + +def get_tools_cache(request) -> dict: + return _state_cache(request, 'TOOLS') + + +def get_tool_contents_cache(request) -> dict: + return _state_cache(request, 'TOOL_CONTENTS') + + +def get_functions_cache(request) -> dict: + return _state_cache(request, 'FUNCTIONS') + + +def get_function_contents_cache(request) -> dict: + return _state_cache(request, 'FUNCTION_CONTENTS') + + async def get_tool_module_from_cache(request, tool_id, load_from_db=True): + tools_cache = get_tools_cache(request) + tool_contents_cache = get_tool_contents_cache(request) + content = None + if load_from_db: # Always load from the database by default tool = await Tools.get_tool_by_id(tool_id) @@ -312,27 +338,19 @@ async def get_tool_module_from_cache(request, tool_id, load_from_db=True): # Update the tool content in the database await Tools.update_tool_by_id(tool_id, {'content': content}) - if (hasattr(request.app.state, 'TOOL_CONTENTS') and tool_id in request.app.state.TOOL_CONTENTS) and ( - hasattr(request.app.state, 'TOOLS') and tool_id in request.app.state.TOOLS - ): - if request.app.state.TOOL_CONTENTS[tool_id] == content: - return request.app.state.TOOLS[tool_id], None + if tool_id in tool_contents_cache and tool_id in tools_cache: + if tool_contents_cache[tool_id] == content: + return tools_cache[tool_id], None tool_module, frontmatter = await load_tool_module_by_id(tool_id, content) else: - if hasattr(request.app.state, 'TOOLS') and tool_id in request.app.state.TOOLS: - return request.app.state.TOOLS[tool_id], None + if tool_id in tools_cache: + return tools_cache[tool_id], None tool_module, frontmatter = await load_tool_module_by_id(tool_id) - if not hasattr(request.app.state, 'TOOLS'): - request.app.state.TOOLS = {} - - if not hasattr(request.app.state, 'TOOL_CONTENTS'): - request.app.state.TOOL_CONTENTS = {} - - request.app.state.TOOLS[tool_id] = tool_module - request.app.state.TOOL_CONTENTS[tool_id] = content + tools_cache[tool_id] = tool_module + tool_contents_cache[tool_id] = content return tool_module, frontmatter @@ -340,6 +358,10 @@ async def get_tool_module_from_cache(request, tool_id, load_from_db=True): async def get_function_module_from_cache( request, function_id, function: FunctionModel | None = None, load_from_db=True ): + functions_cache = get_functions_cache(request) + function_contents_cache = get_function_contents_cache(request) + content = None + if load_from_db: # Always load from the database by default # This is useful for hooks like "inlet" or "outlet" where the content might change @@ -357,30 +379,22 @@ async def get_function_module_from_cache( # Update the function content in the database await Functions.update_function_by_id(function_id, {'content': content}) - if ( - hasattr(request.app.state, 'FUNCTION_CONTENTS') and function_id in request.app.state.FUNCTION_CONTENTS - ) and (hasattr(request.app.state, 'FUNCTIONS') and function_id in request.app.state.FUNCTIONS): - if request.app.state.FUNCTION_CONTENTS[function_id] == content: - return request.app.state.FUNCTIONS[function_id], None, None + if function_id in function_contents_cache and function_id in functions_cache: + if function_contents_cache[function_id] == content: + return functions_cache[function_id], None, None function_module, function_type, frontmatter = await load_function_module_by_id(function_id, content) else: # Load from cache (e.g. "stream" hook) # This is useful for performance reasons - if hasattr(request.app.state, 'FUNCTIONS') and function_id in request.app.state.FUNCTIONS: - return request.app.state.FUNCTIONS[function_id], None, None + if function_id in functions_cache: + return functions_cache[function_id], None, None function_module, function_type, frontmatter = await load_function_module_by_id(function_id) - if not hasattr(request.app.state, 'FUNCTIONS'): - request.app.state.FUNCTIONS = {} - - if not hasattr(request.app.state, 'FUNCTION_CONTENTS'): - request.app.state.FUNCTION_CONTENTS = {} - - request.app.state.FUNCTIONS[function_id] = function_module - request.app.state.FUNCTION_CONTENTS[function_id] = content + functions_cache[function_id] = function_module + function_contents_cache[function_id] = content return function_module, function_type, frontmatter diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index c5ef34a299..931d4288e5 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -95,7 +95,7 @@ from open_webui.tools.builtin import ( from open_webui.utils.access_control import has_access, has_connection_access, has_permission from open_webui.utils.headers import get_custom_headers, include_user_info_headers from open_webui.utils.misc import is_string_allowed -from open_webui.utils.plugin import load_tool_module_by_id +from open_webui.utils.plugin import get_tool_contents_cache, get_tools_cache, load_tool_module_by_id from pydantic import BaseModel, Field, create_model from pydantic.fields import FieldInfo @@ -250,11 +250,13 @@ async def get_tools(request: Request, tool_ids: list[str], user: UserModel, extr log.warning(f'Access denied to tool {tool_id} for user {user.id}') continue - module = request.app.state.TOOLS.get(tool_id) - if module is None or request.app.state.TOOL_CONTENTS.get(tool_id) != tool.content: + tools_cache = get_tools_cache(request) + tool_contents_cache = get_tool_contents_cache(request) + module = tools_cache.get(tool_id) + if module is None or tool_contents_cache.get(tool_id) != tool.content: module, _ = await load_tool_module_by_id(tool_id, content=tool.content) - request.app.state.TOOLS[tool_id] = module - request.app.state.TOOL_CONTENTS[tool_id] = tool.content + tools_cache[tool_id] = module + tool_contents_cache[tool_id] = tool.content __user__ = { **extra_params['__user__'],