import { auth } from '@clerk/nextjs/server' import { createOpenAI } from '@ai-sdk/openai' import { streamText, tool, convertToModelMessages, stepCountIs, type UIMessage } from 'ai' import { success, z } from 'zod' import { eq, and } from 'drizzle-orm' import { env } from '~/env' import { db } from '~/server/db' import { chatSession, chatMessage } from '~/server/dbschema/schema' import { servTrpc } from '~/app/_trpc/ServerClient' import { scheduleMeeting } from '~/app/actions/scheduleMeeting' import currentTime from '~/app/actions/currentTime'; const openai = createOpenAI({ apiKey: env.OPENAI_API_KEY }) export async function POST(req: Request) { const { userId } = await auth() if (userId == null) return new Response('Unauthorized', { status: 401 }) const { messages, sessionId } = (await req.json()) as { messages: UIMessage[] sessionId: string } // Verify this session belongs to the authenticated user const session = await db .select() .from(chatSession) .where(and(eq(chatSession.id, sessionId), eq(chatSession.userId, userId))) .limit(1) .then((r) => r[0]) if (!session) return new Response('Session not found', { status: 404 }) const systemPrompt = await servTrpc.chat.getSystemPrompt() || 'You are an AI recruiter assistant.' // Save the latest user message const lastMessage = messages[messages.length - 1] if (lastMessage?.role === 'user') { const content = lastMessage.parts .filter((p): p is { type: 'text'; text: string } => p.type === 'text') .map((p) => p.text) .join('') if (content) { await db.insert(chatMessage).values({ sessionId, role: 'user', content }) } } const result = streamText({ model: openai('gpt-5-mini'), system: systemPrompt, messages: await convertToModelMessages(messages), tools: { scheduleMeeting: tool({ description: 'Schedule a meeting with Gregor Lohaus and add it to his Google Calendar', inputSchema: z.object({ title: z.string().describe('Meeting title, make something up if not provided'), description: z.string().describe('Meeting description / agenda, make something up if not provided'), dateTime: z .string() .describe( 'ISO 8601 datetime for the meeting start, e.g. 2025-04-01T10:00:00', ), durationMinutes: z .number() .int() .min(15) .max(120) .describe('Duration of the meeting in minutes, if none provided ask if 20 minutes is ok'), attendeeEmail: z .string() .email() .optional() .describe('Optional Email of the visitor to invite (if provided)'), attendeeName: z.string().optional().describe('Name of the visitor'), }), execute: async (input) => scheduleMeeting({ ...input }), }), getCurrentUnixTime: tool({ description: 'Get the current unix time to reference for meeting dates', inputSchema: z.object({ none: z.string().optional().describe("no inputs are needed") }), execute: async () => currentTime() }) }, stopWhen: stepCountIs(5), onFinish: async ({ text, finishReason }) => { if (text && finishReason === 'stop') { await db.insert(chatMessage).values({ sessionId, role: 'assistant', content: text, }) } }, }) return result.toUIMessageStreamResponse() }