first commit

This commit is contained in:
2026-06-10 18:30:46 +02:00
parent 3e7b0ae2e0
commit cabd4803a4
39 changed files with 3635 additions and 81 deletions
+69
View File
@@ -0,0 +1,69 @@
import { z } from "zod";
const SYSTEM_PROMPT = `You are a film research assistant for an international audience.
You MUST respond in English with valid JSON only — no markdown fences, no commentary.
Use a clear, neutral tone. If uncertain about a scene or timestamp, state uncertainty and set confidence to "low".
Do not invent scenes, quotes, or timestamps. Base answers on the movie context provided.`;
export async function generateStructuredResponse<T extends z.ZodType>(
schema: T,
userPrompt: string,
): Promise<z.infer<T>> {
const apiKey = process.env.LLM_API_KEY;
if (!apiKey) {
throw new Error("LLM_API_KEY is not set");
}
const baseUrl = process.env.LLM_BASE_URL ?? "https://api.openai.com/v1";
const model = process.env.LLM_MODEL ?? "gpt-4o-mini";
const headers: Record<string, string> = {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
};
if (process.env.LLM_HTTP_REFERER) {
headers["HTTP-Referer"] = process.env.LLM_HTTP_REFERER;
}
if (process.env.LLM_APP_TITLE) {
headers["X-Title"] = process.env.LLM_APP_TITLE;
}
const response = await fetch(`${baseUrl}/chat/completions`, {
method: "POST",
headers,
body: JSON.stringify({
model,
temperature: 0.2,
response_format: { type: "json_object" },
messages: [
{ role: "system", content: SYSTEM_PROMPT },
{ role: "user", content: userPrompt },
],
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`LLM request failed: ${response.status} ${errorText}`);
}
const data = (await response.json()) as {
choices: Array<{ message: { content: string } }>;
};
const content = data.choices[0]?.message?.content;
if (!content) {
throw new Error("LLM returned empty content");
}
let parsed: unknown;
try {
parsed = JSON.parse(content);
} catch {
throw new Error("LLM returned invalid JSON");
}
return schema.parse(parsed);
}