import { languageName, type Level } from "@/lib/languages"; import { createInferenceClient } from "@/lib/hf/client"; const MODEL = "mistralai/Mistral-7B-Instruct-v0.3"; export type ExampleSentence = { target: string; gloss: string }; export async function generateSentencesForImage(opts: { captionEn: string; objectsEn: string[]; targetLang: string; nativeLang: string; level: Level; accessToken?: string; }): Promise { const target = languageName(opts.targetLang); const native = languageName(opts.nativeLang); const system = `You are a language tutor. Given a photo caption and list of visible objects, produce example sentences in ${target} ` + `at CEFR ${opts.level} level describing what's in the image. Each sentence must come with a natural ${native} gloss. ` + `Output STRICT JSON: {"sentences": [{"target": string, "gloss": string}, ...]}.`; const user = `Caption: ${opts.captionEn}\nObjects: ${opts.objectsEn.join(", ") || "(none)"}\n`; const hf = createInferenceClient(opts.accessToken); const res = await hf.chatCompletion({ provider: "novita", model: MODEL, messages: [ { role: "system", content: system }, { role: "user", content: user }, ], max_tokens: 400, temperature: 0.6, response_format: { type: "json_object" }, }); const raw = res.choices?.[0]?.message?.content; if (!raw) return []; const parsed = safeParse(raw); const arr = Array.isArray(parsed.sentences) ? parsed.sentences : []; return arr .filter((v: unknown): v is { target: unknown; gloss: unknown } => typeof v === "object" && v !== null) .map((v) => ({ target: String(v.target ?? ""), gloss: String(v.gloss ?? "") })) .filter((v) => v.target); } function safeParse(raw: string): Record { try { return JSON.parse(raw); } catch { const m = raw.match(/\{[\s\S]*\}/); return m ? (JSON.parse(m[0]) as Record) : {}; } }