import type { Db } from "../db"; import { DEFAULTS, getSetting } from "../routes/settings"; export type LlmDeps = { db: Db; key: Buffer; fetchImpl?: typeof fetch }; export async function chatJSON(deps: LlmDeps, opts: { system: string; user: string }): Promise { const f = deps.fetchImpl ?? fetch; const base = (await getSetting(deps.db, "llm_base_url", deps.key)) ?? DEFAULTS.llm_base_url; const model = (await getSetting(deps.db, "llm_model", deps.key)) ?? DEFAULTS.llm_model; const apiKey = await getSetting(deps.db, "llm_key", deps.key); const headers: Record = { "content-type": "application/json" }; if (apiKey) headers.authorization = `Bearer ${apiKey}`; const res = await f(`${base.replace(/\/$/, "")}/chat/completions`, { method: "POST", headers, body: JSON.stringify({ model, response_format: { type: "json_object" }, messages: [ { role: "system", content: opts.system }, { role: "user", content: opts.user }, ], }), }); if (!res.ok) throw new Error(`llm_error: provider returned ${res.status}`); const data = (await res.json()) as { choices?: { message?: { content?: string } }[] }; const content = data.choices?.[0]?.message?.content; if (!content) throw new Error("llm_error: empty completion"); const stripped = content.replace(/^\s*```(?:json)?\s*/i, "").replace(/\s*```\s*$/, ""); try { return JSON.parse(stripped); } catch { throw new Error("llm_error: completion was not valid JSON"); } }