diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 0529635e0a..d964469e19 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -128,6 +128,7 @@ from open_webui.utils.task import ( ) from open_webui.utils.tools import ( build_tool_server_headers, + get_attached_knowledge, get_builtin_tools, get_terminal_tools, get_tools, @@ -2828,6 +2829,31 @@ async def process_chat_payload(request, form_data, user, metadata, model): # 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) + + if (model.get('info', {}).get('meta', {}).get('builtinTools') or {}).get('knowledge', True): + from html import escape + + knowledge_tags = [] + for item in get_attached_knowledge(model, metadata): + if not item.get('id') or not item.get('type'): + continue + attrs = ( + f'type="{escape(str(item["type"]), quote=True)}" ' + f'id="{escape(str(item["id"]), quote=True)}"' + ) + if item.get('name'): + attrs += f' name="{escape(str(item["name"]), quote=True)}"' + if item.get('source'): + attrs += f' source="{escape(str(item["source"]), quote=True)}"' + knowledge_tags.append(f'') + + if knowledge_tags: + form_data['messages'] = add_or_update_system_message( + '\n' + '\n'.join(knowledge_tags) + '\n', + form_data['messages'], + append=True, + ) + builtin_tools = await get_builtin_tools( request, { diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index ec372e78ce..1930837af8 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -475,6 +475,45 @@ async def get_tools(request: Request, tool_ids: list[str], user: UserModel, extr return tools_dict +def get_attached_knowledge(model: dict, metadata: dict) -> list[dict]: + model_meta = model.get('info', {}).get('meta', {}) + knowledge = [] + seen = set() + + for source, items in ( + ('model', model_meta.get('knowledge') or []), + ('folder', metadata.get('folder_knowledge') or []), + ): + for item in items: + if not isinstance(item, dict): + continue + key = (item.get('type'), item.get('id')) + if not all(key) or key in seen: + continue + knowledge.append({**item, 'source': source}) + seen.add(key) + + file_context_enabled = (model_meta.get('capabilities') or {}).get('file_context', True) + if not file_context_enabled: + for item in metadata.get('files') or []: + if not isinstance(item, dict) or item.get('type') not in ('collection', 'note'): + continue + key = (item.get('type'), item.get('id')) + if not all(key) or key in seen: + continue + knowledge.append( + { + 'type': item.get('type'), + 'id': item.get('id'), + 'name': item.get('name'), + 'source': 'chat', + } + ) + seen.add(key) + + return knowledge + + async def get_builtin_tools( request: Request, extra_params: dict, features: dict = None, model: dict = None ) -> dict[str, dict]: @@ -557,11 +596,7 @@ async def get_builtin_tools( # Knowledge base tools - conditional injection based on model knowledge # If model has attached knowledge (any type), only provide query_knowledge_files # Otherwise, provide all KB browsing tools - model_knowledge = model.get('info', {}).get('meta', {}).get('knowledge', []) - # Merge folder-attached knowledge so builtin tools can search it - folder_knowledge = extra_params.get('__metadata__', {}).get('folder_knowledge') - if folder_knowledge: - model_knowledge = list(model_knowledge or []) + list(folder_knowledge) + model_knowledge = get_attached_knowledge(model, metadata) if is_builtin_tool_enabled('knowledge'): from open_webui.env import ENABLE_KB_EXEC