2025-03-05 15:09:10 +01:00
|
|
|
import { TripPlannerState, TripPlannerUpdate } from "../types";
|
2025-03-03 16:51:46 -08:00
|
|
|
import { ChatOpenAI } from "@langchain/openai";
|
|
|
|
|
import { typedUi } from "@langchain/langgraph-sdk/react-ui/server";
|
|
|
|
|
import type ComponentMap from "../../uis/index";
|
|
|
|
|
import { z } from "zod";
|
|
|
|
|
import { LangGraphRunnableConfig } from "@langchain/langgraph";
|
2025-03-05 11:39:08 -08:00
|
|
|
import { getAccommodationsListProps } from "../utils/get-accommodations";
|
2025-03-06 11:42:39 -08:00
|
|
|
import { findToolCall } from "../../find-tool-call";
|
2025-03-03 16:51:46 -08:00
|
|
|
|
2025-03-06 11:42:39 -08:00
|
|
|
const listAccommodationsSchema = z.object({}).describe("A tool to list accommodations for the user")
|
|
|
|
|
const bookAccommodationSchema = z.object({
|
|
|
|
|
accommodationName: z.string().describe("The name of the accommodation to book a reservation for"),
|
|
|
|
|
}).describe("A tool to book a reservation for an accommodation");
|
|
|
|
|
const listRestaurantsSchema = z.object({}).describe("A tool to list restaurants for the user");
|
|
|
|
|
const bookRestaurantSchema = z.object({
|
|
|
|
|
restaurantName: z.string().describe("The name of the restaurant to book a reservation for"),
|
|
|
|
|
}).describe("A tool to book a reservation for a restaurant");
|
2025-03-03 16:51:46 -08:00
|
|
|
|
2025-03-06 11:42:39 -08:00
|
|
|
const ACCOMMODATIONS_TOOLS = [
|
|
|
|
|
{
|
|
|
|
|
name: "list-accommodations",
|
|
|
|
|
description: "A tool to list accommodations for the user",
|
|
|
|
|
schema: listAccommodationsSchema,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "book-accommodation",
|
|
|
|
|
description: "A tool to book a reservation for an accommodation",
|
|
|
|
|
schema: bookAccommodationSchema,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "list-restaurants",
|
|
|
|
|
description: "A tool to list restaurants for the user",
|
|
|
|
|
schema: listRestaurantsSchema,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "book-restaurant",
|
|
|
|
|
description: "A tool to book a reservation for a restaurant",
|
|
|
|
|
schema: bookRestaurantSchema,
|
|
|
|
|
},
|
|
|
|
|
];
|
2025-03-03 16:51:46 -08:00
|
|
|
|
|
|
|
|
export async function callTools(
|
|
|
|
|
state: TripPlannerState,
|
|
|
|
|
config: LangGraphRunnableConfig,
|
2025-03-05 15:09:10 +01:00
|
|
|
): Promise<TripPlannerUpdate> {
|
2025-03-03 16:51:46 -08:00
|
|
|
if (!state.tripDetails) {
|
|
|
|
|
throw new Error("No trip details found");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ui = typedUi<typeof ComponentMap>(config);
|
|
|
|
|
|
2025-03-06 11:42:39 -08:00
|
|
|
const llm = new ChatOpenAI({ model: "gpt-4o", temperature: 0 }).bindTools(ACCOMMODATIONS_TOOLS);
|
2025-03-03 16:51:46 -08:00
|
|
|
|
|
|
|
|
const response = await llm.invoke([
|
|
|
|
|
{
|
|
|
|
|
role: "system",
|
|
|
|
|
content:
|
|
|
|
|
"You are an AI assistant who helps users book trips. Use the user's most recent message(s) to contextually generate a response.",
|
|
|
|
|
},
|
|
|
|
|
...state.messages,
|
|
|
|
|
]);
|
|
|
|
|
|
2025-03-06 11:42:39 -08:00
|
|
|
const listAccommodationsToolCall = response.tool_calls?.find(
|
|
|
|
|
findToolCall("list-accommodations")<typeof listAccommodationsSchema>,
|
|
|
|
|
);
|
|
|
|
|
const bookAccommodationToolCall = response.tool_calls?.find(
|
|
|
|
|
findToolCall("book-accommodation")<typeof bookAccommodationSchema>,
|
|
|
|
|
);
|
|
|
|
|
const listRestaurantsToolCall = response.tool_calls?.find(
|
|
|
|
|
findToolCall("list-restaurants")<typeof listRestaurantsSchema>,
|
|
|
|
|
);
|
|
|
|
|
const bookRestaurantToolCall = response.tool_calls?.find(
|
|
|
|
|
findToolCall("book-restaurant")<typeof bookRestaurantSchema>,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!listAccommodationsToolCall && !bookAccommodationToolCall && !listRestaurantsToolCall && !bookRestaurantToolCall) {
|
|
|
|
|
throw new Error("No tool calls found");
|
2025-03-03 16:51:46 -08:00
|
|
|
}
|
|
|
|
|
|
2025-03-06 11:42:39 -08:00
|
|
|
if (listAccommodationsToolCall) {
|
2025-03-05 13:32:51 -08:00
|
|
|
ui.write("accommodations-list", {
|
2025-03-06 11:42:39 -08:00
|
|
|
toolCallId: listAccommodationsToolCall.id ?? "",
|
2025-03-05 13:32:51 -08:00
|
|
|
...getAccommodationsListProps(state.tripDetails),
|
|
|
|
|
});
|
2025-03-03 16:51:46 -08:00
|
|
|
}
|
2025-03-06 11:42:39 -08:00
|
|
|
if (bookAccommodationToolCall && bookAccommodationToolCall.args.accommodationName) {
|
2025-03-03 16:51:46 -08:00
|
|
|
ui.write("book-accommodation", {
|
|
|
|
|
tripDetails: state.tripDetails,
|
2025-03-06 11:42:39 -08:00
|
|
|
accommodationName: bookAccommodationToolCall.args.accommodationName,
|
2025-03-03 16:51:46 -08:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-06 11:42:39 -08:00
|
|
|
if (listRestaurantsToolCall) {
|
2025-03-03 16:51:46 -08:00
|
|
|
ui.write("restaurants-list", { tripDetails: state.tripDetails });
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-06 11:42:39 -08:00
|
|
|
if (bookRestaurantToolCall && bookRestaurantToolCall.args.restaurantName) {
|
2025-03-03 16:51:46 -08:00
|
|
|
ui.write("book-restaurant", {
|
|
|
|
|
tripDetails: state.tripDetails,
|
2025-03-06 11:42:39 -08:00
|
|
|
restaurantName: bookRestaurantToolCall.args.restaurantName,
|
2025-03-03 16:51:46 -08:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
messages: [response],
|
2025-03-05 12:09:57 -08:00
|
|
|
ui: ui.collect as TripPlannerUpdate["ui"],
|
2025-03-03 16:51:46 -08:00
|
|
|
timestamp: Date.now(),
|
|
|
|
|
};
|
|
|
|
|
}
|