/** * Polderfest 2027 — chat API route * -------------------------------------------------- * Les 11 — Vercel AI SDK + Supabase context. * Plaats dit bestand op: app/api/chat/route.ts * * Werking: * 1. Haal alle bands op uit Supabase * 2. Formatteer als tekst-context * 3. Stuur naar OpenAI via streamText + system prompt * 4. Return een stream voor useChat * * Vereist: * - NEXT_PUBLIC_SUPABASE_URL en NEXT_PUBLIC_SUPABASE_ANON_KEY in .env.local * - OPENAI_API_KEY in .env.local * - npm i ai @ai-sdk/openai @supabase/supabase-js */ import { convertToModelMessages, streamText, type UIMessage } from "ai"; import { openai } from "@ai-sdk/openai"; import { createClient } from "@supabase/supabase-js"; const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY ?? process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; if (!supabaseUrl) { throw new Error("Missing env: NEXT_PUBLIC_SUPABASE_URL"); } if (!supabaseKey) { throw new Error( "Missing env: SUPABASE_SERVICE_ROLE_KEY (preferred) or NEXT_PUBLIC_SUPABASE_ANON_KEY", ); } const supabase = createClient(supabaseUrl, supabaseKey); export async function POST(req: Request) { try { const { messages }: { messages: UIMessage[] } = await req.json(); // 1. Haal alle bands op uit Supabase const { data: bands, error } = await supabase.from("bands").select("*"); if (error) { return Response.json( { error: "Supabase query failed", details: error.message }, { status: 500 }, ); } // 2. Format bands als context-string const context = (bands ?? []) .map( (b) => `- ${b.name} (${b.genre}, ${b.tier}, ${b.day} ${b.start_time} ` + `op ${b.stage}, uit ${b.origin_city}), populariteit: ${b.popularity}`, ) .join("\n"); // 3. System prompt met context const system = `Je bent een festival-assistent voor Polderfest 2027. Hier zijn alle bands die op het festival spelen: ${context} Beantwoord vragen van bezoekers over de line-up. Verzin niets — gebruik alleen bovenstaande data. Antwoord in het Nederlands. Wees beknopt. Geef puur antwoord op de gestelde vraag en voeg geen extra informatie toe. Zodra iemand iets vraagt over het schema. Geef dan altijd een volledig schema terug met band, stage en start en eind tijd. Sorteer op start tijd. Wanneer een band speelt op bijvoorbeeld vrijdagavond, geef hem dan niet weer als iemand vraagt om de bands van zaterdag. Regels: - Zaterdag start om 16:00 `; // 4. Stream naar OpenAI const result = streamText({ model: openai('gpt-5.2'), system, messages: await convertToModelMessages(messages), }); return result.toUIMessageStreamResponse({ onError(error: unknown) { if (error == null) return "unknown error"; if (typeof error === "string") return error; if (error instanceof Error) return error.message; return JSON.stringify(error); }, }); } catch (err) { const message = err instanceof Error ? err.message : "Unknown error"; return Response.json({ error: "Chat route failed", details: message }, { status: 500 }); } }