Template
53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
import { streamText, convertToModelMessages, type UIMessage } from "ai";
|
|
import { getModel } from "@/lib/ai/model";
|
|
|
|
// Streaming-Chat-Endpoint (Muster C). Wird von useChat (Default /api/chat) aufgerufen.
|
|
//
|
|
// ACHTUNG: Dieser Endpoint hat KEINEN Login. Wer die Adresse der App kennt, kann hier
|
|
// auf Kosten des hinterlegten ANTHROPIC_API_KEY chatten (offener KI-Proxy).
|
|
// Deshalb ist er standardmäßig AUS und läuft nur mit CHAT_ENABLED=true.
|
|
// Nie mit echtem Key öffentlich veröffentlichen, solange die App keinen Schutz hat
|
|
// (Login, Zugangscode). Die Grenzen unten dämpfen Missbrauch nur, sie verhindern ihn nicht.
|
|
|
|
const MAX_MESSAGES = 40;
|
|
const MAX_REQUEST_CHARS = 60000;
|
|
const MAX_OUTPUT_TOKENS = 1024;
|
|
|
|
function parseJson(text: string): unknown {
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function POST(req: Request) {
|
|
if (process.env.CHAT_ENABLED !== "true") {
|
|
return new Response(
|
|
"KI-Chat ist deaktiviert. Zum Einschalten CHAT_ENABLED=true setzen (nur mit Schutz öffentlich betreiben).",
|
|
{ status: 403 },
|
|
);
|
|
}
|
|
|
|
const raw = await req.text();
|
|
if (raw.length > MAX_REQUEST_CHARS) {
|
|
return new Response("Anfrage zu groß.", { status: 413 });
|
|
}
|
|
|
|
const body = parseJson(raw) as { messages?: unknown } | null;
|
|
const messages = body?.messages;
|
|
if (!Array.isArray(messages) || messages.length === 0) {
|
|
return new Response("Ungültige Anfrage.", { status: 400 });
|
|
}
|
|
if (messages.length > MAX_MESSAGES) {
|
|
return new Response("Der Chat ist zu lang. Bitte die Seite neu laden.", { status: 413 });
|
|
}
|
|
|
|
const result = streamText({
|
|
model: getModel(),
|
|
messages: await convertToModelMessages(messages as UIMessage[]),
|
|
maxOutputTokens: MAX_OUTPUT_TOKENS,
|
|
});
|
|
return result.toUIMessageStreamResponse();
|
|
}
|