98 lines
3.1 KiB
TypeScript
98 lines
3.1 KiB
TypeScript
/**
|
|
* 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 });
|
|
}
|
|
}
|