import { auth } from '@clerk/nextjs/server' import { createOpenAI } from '@ai-sdk/openai' import { streamText, convertToModelMessages, stepCountIs, type UIMessage } from 'ai' 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 { createChatTools } from '~/server/ai/tools' 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 configuredSystemPrompt = await servTrpc.chat.getSystemPrompt() || 'You are an AI recruiter assistant.' const systemPrompt = `${configuredSystemPrompt} Runtime context: - Current server time: ${new Date().toISOString()}. - Default meeting timezone: Europe/Berlin. - For availability questions like "next open spot", call getAvailability once. It defaults to checking from now. Use nextAvailableSlot for the next opening, or the first item in availableSlots if needed. Do not call getAvailability again just to get more slots. - After scheduleMeeting succeeds, include the returned inviteLink or htmlLink in your response. - Do not calculate or invent calendar availability yourself.` const model = await servTrpc.chat.getModel() // 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(model), system: systemPrompt, messages: await convertToModelMessages(messages), tools: createChatTools(), stopWhen: stepCountIs(2), onFinish: async ({ text, finishReason }) => { console.log('[ai:chat:onFinish]', { finishReason, hasText: Boolean(text), textLength: text.length, }) if (text && finishReason === 'stop') { await db.insert(chatMessage).values({ sessionId, role: 'assistant', content: text, }) } }, }) return result.toUIMessageStreamResponse() }