add les 12
This commit is contained in:
BIN
Les11-AI-SDK/Les11-Slides.key
Executable file
BIN
Les11-AI-SDK/Les11-Slides.key
Executable file
Binary file not shown.
68
Les11-AI-SDK/page.tsx
Normal file
68
Les11-AI-SDK/page.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Polderfest 2027 — chat pagina
|
||||
* --------------------------------------------------
|
||||
* Les 11 — useChat hook + Tailwind chat UI.
|
||||
* Plaats dit bestand op: app/chat/page.tsx
|
||||
*
|
||||
* Werking:
|
||||
* - useChat() regelt messages, input, submit-handler, streaming
|
||||
* - Praat met /api/chat (de route.ts)
|
||||
* - Disabled tijdens streaming
|
||||
*
|
||||
* Vereist:
|
||||
* - app/api/chat/route.ts (zie route.ts)
|
||||
* - npm i ai
|
||||
* - Tailwind aanwezig in project (standaard in create-next-app)
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { useChat } from "ai/react";
|
||||
|
||||
export default function ChatPage() {
|
||||
const { messages, input, handleInputChange, handleSubmit, status } =
|
||||
useChat();
|
||||
|
||||
return (
|
||||
<main className="max-w-2xl mx-auto p-6 flex flex-col h-screen">
|
||||
<h1 className="text-2xl font-bold mb-4">
|
||||
Polderfest 2027 — vraag de AI
|
||||
</h1>
|
||||
|
||||
<div className="flex-1 overflow-y-auto space-y-4 mb-4">
|
||||
{messages.map((m) => (
|
||||
<div
|
||||
key={m.id}
|
||||
className={
|
||||
m.role === "user"
|
||||
? "bg-blue-50 p-3 rounded-lg ml-12"
|
||||
: "bg-gray-50 p-3 rounded-lg mr-12"
|
||||
}
|
||||
>
|
||||
<div className="font-medium text-sm text-gray-500 mb-1">
|
||||
{m.role === "user" ? "Jij" : "Festival AI"}
|
||||
</div>
|
||||
<div className="whitespace-pre-wrap">{m.content}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="flex gap-2">
|
||||
<input
|
||||
value={input}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Stel een vraag over de line-up..."
|
||||
className="flex-1 p-3 border rounded-lg"
|
||||
disabled={status !== "ready"}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status !== "ready"}
|
||||
className="px-6 py-3 bg-blue-600 text-white rounded-lg disabled:opacity-50"
|
||||
>
|
||||
Stuur
|
||||
</button>
|
||||
</form>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
1
Les11-AI-SDK/polderfest-demo
Submodule
1
Les11-AI-SDK/polderfest-demo
Submodule
Submodule Les11-AI-SDK/polderfest-demo added at ac06b4d59e
61
Les11-AI-SDK/route.ts
Normal file
61
Les11-AI-SDK/route.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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 { streamText } from "ai";
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
);
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const { messages } = await req.json();
|
||||
|
||||
// 1. Haal alle bands op uit Supabase
|
||||
const { data: bands, error } = await supabase.from("bands").select("*");
|
||||
if (error) throw error;
|
||||
|
||||
// 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})`,
|
||||
)
|
||||
.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.`;
|
||||
|
||||
// 4. Stream naar OpenAI
|
||||
const result = streamText({
|
||||
model: openai("gpt-4o-mini"),
|
||||
system,
|
||||
messages,
|
||||
});
|
||||
|
||||
return result.toDataStreamResponse();
|
||||
}
|
||||
@@ -15,13 +15,27 @@
|
||||
*/
|
||||
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
import "dotenv/config";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
const supabase = createClient(
|
||||
process.env.SUPABASE_URL!,
|
||||
process.env.SUPABASE_SERVICE_ROLE_KEY!,
|
||||
{ auth: { persistSession: false } }
|
||||
);
|
||||
// Laad .env.local (i.p.v. default .env)
|
||||
dotenv.config({ path: ".env.local" });
|
||||
|
||||
const SUPABASE_URL =
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL ?? process.env.SUPABASE_URL;
|
||||
const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
||||
|
||||
if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) {
|
||||
console.error(
|
||||
"Ontbrekende env vars. Check .env.local:\n" +
|
||||
" NEXT_PUBLIC_SUPABASE_URL=https://<project>.supabase.co\n" +
|
||||
" SUPABASE_SERVICE_ROLE_KEY=<service role key>"
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, {
|
||||
auth: { persistSession: false },
|
||||
});
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// Deterministische random (zodat seed reproduceerbaar is)
|
||||
@@ -189,7 +203,7 @@ function generateBio(name: string): string {
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// Hoofdfunctie
|
||||
// ────────────────────────────────────────────────────────────
|
||||
async function seed() {
|
||||
async function runSeed() {
|
||||
console.log("Genereren van 500 Polderfest bands...");
|
||||
|
||||
// Wipe bestaande data (optioneel)
|
||||
@@ -252,7 +266,7 @@ async function seed() {
|
||||
console.log("Klaar! 500 Polderfest bands staan in Supabase.");
|
||||
}
|
||||
|
||||
seed().catch((e) => {
|
||||
runSeed().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user