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