add: human-in-the-loop tool-calling
This commit is contained in:
@@ -5,6 +5,7 @@ import { singleSpace } from "../util.js";
|
||||
export const personalAssistantAgent: Agent = {
|
||||
id: "personal-assistant",
|
||||
name: "Personal Assistant",
|
||||
// modelName: "qwen/qwen3-32b:free",
|
||||
// modelName: "mistral/ministral-8b",
|
||||
modelName: "google/gemini-2.5-flash-preview",
|
||||
systemMessage:
|
||||
@@ -78,21 +79,21 @@ export const personalAssistantAgent: Agent = {
|
||||
// },
|
||||
}),
|
||||
|
||||
say: tool({
|
||||
description: "Say something.",
|
||||
parameters: jsonSchema<{ message: string }>({
|
||||
type: "object",
|
||||
properties: {
|
||||
message: {
|
||||
type: "string",
|
||||
description: "The message to say.",
|
||||
},
|
||||
},
|
||||
}),
|
||||
execute: async ({ message }: { message: string }) => {
|
||||
console.log(message);
|
||||
},
|
||||
}),
|
||||
// say: tool({
|
||||
// description: "Say something.",
|
||||
// parameters: jsonSchema<{ message: string }>({
|
||||
// type: "object",
|
||||
// properties: {
|
||||
// message: {
|
||||
// type: "string",
|
||||
// description: "The message to say.",
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// execute: async ({ message }: { message: string }) => {
|
||||
// return message;
|
||||
// },
|
||||
// }),
|
||||
exit: tool({
|
||||
description: "Exits the conversation.",
|
||||
parameters: jsonSchema<{ exitCode: number }>({
|
||||
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
delegate: async function () {
|
||||
return "Here's a vegetarian lasagna recipe for 4 people:";
|
||||
},
|
||||
};
|
||||
@@ -1,3 +1,30 @@
|
||||
import { Message } from "ai";
|
||||
import tools from "./tools.js";
|
||||
|
||||
export function singleSpace(str: string) {
|
||||
return str.replace(/\s+/g, " ");
|
||||
}
|
||||
|
||||
export async function processPendingToolCalls(messages: Message[]) {
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
if (!lastMessage) {
|
||||
return;
|
||||
}
|
||||
if (!lastMessage.parts) {
|
||||
return;
|
||||
}
|
||||
/** Execute all the pending tool calls: */
|
||||
lastMessage.parts = await Promise.all(
|
||||
lastMessage.parts?.map(async (part) => {
|
||||
const toolInvocation = part.toolInvocation;
|
||||
if (toolInvocation?.state === "call") {
|
||||
toolInvocation.state = "result";
|
||||
toolInvocation.result =
|
||||
toolInvocation.result === "yes"
|
||||
? await tools[toolInvocation.toolName]?.()
|
||||
: "Error: User denied tool call.";
|
||||
}
|
||||
return part;
|
||||
}) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
+24
-10
@@ -1,10 +1,11 @@
|
||||
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
||||
import { streamText } from "ai";
|
||||
import { streamText, Message } from "ai";
|
||||
import { Hono } from "hono";
|
||||
import { stream } from "hono/streaming";
|
||||
import { personalAssistantAgent } from "./agents/assistant.js";
|
||||
import { personalAssistantAgent } from "./agents/personalAssistant.js";
|
||||
import { chefAgent } from "./agents/chef.js";
|
||||
import { Agent } from "./types.js";
|
||||
import { processPendingToolCalls } from "./util.js";
|
||||
|
||||
// This declaration is primarily for providing type hints in your code
|
||||
// and it doesn't directly define the *values* of the environment variables.
|
||||
@@ -24,27 +25,40 @@ const openrouter = createOpenRouter({
|
||||
apiKey: import.meta.env.VITE_OPENROUTER_API_KEY || env.OPENROUTER_API_KEY,
|
||||
});
|
||||
|
||||
const agentsByName: Record<string, Agent> = {
|
||||
assistant: personalAssistantAgent,
|
||||
const agentsById: Record<string, Agent> = {
|
||||
"personal-assistant": personalAssistantAgent,
|
||||
chef: chefAgent,
|
||||
};
|
||||
|
||||
app.post("/api/chat/:agent_name", async (c) => {
|
||||
const input = await c.req.json();
|
||||
const agentName = c.req.param("agent_name");
|
||||
const agent = agentsByName[agentName];
|
||||
app.post("/api/chat/:agent_id", async (c) => {
|
||||
const input: { messages: Message[] } = await c.req.json();
|
||||
const agentId = c.req.param("agent_id");
|
||||
const agent = agentsById[agentId];
|
||||
if (!agent) {
|
||||
c.status(404);
|
||||
return c.json({ error: `No such agent: ${agentName}` });
|
||||
return c.json({ error: `No such agent: ${agentId}` });
|
||||
}
|
||||
console.log(input);
|
||||
await processPendingToolCalls(input.messages);
|
||||
const result = streamText({
|
||||
model: openrouter(agent.modelName),
|
||||
maxSteps: 5,
|
||||
messages: [
|
||||
{ role: "system", content: agent.systemMessage },
|
||||
...Object.values(agentsById).map((agent) => ({
|
||||
role: "system" as const,
|
||||
content: `Agent ${JSON.stringify({
|
||||
id: agent.id,
|
||||
name: agent.name,
|
||||
description: agent.description,
|
||||
skills: agent.skills,
|
||||
})}`,
|
||||
})),
|
||||
...input.messages,
|
||||
],
|
||||
tools: agent.tools,
|
||||
onError: (error) => {
|
||||
console.log(error);
|
||||
},
|
||||
});
|
||||
|
||||
// Mark the response as a v1 data stream:
|
||||
|
||||
Reference in New Issue
Block a user