Docs/Skills and code/src/
src/llm-client.js
What the file says about itself
OpenAI-compatible adapter for local Ollama or another compatible endpoint.
/* OpenAI-compatible adapter for local Ollama or another compatible endpoint. */
import { config as appConfig } from "../config.js";
export function getLLMConfig() {
return {
baseUrl: process.env.LLM_BASE_URL ?? appConfig.qualification?.llm?.baseUrl ?? "http://localhost:11434/v1",
model: process.env.LLM_MODEL ?? appConfig.qualification?.llm?.model ?? "gemma4",
apiKey: process.env.LLM_API_KEY ?? appConfig.qualification?.llm?.apiKey ?? "local"
};
}
export async function callLLM({ system, user, temperature = 0.1, maxTokens = 1200 }) {
const settings = getLLMConfig();
if (!settings.model) throw new Error("LLM model is not configured. Set LLM_MODEL or config.qualification.llm.model.");
const url = `${settings.baseUrl.replace(/\/$/, "")}/chat/completions`;
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${settings.apiKey}` },
body: JSON.stringify({ model: settings.model, temperature, max_tokens: maxTokens,
messages: [{ role: "system", content: system }, { role: "user", content: user }] })
});
const body = await response.text();
if (!response.ok) throw new Error(`LLM request failed (${response.status}): ${body.slice(0, 1200)}`);
let parsed;
try { parsed = JSON.parse(body); }
catch { throw new Error(`LLM returned invalid transport JSON: ${body.slice(0, 1000)}`); }
const content = parsed?.choices?.[0]?.message?.content;
if (!content) throw new Error("LLM response contained no message content.");
return { content, model: parsed.model ?? settings.model };
}
export function parseJsonObject(content) {
const text = String(content ?? "").trim();
try { return JSON.parse(text); } catch {}
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
if (fenced) { try { return JSON.parse(fenced[1]); } catch {} }
const startObject = text.indexOf("{");
const endObject = text.lastIndexOf("}");
const startArray = text.indexOf("[");
const endArray = text.lastIndexOf("]");
const start = startArray >= 0 && (startObject < 0 || startArray < startObject) ? startArray : startObject;
const end = start === startArray ? endArray : endObject;
if (start >= 0 && end > start) { try { return JSON.parse(text.slice(start, end + 1)); } catch {} }
throw new Error("Could not parse structured JSON from LLM response.");
}
Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.