add les 12
This commit is contained in:
97
Les12-Tool-Calling/polderfest-demo/app/api/chat/route.ts
Normal file
97
Les12-Tool-Calling/polderfest-demo/app/api/chat/route.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user