This commit is contained in:
Timothy Jaeryang Baek
2026-07-09 17:28:34 -05:00
parent bd6e0b61c2
commit 8e46450acd
11 changed files with 143 additions and 65 deletions
+4 -1
View File
@@ -4,7 +4,7 @@ import sys
from typing import Any
from fastapi import Request
from open_webui.env import GLOBAL_LOG_LEVEL
from open_webui.env import ENABLE_PLUGINS, GLOBAL_LOG_LEVEL
from open_webui.models.functions import Functions
from open_webui.models.users import UserModel
from open_webui.socket.main import get_event_call, get_event_emitter
@@ -17,6 +17,9 @@ log = logging.getLogger(__name__)
async def chat_action(request: Request, action_id: str, form_data: dict, user: Any):
if not ENABLE_PLUGINS:
raise Exception('Plugins are disabled by ENABLE_PLUGINS=false')
if '.' in action_id:
action_id, sub_action_id = action_id.split('.')
else:
+7
View File
@@ -1,6 +1,7 @@
import inspect
import logging
from open_webui.env import ENABLE_PLUGINS
from open_webui.models.functions import Functions
from open_webui.utils.plugin import (
get_function_module_from_cache,
@@ -19,6 +20,9 @@ async def get_function_module(request, function_id, load_from_db=True):
async def get_sorted_filter_ids(request, model: dict, enabled_filter_ids: list = None):
if not ENABLE_PLUGINS:
return []
async def get_priority(function_id):
try:
function_module = await get_function_module(request, function_id)
@@ -64,6 +68,9 @@ async def get_sorted_filter_ids(request, model: dict, enabled_filter_ids: list =
# Grant these filters the discernment to pass what serves
# and refuse what harms, for every soul in the house.
async def process_filter_functions(request, filter_functions, filter_type, form_data, extra_params):
if not ENABLE_PLUGINS:
return form_data, {}
skip_files = None
for function in filter_functions:
+52 -38
View File
@@ -33,6 +33,7 @@ from open_webui.env import (
CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE,
ENABLE_API_OUTLET_FILTERS,
ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION,
ENABLE_PLUGINS,
ENABLE_QUERIES_CACHE,
ENABLE_REALTIME_CHAT_SAVE,
ENABLE_RESPONSES_API_STATEFUL,
@@ -2421,19 +2422,20 @@ async def process_chat_payload(request, form_data, user, metadata, model):
except Exception as e:
raise e
try:
filter_ids = await get_sorted_filter_ids(request, model, metadata.get('filter_ids', []))
filter_functions = await Functions.get_functions_by_ids(filter_ids)
if ENABLE_PLUGINS:
try:
filter_ids = await get_sorted_filter_ids(request, model, metadata.get('filter_ids', []))
filter_functions = await Functions.get_functions_by_ids(filter_ids)
form_data, flags = await process_filter_functions(
request=request,
filter_functions=filter_functions,
filter_type='inlet',
form_data=form_data,
extra_params=extra_params,
)
except Exception as e:
raise Exception(f'{e}')
form_data, flags = await process_filter_functions(
request=request,
filter_functions=filter_functions,
filter_type='inlet',
form_data=form_data,
extra_params=extra_params,
)
except Exception as e:
raise Exception(f'{e}')
features = form_data.pop('features', None) or {}
extra_params['__features__'] = features
@@ -2618,6 +2620,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
mcp_tools_dict = {}
if tool_ids:
db_tool_ids = []
for tool_id in tool_ids:
if tool_id.startswith('server:mcp:'):
try:
@@ -2669,18 +2672,21 @@ async def process_chat_payload(request, form_data, user, metadata, model):
}
)
continue
elif ENABLE_PLUGINS:
db_tool_ids.append(tool_id)
tools_dict = await get_tools(
request,
tool_ids,
user,
{
**extra_params,
'__model__': models[task_model_id],
'__messages__': form_data['messages'],
'__files__': metadata.get('files', []),
},
)
if db_tool_ids:
tools_dict = await get_tools(
request,
db_tool_ids,
user,
{
**extra_params,
'__model__': models[task_model_id],
'__messages__': form_data['messages'],
'__files__': metadata.get('files', []),
},
)
if mcp_tools_dict:
tools_dict = {**tools_dict, **mcp_tools_dict}
@@ -2737,7 +2743,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
# Inject builtin tools for native function calling based on enabled features and model capability.
# Only inject when the request originates from the UI (identified by session_id).
# API callers don't expect hidden tools; they can explicitly request tools via tool_ids.
if use_builtin_tools:
if ENABLE_PLUGINS and use_builtin_tools:
# Add file context to user messages
chat_id = metadata.get('chat_id')
form_data['messages'] = await add_file_context(form_data.get('messages', []), chat_id, user)
@@ -3371,16 +3377,19 @@ async def outlet_filter_handler(ctx):
'__model__': model,
}
filter_ids = await get_sorted_filter_ids(request, model, metadata.get('filter_ids', []))
filter_functions = await Functions.get_functions_by_ids(filter_ids)
if ENABLE_PLUGINS:
filter_ids = await get_sorted_filter_ids(request, model, metadata.get('filter_ids', []))
filter_functions = await Functions.get_functions_by_ids(filter_ids)
outlet_result, _ = await process_filter_functions(
request=request,
filter_functions=filter_functions,
filter_type='outlet',
form_data=outlet_data,
extra_params=extra_params,
)
outlet_result, _ = await process_filter_functions(
request=request,
filter_functions=filter_functions,
filter_type='outlet',
form_data=outlet_data,
extra_params=extra_params,
)
else:
outlet_result = outlet_data
if outlet_result and outlet_result.get('messages'):
if not is_temp_chat and messages_map:
@@ -3620,10 +3629,14 @@ async def streaming_chat_response_handler(response, ctx):
'__model__': model,
}
filter_functions = [
await Functions.get_function_by_id(filter_id)
for filter_id in await get_sorted_filter_ids(request, model, metadata.get('filter_ids', []))
]
filter_functions = (
[
await Functions.get_function_by_id(filter_id)
for filter_id in await get_sorted_filter_ids(request, model, metadata.get('filter_ids', []))
]
if ENABLE_PLUGINS
else []
)
# Standard streaming response handler
# event_caller is optional — only needed for direct (client-side) tools
@@ -3907,7 +3920,8 @@ async def streaming_chat_response_handler(response, ctx):
model_capabilities = model.get('info', {}).get('meta', {}).get('capabilities') or {}
builtin_tools_meta = model.get('info', {}).get('meta', {}).get('builtinTools', {})
DETECT_CODE_INTERPRETER = (
bool(features.get('code_interpreter'))
ENABLE_PLUGINS
and bool(features.get('code_interpreter'))
and builtin_tools_meta.get('code_interpreter', True)
and await Config.get('code_interpreter.enable')
and model_capabilities.get('code_interpreter', True)
+22 -9
View File
@@ -9,7 +9,7 @@ from open_webui.config import (
BYPASS_ADMIN_ACCESS_CONTROL,
DEFAULT_ARENA_MODEL,
)
from open_webui.env import BYPASS_MODEL_ACCESS_CONTROL, GLOBAL_LOG_LEVEL
from open_webui.env import BYPASS_MODEL_ACCESS_CONTROL, ENABLE_PLUGINS, GLOBAL_LOG_LEVEL
from open_webui.functions import get_function_models
from open_webui.models.access_grants import AccessGrants
from open_webui.models.config import Config
@@ -125,11 +125,23 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None)
]
models = models + arena_models
global_action_ids = {function.id for function in await Functions.get_global_action_functions()}
enabled_action_ids = {function.id for function in await Functions.get_functions_by_type('action', active_only=True)}
global_action_ids = (
{function.id for function in await Functions.get_global_action_functions()} if ENABLE_PLUGINS else set()
)
enabled_action_ids = (
{function.id for function in await Functions.get_functions_by_type('action', active_only=True)}
if ENABLE_PLUGINS
else set()
)
global_filter_ids = {function.id for function in await Functions.get_global_filter_functions()}
enabled_filter_ids = {function.id for function in await Functions.get_functions_by_type('filter', active_only=True)}
global_filter_ids = (
{function.id for function in await Functions.get_global_filter_functions()} if ENABLE_PLUGINS else set()
)
enabled_filter_ids = (
{function.id for function in await Functions.get_functions_by_type('filter', active_only=True)}
if ENABLE_PLUGINS
else set()
)
custom_models = await Models.get_all_models()
@@ -157,8 +169,9 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None)
if 'info' in model:
if 'meta' in model['info']:
action_ids.extend(model['info']['meta'].get('actionIds', []))
filter_ids.extend(model['info']['meta'].get('filterIds', []))
if ENABLE_PLUGINS:
action_ids.extend(model['info']['meta'].get('actionIds', []))
filter_ids.extend(model['info']['meta'].get('filterIds', []))
if 'params' in model['info']:
del model['info']['params']
@@ -211,10 +224,10 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None)
if custom_model.meta:
meta = custom_model.meta.model_dump()
if 'actionIds' in meta:
if ENABLE_PLUGINS and 'actionIds' in meta:
action_ids.extend(meta['actionIds'])
if 'filterIds' in meta:
if ENABLE_PLUGINS and 'filterIds' in meta:
filter_ids.extend(meta['filterIds'])
model['action_ids'] = action_ids
+11
View File
@@ -13,6 +13,7 @@ from typing import Any
from open_webui.env import (
ENABLE_PIP_INSTALL_FRONTMATTER_REQUIREMENTS,
ENABLE_PLUGINS,
OFFLINE_MODE,
PIP_OPTIONS,
PIP_PACKAGE_INDEX_OPTIONS,
@@ -203,6 +204,9 @@ def replace_imports(content):
# May the intent of the one who wrote it survive every
# import and transformation, as a deed survives the generations.
async def load_tool_module_by_id(tool_id, content=None):
if not ENABLE_PLUGINS:
raise RuntimeError('Plugins are disabled by ENABLE_PLUGINS=false')
if content is None:
tool = await Tools.get_tool_by_id(tool_id)
if not tool:
@@ -251,6 +255,9 @@ async def load_tool_module_by_id(tool_id, content=None):
async def load_function_module_by_id(function_id: str, content: str | None = None):
if not ENABLE_PLUGINS:
raise RuntimeError('Plugins are disabled by ENABLE_PLUGINS=false')
if content is None:
function = await Functions.get_function_by_id(function_id)
if not function:
@@ -447,6 +454,10 @@ async def install_tool_and_function_dependencies():
and then installing them using pip. Duplicates or similar version specifications are
handled by pip as much as possible.
"""
if not ENABLE_PLUGINS:
log.info('ENABLE_PLUGINS is disabled, skipping tool and function dependencies.')
return
function_list = await Functions.get_functions(active_only=True)
tool_list = await Tools.get_tools()
+7
View File
@@ -35,6 +35,7 @@ from open_webui.env import (
AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER,
AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA,
ENABLE_FORWARD_USER_INFO_HEADERS,
ENABLE_PLUGINS,
FORWARD_SESSION_INFO_HEADER_CHAT_ID,
FORWARD_SESSION_INFO_HEADER_MESSAGE_ID,
REDIS_KEY_PREFIX,
@@ -253,6 +254,9 @@ async def get_updated_tool_function(function: Callable, extra_params: dict):
async def get_tools(request: Request, tool_ids: list[str], user: UserModel, extra_params: dict) -> dict[str, dict]:
"""Load tools for the given tool_ids, checking access control."""
if not ENABLE_PLUGINS:
return {}
if not tool_ids:
return {}
@@ -469,6 +473,9 @@ async def get_builtin_tools(
Get built-in tools for native function calling.
Only returns tools when BOTH the global config is enabled AND the model capability allows it.
"""
if not ENABLE_PLUGINS:
return {}
tools_dict = {}
builtin_functions = []
features = features or {}