From fd3fffd7dde8948e28008def07c430e03b3baa95 Mon Sep 17 00:00:00 2001 From: Dexter Storey <36115192+DexterStorey@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:26:39 -0500 Subject: [PATCH] feat: add booking date range filters and freeze GPT-4 version (#12508) * add booking date range filters * fix dateFrom, dateTo strings * simplify temporal filters * Freeze GPT-4-0613 * parse and validate booking dateFrom and dateTo fields * add weekday to agent prompt --- apps/ai/src/tools/getBookings.ts | 6 +++--- apps/ai/src/utils/agent.ts | 11 +++++++++-- apps/ai/src/utils/now.ts | 3 ++- apps/api/lib/validations/booking.ts | 5 +++++ apps/api/pages/api/bookings/_get.ts | 19 ++++++++++++++++++- 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/apps/ai/src/tools/getBookings.ts b/apps/ai/src/tools/getBookings.ts index 2ccb563daf..367e78f9ef 100644 --- a/apps/ai/src/tools/getBookings.ts +++ b/apps/ai/src/tools/getBookings.ts @@ -22,6 +22,8 @@ const fetchBookings = async ({ const params = { apiKey, userId: userId.toString(), + dateFrom: from, + dateTo: to, }; const urlParams = new URLSearchParams(params); @@ -40,11 +42,9 @@ const fetchBookings = async ({ const bookings: Booking[] = data.bookings .filter((booking: Booking) => { - const afterFrom = new Date(booking.startTime).getTime() > new Date(from).getTime(); - const beforeTo = new Date(booking.endTime).getTime() < new Date(to).getTime(); const notCancelled = booking.status !== BOOKING_STATUS.CANCELLED; - return afterFrom && beforeTo && notCancelled; + return notCancelled; }) .map(({ endTime, eventTypeId, id, startTime, status, title }: Booking) => ({ endTime, diff --git a/apps/ai/src/utils/agent.ts b/apps/ai/src/utils/agent.ts index 844b4a7747..e97000d37f 100644 --- a/apps/ai/src/utils/agent.ts +++ b/apps/ai/src/utils/agent.ts @@ -13,7 +13,7 @@ import type { User, UserList } from "../types/user"; import type { WorkingHours } from "../types/workingHours"; import now from "./now"; -const gptModel = "gpt-4"; +const gptModel = "gpt-4-0613"; /** * Core of the Cal.ai booking agent: a LangChain Agent Executor. @@ -58,7 +58,14 @@ If you can't find a referenced user, ask the user for their email or @username. The primary user's id is: ${userId} The primary user's username is: ${user.username} -The current time in the primary user's timezone is: ${now(user.timeZone)} +The current time in the primary user's timezone is: ${now(user.timeZone, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "numeric", + })} The primary user's time zone is: ${user.timeZone} The primary user's event types are: ${user.eventTypes .map((e: EventType) => `ID: ${e.id}, Slug: ${e.slug}, Title: ${e.title}, Length: ${e.length};`) diff --git a/apps/ai/src/utils/now.ts b/apps/ai/src/utils/now.ts index fc77993ca0..98630d7bc4 100644 --- a/apps/ai/src/utils/now.ts +++ b/apps/ai/src/utils/now.ts @@ -1,5 +1,6 @@ -export default function now(timeZone: string) { +export default function now(timeZone: string, options: Intl.DateTimeFormatOptions = {}) { return new Date().toLocaleString("en-US", { timeZone, + ...options, }); } diff --git a/apps/api/lib/validations/booking.ts b/apps/api/lib/validations/booking.ts index 950c6ab98c..6b5e84298c 100644 --- a/apps/api/lib/validations/booking.ts +++ b/apps/api/lib/validations/booking.ts @@ -18,6 +18,11 @@ const schemaBookingBaseBodyParams = Booking.pick({ export const schemaBookingCreateBodyParams = extendedBookingCreateBody.merge(schemaQueryUserId.partial()); +export const schemaBookingGetParams = z.object({ + dateFrom: iso8601.optional(), + dateTo: iso8601.optional(), +}); + const schemaBookingEditParams = z .object({ title: z.string().optional(), diff --git a/apps/api/pages/api/bookings/_get.ts b/apps/api/pages/api/bookings/_get.ts index 16bd501a7b..e898653621 100644 --- a/apps/api/pages/api/bookings/_get.ts +++ b/apps/api/pages/api/bookings/_get.ts @@ -4,7 +4,7 @@ import type { NextApiRequest } from "next"; import { HttpError } from "@calcom/lib/http-error"; import { defaultResponder } from "@calcom/lib/server"; -import { schemaBookingReadPublic } from "~/lib/validations/booking"; +import { schemaBookingGetParams, schemaBookingReadPublic } from "~/lib/validations/booking"; import { schemaQuerySingleOrMultipleAttendeeEmails } from "~/lib/validations/shared/queryAttendeeEmail"; import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/queryUserId"; @@ -162,6 +162,9 @@ function buildWhereClause( async function handler(req: NextApiRequest) { const { userId, isAdmin, prisma } = req; + + const { dateFrom, dateTo } = schemaBookingGetParams.parse(req.query); + const args: Prisma.BookingFindManyArgs = {}; args.include = { attendees: true, @@ -203,6 +206,20 @@ async function handler(req: NextApiRequest) { } args.where = buildWhereClause(userId, attendeeEmails, [], []); } + + if (dateFrom) { + args.where = { + ...args.where, + startTime: { gte: dateFrom }, + }; + } + if (dateTo) { + args.where = { + ...args.where, + endTime: { lte: dateTo }, + }; + } + const data = await prisma.booking.findMany(args); return { bookings: data.map((booking) => schemaBookingReadPublic.parse(booking)) }; }