move rb widget apis to /api/widget/v1

This commit is contained in:
ramnique 2025-01-14 16:26:59 +05:30
parent 7f571092a2
commit 573188afb0
10 changed files with 106 additions and 83 deletions

View file

@ -15,7 +15,7 @@ import crypto from 'crypto';
import { SignJWT } from "jose";
import { Claims, getSession } from "@auth0/nextjs-auth0";
import { revalidatePath } from "next/cache";
import { baseWorkflow } from "./lib/utils";
import { baseWorkflow, callClientToolWebhook, getAgenticApiResponse } from "./lib/utils";
const crawler = new FirecrawlApp({ apiKey: process.env.FIRECRAWL_API_KEY || '' });
@ -466,25 +466,8 @@ export async function getAssistantResponse(
}> {
await projectAuthCheck(projectId);
// call agentic api
const response = await fetch(process.env.AGENTIC_API_URL + '/chat', {
method: 'POST',
body: JSON.stringify(request),
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
console.error('Failed to call agentic api', response);
throw new Error(`Failed to call agentic api: ${response.statusText}`);
}
const responseJson = await response.json();
const result: z.infer<typeof AgenticAPIChatResponse> = responseJson;
return {
messages: convertFromAgenticAPIChatMessages(result.messages),
state: result.state,
rawAPIResponse: result,
};
const response = await getAgenticApiResponse(request);
return response;
}
export async function getCopilotResponse(
@ -916,61 +899,6 @@ export async function executeClientTool(
): Promise<unknown> {
await projectAuthCheck(projectId);
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 content = JSON.stringify({
toolCall,
} as z.infer<typeof ClientToolCallRequestBody>);
const requestId = crypto.randomUUID();
const bodyHash = crypto
.createHash('sha256')
.update(content, 'utf8')
.digest('hex');
// sign request
const jwt = await new SignJWT({
requestId,
projectId,
bodyHash,
} as z.infer<typeof ClientToolCallJwt>)
.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: z.infer<typeof ClientToolCallRequest> = {
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;
const result = await callClientToolWebhook(toolCall, projectId);
return result;
}

View file

@ -4,8 +4,8 @@ import { agentWorkflowsCollection, db, projectsCollection } from "@/app/lib/mong
import { z } from "zod";
import { ObjectId, WithId } from "mongodb";
import { authCheck } from "../../../utils";
import { AgenticAPIChatRequest, convertToAgenticAPIChatMessages, convertToCoreMessages, convertWorkflowToAgenticAPI } from "@/app/lib/types";
import { executeClientTool, getAssistantResponse } from "@/app/actions";
import { AgenticAPIChatRequest, convertToAgenticAPIChatMessages, convertWorkflowToAgenticAPI } from "@/app/lib/types";
import { callClientToolWebhook, getAgenticApiResponse } from "@/app/lib/utils";
const chatsCollection = db.collection<z.infer<typeof apiV1.Chat>>("chats");
const chatMessagesCollection = db.collection<z.infer<typeof apiV1.ChatMessage>>("chatMessages");
@ -91,7 +91,7 @@ export async function POST(
startAgent,
};
console.log("turn: sending agentic request", JSON.stringify(request, null, 2));
const response = await getAssistantResponse(session.projectId, request);
const response = await getAgenticApiResponse(request);
state = response.state;
if (response.messages.length === 0) {
throw new Error("No messages returned from assistant");
@ -111,7 +111,7 @@ export async function POST(
const toolCallResults = await Promise.all(lastMessage.tool_calls.map(async toolCall => {
console.log('executing tool call', toolCall);
try {
return await executeClientTool(toolCall, session.projectId);
return await callClientToolWebhook(toolCall, session.projectId);
} catch (error) {
console.error(`Error executing tool call ${toolCall.id}:`, error);
return { error: "Tool execution failed" };

View file

@ -1,5 +1,9 @@
import { Workflow } from "@/app/lib/types";
import { AgenticAPIChatRequest, AgenticAPIChatResponse, ClientToolCallJwt, ClientToolCallRequest, ClientToolCallRequestBody, convertFromAgenticAPIChatMessages, Workflow } from "@/app/lib/types";
import { z } from "zod";
import { projectsCollection } from "./mongodb";
import { apiV1 } from "rowboat-shared";
import { SignJWT } from "jose";
import crypto from "crypto";
export const baseWorkflow: z.infer<typeof Workflow> = {
projectId: "",
@ -100,4 +104,95 @@ You are an helpful customer support assistant
},
],
tools: [],
};
};
export async function callClientToolWebhook(
toolCall: z.infer<typeof apiV1.AssistantMessageWithToolCalls>['tool_calls'][number],
projectId: string,
): Promise<unknown> {
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 content = JSON.stringify({
toolCall,
} as z.infer<typeof ClientToolCallRequestBody>);
const requestId = crypto.randomUUID();
const bodyHash = crypto
.createHash('sha256')
.update(content, 'utf8')
.digest('hex');
// sign request
const jwt = await new SignJWT({
requestId,
projectId,
bodyHash,
} as z.infer<typeof ClientToolCallJwt>)
.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: z.infer<typeof ClientToolCallRequest> = {
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;
}
export async function getAgenticApiResponse(
request: z.infer<typeof AgenticAPIChatRequest>,
): Promise<{
messages: z.infer<typeof apiV1.ChatMessage>[],
state: unknown,
rawAPIResponse: unknown,
}> {
// call agentic api
const response = await fetch(process.env.AGENTIC_API_URL + '/chat', {
method: 'POST',
body: JSON.stringify(request),
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
console.error('Failed to call agentic api', response);
throw new Error(`Failed to call agentic api: ${response.statusText}`);
}
const responseJson = await response.json();
const result: z.infer<typeof AgenticAPIChatResponse> = responseJson;
return {
messages: convertFromAgenticAPIChatMessages(result.messages),
state: result.state,
rawAPIResponse: result,
};
}