This commit is contained in:
Timothy Jaeryang Baek 2025-03-27 02:50:53 -07:00
parent d1bc2cfa2f
commit 038df1131e
4 changed files with 77 additions and 44 deletions

View File

@ -198,7 +198,6 @@ async def chat_completion_tools_handler(
allowed_params = ( allowed_params = (
spec.get("parameters", {}).get("properties", {}).keys() spec.get("parameters", {}).get("properties", {}).keys()
) )
tool_function = tool["callable"]
tool_function_params = { tool_function_params = {
k: v k: v
for k, v in tool_function_params.items() for k, v in tool_function_params.items()
@ -206,8 +205,6 @@ async def chat_completion_tools_handler(
} }
if tool.get("direct", False): if tool.get("direct", False):
tool_output = await tool_function(**tool_function_params)
else:
tool_output = await event_caller( tool_output = await event_caller(
{ {
"type": "execute:tool", "type": "execute:tool",
@ -215,12 +212,14 @@ async def chat_completion_tools_handler(
"id": str(uuid4()), "id": str(uuid4()),
"name": tool_function_name, "name": tool_function_name,
"params": tool_function_params, "params": tool_function_params,
"tool": tool,
"server": tool.get("server", {}), "server": tool.get("server", {}),
"session_id": metadata.get("session_id", None), "session_id": metadata.get("session_id", None),
}, },
} }
) )
else:
tool_function = tool["callable"]
tool_output = await tool_function(**tool_function_params)
except Exception as e: except Exception as e:
tool_output = str(e) tool_output = str(e)
@ -229,8 +228,9 @@ async def chat_completion_tools_handler(
tool_output = json.dumps(tool_output, indent=4) tool_output = json.dumps(tool_output, indent=4)
if isinstance(tool_output, str): if isinstance(tool_output, str):
tool_id = tools[tool_function_name].get("toolkit_id", "") tool = tools[tool_function_name]
if tools[tool_function_name].get("citation", False): tool_id = tool.get("toolkit_id", "")
if tool.get("citation", False) or tool.get("direct", False):
sources.append( sources.append(
{ {
@ -1825,7 +1825,7 @@ async def process_chat_response(
.get("properties", {}) .get("properties", {})
.keys() .keys()
) )
tool_function = tool["callable"]
tool_function_params = { tool_function_params = {
k: v k: v
for k, v in tool_function_params.items() for k, v in tool_function_params.items()
@ -1833,10 +1833,6 @@ async def process_chat_response(
} }
if tool.get("direct", False): if tool.get("direct", False):
tool_result = await tool_function(
**tool_function_params
)
else:
tool_result = await event_caller( tool_result = await event_caller(
{ {
"type": "execute:tool", "type": "execute:tool",
@ -1844,7 +1840,6 @@ async def process_chat_response(
"id": str(uuid4()), "id": str(uuid4()),
"name": tool_name, "name": tool_name,
"params": tool_function_params, "params": tool_function_params,
"tool": tool,
"server": tool.get("server", {}), "server": tool.get("server", {}),
"session_id": metadata.get( "session_id": metadata.get(
"session_id", None "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: except Exception as e:
tool_result = str(e) tool_result = str(e)

View File

@ -323,26 +323,25 @@ export const executeToolServer = async (
token: string, token: string,
url: string, url: string,
name: string, name: string,
params: object, params: Record<string, any>,
serverData: { openapi: any; info: any; specs: any } serverData: { openapi: any; info: any; specs: any }
) => { ) => {
let error = null; let error = null;
try { try {
// Find the matching operationId in the OpenAPI specification // Find the matching operationId in the OpenAPI spec
const matchingRoute = Object.entries(serverData.openapi.paths).find(([path, methods]) => { const matchingRoute = Object.entries(serverData.openapi.paths).find(([_, methods]) =>
return Object.entries(methods).some( Object.entries(methods as any).some(([__, operation]: any) => operation.operationId === name)
([method, operation]: any) => operation.operationId === name );
);
});
if (!matchingRoute) { if (!matchingRoute) {
throw new Error(`No matching route found for operationId: ${name}`); throw new Error(`No matching route found for operationId: ${name}`);
} }
const [route, methods] = matchingRoute; const [routePath, methods] = matchingRoute;
const methodEntry = Object.entries(methods).find(
([method, operation]: any) => operation.operationId === name const methodEntry = Object.entries(methods as any).find(
([_, operation]: any) => operation.operationId === name
); );
if (!methodEntry) { if (!methodEntry) {
@ -351,18 +350,55 @@ export const executeToolServer = async (
const [httpMethod, operation]: [string, any] = methodEntry; const [httpMethod, operation]: [string, any] = methodEntry;
// Replace path parameters in the URL // Split parameters by type
let finalUrl = `${url}${route}`; const pathParams: Record<string, any> = {};
const queryParams: Record<string, any> = {};
let bodyParams: any = {};
if (operation.parameters) { if (operation.parameters) {
Object.entries(params).forEach(([key, value]) => { operation.parameters.forEach((param: any) => {
finalUrl = finalUrl.replace(`{${key}}`, encodeURIComponent(value as string)); 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 let finalUrl = `${url}${routePath}`;
const headers = {
...(token && { authorization: `Bearer ${token}` }), // Replace path parameters (`{param}`)
'Content-Type': 'application/json' 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<string, string> = {
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
}; };
let requestOptions: RequestInit = { let requestOptions: RequestInit = {
@ -370,15 +406,14 @@ export const executeToolServer = async (
headers headers
}; };
// Handle request body for POST, PUT, PATCH
if (['post', 'put', 'patch'].includes(httpMethod.toLowerCase()) && operation.requestBody) { 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); const res = await fetch(finalUrl, requestOptions);
if (!res.ok) { 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(); return await res.json();

View File

@ -999,7 +999,6 @@
return; return;
} }
console.log('keypress', e);
// Prevent Enter key from creating a new line // Prevent Enter key from creating a new line
const isCtrlPressed = e.ctrlKey || e.metaKey; const isCtrlPressed = e.ctrlKey || e.metaKey;
const enterPressed = const enterPressed =

View File

@ -25,7 +25,8 @@
temporaryChatEnabled, temporaryChatEnabled,
isLastActiveTab, isLastActiveTab,
isApp, isApp,
appInfo appInfo,
toolServers
} from '$lib/stores'; } from '$lib/stores';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/stores';
@ -204,9 +205,9 @@
}; };
const executeTool = async (data, cb) => { 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) { if (toolServer) {
const res = await executeToolServer( const res = await executeToolServer(
@ -215,13 +216,9 @@
data?.name, data?.name,
data?.params, data?.params,
toolServer toolServer
).catch((error) => { );
console.error('executeToolServer', error);
return {
error: error
};
});
console.log('executeToolServer', res);
if (cb) { if (cb) {
cb(JSON.parse(JSON.stringify(res))); cb(JSON.parse(JSON.stringify(res)));
} }