From f660a12d665220f248fcc104a378aa5dd5a7a2e8 Mon Sep 17 00:00:00 2001 From: Jesper Kristensen Date: Thu, 9 Apr 2026 17:13:32 +0200 Subject: [PATCH] Added support for web_search tool hiting normal web-search path --- backend/open_webui/utils/middleware.py | 75 +++++++++++++++++++------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 271d072992d..2ecb2a4773c 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -307,6 +307,14 @@ def get_citation_source_from_tool_result( if isinstance(tool_result, dict) and 'error' in tool_result: return [] + # Unwrap {'results': [...]} wrapper added by process_tool_result for MCP tools + if ( + isinstance(tool_result, dict) + and 'results' in tool_result + and isinstance(tool_result['results'], list) + ): + tool_result = tool_result['results'] + # Validate tool_result type based on what the branch expects if tool_name in _EXPECTS_LIST and not isinstance(tool_result, list): return [] @@ -1312,24 +1320,43 @@ async def tool_call_handler(tool_call): tool = tools[tool_function_name] tool_id = tool.get('tool_id', '') - tool_name = f'{tool_id}/{tool_function_name}' if tool_id else f'{tool_function_name}' - - # Citation is enabled for this tool - sources.append( - { - 'source': { - 'name': (f'{tool_name}'), - }, - 'document': [str(tool_result)], - 'metadata': [ - { - 'source': (f'{tool_name}'), - 'parameters': tool_function_params, - } - ], - 'tool_result': True, - } - ) + # Use original (un-prefixed) name for MCP tools so that + # e.g. "websearch_search_web" matches the "search_web" handler. + citation_tool_name = tool.get('original_name', tool_function_name) + + if citation_tool_name in [ + 'search_web', + 'fetch_url', + 'view_file', + 'view_knowledge_file', + 'query_knowledge_files', + ]: + try: + citation_sources = get_citation_source_from_tool_result( + tool_name=citation_tool_name, + tool_params=tool_function_params, + tool_result=tool_result, + tool_id=tool_id, + ) + sources.extend(citation_sources) + except Exception as e: + log.exception(f'Error extracting citation source: {e}') + else: + tool_name = f'{tool_id}/{tool_function_name}' if tool_id else f'{tool_function_name}' + sources.append( + { + 'source': { + 'name': (f'{tool_name}'), + }, + 'document': [str(tool_result)], + 'metadata': [ + { + 'source': (f'{tool_name}'), + 'parameters': tool_function_params, + } + ], + } + ) if tools[tool_function_name].get('metadata', {}).get('file_handler', False): skip_files = True @@ -2776,6 +2803,7 @@ async def tool_function(**kwargs): 'type': 'mcp', 'client': client, 'direct': False, + 'original_name': tool_spec['name'], } except Exception as e: log.debug(e) @@ -5056,9 +5084,16 @@ async def execute_tool_call(tool_call): ) # Extract citation sources from tool results + # Use original (un-prefixed) name for MCP tools so that + # e.g. "websearch_search_web" matches the "search_web" handler. + citation_tool_name = ( + tool.get('original_name', tool_function_name) + if tool + else tool_function_name + ) if ( citations_enabled - and tool_function_name + and citation_tool_name in [ 'search_web', 'fetch_url', @@ -5071,7 +5106,7 @@ async def execute_tool_call(tool_call): ): try: citation_sources = get_citation_source_from_tool_result( - tool_name=tool_function_name, + tool_name=citation_tool_name, tool_params=tool_function_params, tool_result=tool_result, tool_id=tool.get('tool_id', '') if tool else '',