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( schema: T, userPrompt: string, ): Promise> { 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 = { 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); }