mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-03 19:25:19 +02:00
attach composio search feature to copilot llm call itself
This commit is contained in:
parent
3fbc739a24
commit
f17bc37583
1 changed files with 30 additions and 50 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { createOpenAI } from "@ai-sdk/openai";
|
import { createOpenAI } from "@ai-sdk/openai";
|
||||||
import { generateObject, generateText, streamText } from "ai";
|
import { generateObject, streamText, tool } from "ai";
|
||||||
import { WithStringId } from "../types/types";
|
import { WithStringId } from "../types/types";
|
||||||
import { Workflow, WorkflowTool } from "../types/workflow_types";
|
import { Workflow, WorkflowTool } from "../types/workflow_types";
|
||||||
import { CopilotChatContext, CopilotMessage } from "../types/copilot_types";
|
import { CopilotChatContext, CopilotMessage } from "../types/copilot_types";
|
||||||
|
|
@ -96,54 +96,31 @@ ${JSON.stringify(simplifiedDataSources)}
|
||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDynamicToolsPrompt(messages: z.infer<typeof CopilotMessage>[], workflow: z.infer<typeof Workflow>): Promise<string> {
|
async function searchRelevantTools(query: string): Promise<string> {
|
||||||
console.log('--- [Co-pilot] Entering Dynamic Tool Creation ---');
|
const logger = new PrefixLogger("copilot-search-tools");
|
||||||
if (!USE_COMPOSIO_TOOLS) {
|
if (!USE_COMPOSIO_TOOLS) {
|
||||||
console.log('[Co-pilot] Dynamic tool creation is disabled.');
|
logger.log("dynamic tool search is disabled");
|
||||||
return '';
|
return 'No tools found!';
|
||||||
}
|
|
||||||
|
|
||||||
// first, check if we need to search for tools at all
|
|
||||||
const startTime = Date.now();
|
|
||||||
const { text } = await generateText({
|
|
||||||
model: openai(COPILOT_MODEL),
|
|
||||||
system: `Your task is to determine if a tool search is required based on the user's request. Respond with a single word: 'yes' or 'no'.
|
|
||||||
|
|
||||||
Say 'yes' if:
|
|
||||||
- The user explicitly mentions a tool or a capability that requires a tool (e.g., "send an email", "connect to an API").
|
|
||||||
- The user describes a goal or workflow that implies the need for external actions or data (e.g., "build an agent to manage my files").
|
|
||||||
- The user asks for a tool to be added to the workflow.
|
|
||||||
- The user asks questions bout how to perform a task.
|
|
||||||
|
|
||||||
Otherwise, say 'no'. Also important to remember if a tool has already been searched and is in context there is no need to search again.`,
|
|
||||||
messages,
|
|
||||||
});
|
|
||||||
console.log("[Co-pilot] Sending the following messages to the LLM for tool search check:", JSON.stringify(messages, null, 2));
|
|
||||||
const endTime = Date.now();
|
|
||||||
console.log(`[Co-pilot] Tool search check took ${endTime - startTime}ms`);
|
|
||||||
console.log("[Co-pilot] LLM response:", text);
|
|
||||||
if (text.toLowerCase() !== "yes") {
|
|
||||||
console.log('[Co-pilot] No tool search needed.');
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const composio = new Composio();
|
const composio = new Composio();
|
||||||
|
|
||||||
// Step 1: Search for relevant tool slugs
|
// Search for relevant tool slugs
|
||||||
console.log('[Co-pilot] 🚀 Searching for relevant tools...');
|
logger.log('searching for relevant tools...');
|
||||||
const searchResult = await composio.tools.execute('COMPOSIO_SEARCH_TOOLS', {
|
const searchResult = await composio.tools.execute('COMPOSIO_SEARCH_TOOLS', {
|
||||||
userId: '0000-0000-0000', // hmmmmm
|
userId: '0000-0000-0000',
|
||||||
arguments: { use_case: messages[messages.length - 1].content }, // use last message
|
arguments: { use_case: query },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!searchResult.successful || !Array.isArray(searchResult.data?.results)) {
|
if (!searchResult.successful || !Array.isArray(searchResult.data?.results)) {
|
||||||
console.warn('[Co-pilot] ⚠️ Tool search was not successful or returned no results.');
|
logger.log("tool search was not successful or returned no results");
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolSlugs: string[] = searchResult.data.results.map((result: any) => result.tool);
|
const toolSlugs: string[] = searchResult.data.results.map((result: any) => result.tool);
|
||||||
console.log(`[Co-pilot] ✅ Found tool slugs: ${toolSlugs.join(', ')}`);
|
logger.log(`found tool slugs: ${toolSlugs.join(', ')}`);
|
||||||
|
|
||||||
|
// Enrich tools with full details
|
||||||
const composioTools = await Promise.all(toolSlugs.map(slug => getTool(slug)));
|
const composioTools = await Promise.all(toolSlugs.map(slug => getTool(slug)));
|
||||||
const workflowTools: z.infer<typeof WorkflowTool>[] = composioTools.map(tool => ({
|
const workflowTools: z.infer<typeof WorkflowTool>[] = composioTools.map(tool => ({
|
||||||
name: tool.name,
|
name: tool.name,
|
||||||
|
|
@ -163,19 +140,14 @@ Otherwise, say 'no'. Also important to remember if a tool has already been searc
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log('--- [Co-pilot] Exiting Dynamic Tool Creation (Success) ---');
|
// Format the response
|
||||||
const toolConfigs = workflowTools.map(tool =>
|
const toolConfigs = workflowTools.map(tool =>
|
||||||
`**${tool.name}**:\n\`\`\`json\n${JSON.stringify(tool, null, 2)}\n\`\`\``
|
`**${tool.name}**:\n\`\`\`json\n${JSON.stringify(tool, null, 2)}\n\`\`\``
|
||||||
).join('\n\n');
|
).join('\n\n');
|
||||||
|
|
||||||
const prompt = `## Tool Suggestions:
|
const response = `The following tools were found:\n\n${toolConfigs}`;
|
||||||
The following are tools suggestions being made by the AI. These are composio tools and they must be added first to the workflow before they can be used.
|
logger.log('returning response', response);
|
||||||
|
return response;
|
||||||
${toolConfigs}
|
|
||||||
|
|
||||||
To add any tool you can copy the above json as an add tool block`;
|
|
||||||
console.log(prompt);
|
|
||||||
return prompt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLastUserMessage(
|
function updateLastUserMessage(
|
||||||
|
|
@ -183,11 +155,10 @@ function updateLastUserMessage(
|
||||||
currentWorkflowPrompt: string,
|
currentWorkflowPrompt: string,
|
||||||
contextPrompt: string,
|
contextPrompt: string,
|
||||||
dataSourcesPrompt: string = '',
|
dataSourcesPrompt: string = '',
|
||||||
dynamicToolsPrompt: string = '',
|
|
||||||
): void {
|
): void {
|
||||||
const lastMessage = messages[messages.length - 1];
|
const lastMessage = messages[messages.length - 1];
|
||||||
if (lastMessage.role === 'user') {
|
if (lastMessage.role === 'user') {
|
||||||
lastMessage.content = `${currentWorkflowPrompt}\n\n${contextPrompt}\n\n${dataSourcesPrompt}\n\n${dynamicToolsPrompt}\n\nUser: ${JSON.stringify(lastMessage.content)}`;
|
lastMessage.content = `${currentWorkflowPrompt}\n\n${contextPrompt}\n\n${dataSourcesPrompt}\n\nUser: ${JSON.stringify(lastMessage.content)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,11 +224,8 @@ export async function* streamMultiAgentResponse(
|
||||||
// set data sources prompt
|
// set data sources prompt
|
||||||
let dataSourcesPrompt = getDataSourcesPrompt(dataSources);
|
let dataSourcesPrompt = getDataSourcesPrompt(dataSources);
|
||||||
|
|
||||||
// get dynamic tools prompt
|
|
||||||
const dynamicToolsPrompt = await getDynamicToolsPrompt(messages, workflow);
|
|
||||||
|
|
||||||
// add the above prompts to the last user message
|
// add the above prompts to the last user message
|
||||||
updateLastUserMessage(messages, currentWorkflowPrompt, contextPrompt, dataSourcesPrompt, dynamicToolsPrompt);
|
updateLastUserMessage(messages, currentWorkflowPrompt, contextPrompt, dataSourcesPrompt);
|
||||||
|
|
||||||
// call model
|
// call model
|
||||||
console.log("calling model", JSON.stringify({
|
console.log("calling model", JSON.stringify({
|
||||||
|
|
@ -267,6 +235,18 @@ export async function* streamMultiAgentResponse(
|
||||||
}));
|
}));
|
||||||
const { textStream } = streamText({
|
const { textStream } = streamText({
|
||||||
model: openai(COPILOT_MODEL),
|
model: openai(COPILOT_MODEL),
|
||||||
|
maxSteps: 5,
|
||||||
|
tools: {
|
||||||
|
"search_relevant_tools": tool({
|
||||||
|
description: "Search for relevant tools",
|
||||||
|
parameters: z.object({
|
||||||
|
query: z.string().describe("the use-case to search for"),
|
||||||
|
}),
|
||||||
|
execute: async ({ query }: { query: string }) => {
|
||||||
|
return await searchRelevantTools(query);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: 'system',
|
role: 'system',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue