mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-29 10:26:23 +02:00
restore webhook tool calling
This commit is contained in:
parent
1b47a03ff1
commit
2ee4f37464
2 changed files with 122 additions and 1 deletions
|
|
@ -7,6 +7,8 @@ import { CoreMessage, embed, generateText } from "ai";
|
|||
import { ObjectId } from "mongodb";
|
||||
import { z } from "zod";
|
||||
import { Composio } from '@composio/core';
|
||||
import { SignJWT } from "jose";
|
||||
import crypto from "crypto";
|
||||
|
||||
// Internal dependencies
|
||||
import { embeddingModel } from '../lib/embedding';
|
||||
|
|
@ -167,6 +169,84 @@ async function invokeRagTool(
|
|||
return results;
|
||||
}
|
||||
|
||||
export async function invokeWebhookTool(
|
||||
logger: PrefixLogger,
|
||||
projectId: string,
|
||||
name: string,
|
||||
input: any,
|
||||
): Promise<unknown> {
|
||||
logger = logger.child(`invokeWebhookTool`);
|
||||
logger.log(`projectId: ${projectId}`);
|
||||
logger.log(`name: ${name}`);
|
||||
logger.log(`input: ${JSON.stringify(input)}`);
|
||||
|
||||
const project = await projectsCollection.findOne({
|
||||
"_id": projectId,
|
||||
});
|
||||
if (!project) {
|
||||
throw new Error('Project not found');
|
||||
}
|
||||
|
||||
if (!project.webhookUrl) {
|
||||
throw new Error('Webhook URL not found');
|
||||
}
|
||||
|
||||
// prepare request body
|
||||
const toolCall: z.infer<typeof AssistantMessageWithToolCalls.shape.toolCalls>[number] = {
|
||||
id: crypto.randomUUID(),
|
||||
type: "function",
|
||||
function: {
|
||||
name,
|
||||
arguments: JSON.stringify(input),
|
||||
},
|
||||
}
|
||||
const content = JSON.stringify({
|
||||
toolCall,
|
||||
});
|
||||
const requestId = crypto.randomUUID();
|
||||
const bodyHash = crypto
|
||||
.createHash('sha256')
|
||||
.update(content, 'utf8')
|
||||
.digest('hex');
|
||||
|
||||
// sign request
|
||||
const jwt = await new SignJWT({
|
||||
requestId,
|
||||
projectId,
|
||||
bodyHash,
|
||||
})
|
||||
.setProtectedHeader({
|
||||
alg: 'HS256',
|
||||
typ: 'JWT',
|
||||
})
|
||||
.setIssuer('rowboat')
|
||||
.setAudience(project.webhookUrl)
|
||||
.setSubject(`tool-call-${toolCall.id}`)
|
||||
.setJti(requestId)
|
||||
.setIssuedAt()
|
||||
.setExpirationTime("5 minutes")
|
||||
.sign(new TextEncoder().encode(project.secret));
|
||||
|
||||
// make request
|
||||
const request = {
|
||||
requestId,
|
||||
content,
|
||||
};
|
||||
const response = await fetch(project.webhookUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-signature-jwt': jwt,
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to call webhook: ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
const responseBody = await response.json();
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
// Helper to handle MCP tool calls
|
||||
async function invokeMcpTool(
|
||||
logger: PrefixLogger,
|
||||
|
|
@ -296,6 +376,40 @@ function createMockTool(
|
|||
});
|
||||
}
|
||||
|
||||
// Helper to create a webhook tool
|
||||
function createWebhookTool(
|
||||
logger: PrefixLogger,
|
||||
config: z.infer<typeof WorkflowTool>,
|
||||
projectId: string,
|
||||
): Tool {
|
||||
const { name, description, parameters } = config;
|
||||
|
||||
return tool({
|
||||
name,
|
||||
description,
|
||||
strict: false,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: parameters.properties,
|
||||
required: parameters.required || [],
|
||||
additionalProperties: true,
|
||||
},
|
||||
async execute(input: any) {
|
||||
try {
|
||||
const result = await invokeWebhookTool(logger, projectId, name, input);
|
||||
return JSON.stringify({
|
||||
result,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.log(`Error executing webhook tool ${config.name}:`, error);
|
||||
return JSON.stringify({
|
||||
error: `Tool execution failed: ${error}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Helper to create an mcp tool
|
||||
function createMcpTool(
|
||||
logger: PrefixLogger,
|
||||
|
|
@ -678,7 +792,8 @@ function createTools(logger: PrefixLogger, workflow: z.infer<typeof Workflow>, t
|
|||
tools[toolName] = createMockTool(logger, config);
|
||||
logger.log(`created mock tool: ${toolName}`);
|
||||
} else {
|
||||
logger.log(`unsupported tool type: ${toolName}`);
|
||||
tools[toolName] = createWebhookTool(logger, config, workflow.projectId);
|
||||
logger.log(`created webhook tool: ${toolName}`);
|
||||
}
|
||||
}
|
||||
return tools;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useState } from 'react';
|
|||
import { Tabs, Tab } from '@/components/ui/tabs';
|
||||
import { HostedServers } from './HostedServers';
|
||||
import { CustomServers } from './CustomServers';
|
||||
import { WebhookConfig } from './WebhookConfig';
|
||||
import { Composio } from './Composio';
|
||||
import type { Key } from 'react';
|
||||
|
||||
|
|
@ -62,6 +63,11 @@ export function ToolsConfig({
|
|||
<CustomServers />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab key="webhook" title="Webhook">
|
||||
<div className="mt-4 p-6">
|
||||
<WebhookConfig />
|
||||
</div>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue