From 038df1131e771788e910046e8bc79919b6df4a4a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 27 Mar 2025 02:50:53 -0700 Subject: [PATCH] refac --- backend/open_webui/utils/middleware.py | 26 +++---- src/lib/apis/index.ts | 79 +++++++++++++++------ src/lib/components/chat/MessageInput.svelte | 1 - src/routes/+layout.svelte | 15 ++-- 4 files changed, 77 insertions(+), 44 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 77b01bdfc..8b17d7058 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -198,7 +198,6 @@ async def chat_completion_tools_handler( allowed_params = ( spec.get("parameters", {}).get("properties", {}).keys() ) - tool_function = tool["callable"] tool_function_params = { k: v for k, v in tool_function_params.items() @@ -206,8 +205,6 @@ async def chat_completion_tools_handler( } if tool.get("direct", False): - tool_output = await tool_function(**tool_function_params) - else: tool_output = await event_caller( { "type": "execute:tool", @@ -215,12 +212,14 @@ async def chat_completion_tools_handler( "id": str(uuid4()), "name": tool_function_name, "params": tool_function_params, - "tool": tool, "server": tool.get("server", {}), "session_id": metadata.get("session_id", None), }, } ) + else: + tool_function = tool["callable"] + tool_output = await tool_function(**tool_function_params) except Exception as e: tool_output = str(e) @@ -229,8 +228,9 @@ async def chat_completion_tools_handler( tool_output = json.dumps(tool_output, indent=4) if isinstance(tool_output, str): - tool_id = tools[tool_function_name].get("toolkit_id", "") - if tools[tool_function_name].get("citation", False): + tool = tools[tool_function_name] + tool_id = tool.get("toolkit_id", "") + if tool.get("citation", False) or tool.get("direct", False): sources.append( { @@ -1825,7 +1825,7 @@ async def process_chat_response( .get("properties", {}) .keys() ) - tool_function = tool["callable"] + tool_function_params = { k: v for k, v in tool_function_params.items() @@ -1833,10 +1833,6 @@ async def process_chat_response( } if tool.get("direct", False): - tool_result = await tool_function( - **tool_function_params - ) - else: tool_result = await event_caller( { "type": "execute:tool", @@ -1844,7 +1840,6 @@ async def process_chat_response( "id": str(uuid4()), "name": tool_name, "params": tool_function_params, - "tool": tool, "server": tool.get("server", {}), "session_id": metadata.get( "session_id", None @@ -1852,6 +1847,13 @@ async def process_chat_response( }, } ) + + else: + tool_function = tool["callable"] + tool_result = await tool_function( + **tool_function_params + ) + except Exception as e: tool_result = str(e) diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts index 2e6e19e6a..9b4944582 100644 --- a/src/lib/apis/index.ts +++ b/src/lib/apis/index.ts @@ -323,26 +323,25 @@ export const executeToolServer = async ( token: string, url: string, name: string, - params: object, + params: Record, serverData: { openapi: any; info: any; specs: any } ) => { let error = null; try { - // Find the matching operationId in the OpenAPI specification - const matchingRoute = Object.entries(serverData.openapi.paths).find(([path, methods]) => { - return Object.entries(methods).some( - ([method, operation]: any) => operation.operationId === name - ); - }); + // Find the matching operationId in the OpenAPI spec + const matchingRoute = Object.entries(serverData.openapi.paths).find(([_, methods]) => + Object.entries(methods as any).some(([__, operation]: any) => operation.operationId === name) + ); if (!matchingRoute) { throw new Error(`No matching route found for operationId: ${name}`); } - const [route, methods] = matchingRoute; - const methodEntry = Object.entries(methods).find( - ([method, operation]: any) => operation.operationId === name + const [routePath, methods] = matchingRoute; + + const methodEntry = Object.entries(methods as any).find( + ([_, operation]: any) => operation.operationId === name ); if (!methodEntry) { @@ -351,18 +350,55 @@ export const executeToolServer = async ( const [httpMethod, operation]: [string, any] = methodEntry; - // Replace path parameters in the URL - let finalUrl = `${url}${route}`; + // Split parameters by type + const pathParams: Record = {}; + const queryParams: Record = {}; + let bodyParams: any = {}; + if (operation.parameters) { - Object.entries(params).forEach(([key, value]) => { - finalUrl = finalUrl.replace(`{${key}}`, encodeURIComponent(value as string)); + operation.parameters.forEach((param: any) => { + const paramName = param.name; + const paramIn = param.in; + if (params.hasOwnProperty(paramName)) { + if (paramIn === 'path') { + pathParams[paramName] = params[paramName]; + } else if (paramIn === 'query') { + queryParams[paramName] = params[paramName]; + } + } }); } - // Headers and request options - const headers = { - ...(token && { authorization: `Bearer ${token}` }), - 'Content-Type': 'application/json' + let finalUrl = `${url}${routePath}`; + + // Replace path parameters (`{param}`) + Object.entries(pathParams).forEach(([key, value]) => { + finalUrl = finalUrl.replace(new RegExp(`{${key}}`, 'g'), encodeURIComponent(value)); + }); + + // Append query parameters to URL if any + if (Object.keys(queryParams).length > 0) { + const queryString = new URLSearchParams( + Object.entries(queryParams).map(([k, v]) => [k, String(v)]) + ).toString(); + finalUrl += `?${queryString}`; + } + + // Handle requestBody composite + if (operation.requestBody && operation.requestBody.content) { + const contentType = Object.keys(operation.requestBody.content)[0]; // typically "application/json" + if (params.body !== undefined) { + bodyParams = params.body; // Assume the provided params has a "body" property containing the payload + } else { + // Optional: Fallback or explicit error if body is expected but not provided + throw new Error(`Request body expected for operation '${name}' but none found.`); + } + } + + // Prepare headers and request options + const headers: Record = { + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) }; let requestOptions: RequestInit = { @@ -370,15 +406,14 @@ export const executeToolServer = async ( headers }; - // Handle request body for POST, PUT, PATCH if (['post', 'put', 'patch'].includes(httpMethod.toLowerCase()) && operation.requestBody) { - requestOptions.body = JSON.stringify(params); + requestOptions.body = JSON.stringify(bodyParams); } - // Execute the request const res = await fetch(finalUrl, requestOptions); if (!res.ok) { - throw new Error(`HTTP error! Status: ${res.status}`); + const resText = await res.text(); + throw new Error(`HTTP error! Status: ${res.status}. Message: ${resText}`); } return await res.json(); diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index a1f82ed44..fe9a62821 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -999,7 +999,6 @@ return; } - console.log('keypress', e); // Prevent Enter key from creating a new line const isCtrlPressed = e.ctrlKey || e.metaKey; const enterPressed = diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index ad90da074..530802cc7 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -25,7 +25,8 @@ temporaryChatEnabled, isLastActiveTab, isApp, - appInfo + appInfo, + toolServers } from '$lib/stores'; import { goto } from '$app/navigation'; import { page } from '$app/stores'; @@ -204,9 +205,9 @@ }; const executeTool = async (data, cb) => { - console.log(data); + const toolServer = $toolServers?.find((server) => server.url === data.server?.url); - const toolServer = $settings?.toolServers?.find((server) => server.url === data.server?.url); + console.log('executeTool', data, toolServer); if (toolServer) { const res = await executeToolServer( @@ -215,13 +216,9 @@ data?.name, data?.params, toolServer - ).catch((error) => { - console.error('executeToolServer', error); - return { - error: error - }; - }); + ); + console.log('executeToolServer', res); if (cb) { cb(JSON.parse(JSON.stringify(res))); }