feat: Basic trip planner agent

This commit is contained in:
bracesproul
2025-03-03 16:51:46 -08:00
parent 5256efb23f
commit da396ac83e
22 changed files with 471 additions and 13 deletions

View File

@@ -4,18 +4,22 @@ import { z } from "zod";
import { GenerativeUIAnnotation, GenerativeUIState } from "./types";
import { stockbrokerGraph } from "./stockbroker";
import { ChatOpenAI } from "@langchain/openai";
import { tripPlannerGraph } from "./trip-planner";
const allToolDescriptions = `- stockbroker: can fetch the price of a ticker, purchase/sell a ticker, or get the user's portfolio
- tripPlanner: helps the user plan their trip. it can suggest restaurants, and places to stay in any given location.`;
async function router(
state: GenerativeUIState,
): Promise<Partial<GenerativeUIState>> {
const routerDescription = `The route to take based on the user's input.
- stockbroker: can fetch the price of a ticker, purchase/sell a ticker, or get the user's portfolio
- weather: can fetch the current weather conditions for a location
- tripPlanner: helps the user plan their trip. it can suggest restaurants, and places to stay in any given location.
- generalInput: handles all other cases where the above tools don't apply
`;
const routerSchema = z.object({
route: z
.enum(["stockbroker", "weather", "generalInput"])
.enum(["stockbroker", "tripPlanner", "generalInput"])
.describe(routerDescription),
});
const routerTool = {
@@ -61,13 +65,19 @@ You should analyze the user's input, and choose the appropriate tool to use.`;
function handleRoute(
state: GenerativeUIState,
): "stockbroker" | "weather" | "generalInput" {
): "stockbroker" | "tripPlanner" | "generalInput" {
return state.next;
}
async function handleGeneralInput(state: GenerativeUIState) {
const llm = new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 });
const response = await llm.invoke(state.messages);
const response = await llm.invoke([
{
role: "system",
content: `You are an AI assistant.\nIf the user asks what you can do, describe these tools. Otherwise, just answer as normal.\n\n${allToolDescriptions}`,
},
...state.messages,
]);
return {
messages: [response],
@@ -77,19 +87,17 @@ async function handleGeneralInput(state: GenerativeUIState) {
const builder = new StateGraph(GenerativeUIAnnotation)
.addNode("router", router)
.addNode("stockbroker", stockbrokerGraph)
.addNode("weather", () => {
throw new Error("Weather not implemented");
})
.addNode("tripPlanner", tripPlannerGraph)
.addNode("generalInput", handleGeneralInput)
.addConditionalEdges("router", handleRoute, [
"stockbroker",
"weather",
"tripPlanner",
"generalInput",
])
.addEdge(START, "router")
.addEdge("stockbroker", END)
.addEdge("weather", END)
.addEdge("tripPlanner", END)
.addEdge("generalInput", END);
export const graph = builder.compile();