const OLLAMA_API_URL = process.env.OLLAMA_API_URL; const OLLAMA_API_KEY = process.env.OLLAMA_API_KEY; export const OLLAMA_MODEL = process.env.OLLAMA_MODEL || "gemma3:4b"; function cleanText(str) { return str .replace(/^#+\s*/gm, "") .replace(/\*\*(.*?)\*\*/g, "$1") .replace(/[*_`]/g, "") .replace(/\s+/g, " ") .trim(); } function inferApiType(url) { return url?.includes("/api/chat/completions") ? "open-webui" : "direct"; } async function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function callOllamaBase(prompt, model, retries, stepName, apiType) { const isUsingOpenWebUI = apiType === "open-webui"; for (let attempt = 1; attempt <= retries; attempt++) { try { const promptCharCount = prompt.length; const promptWordCount = prompt.split(/\s+/).length; console.log( `\n[${stepName}] Sending prompt (attempt ${attempt}/${retries})`, ); console.log( `Prompt: ${promptCharCount} chars, ~${promptWordCount} words`, ); const headers = { "Content-Type": "application/json" }; if (isUsingOpenWebUI && OLLAMA_API_KEY) { headers["Authorization"] = `Bearer ${OLLAMA_API_KEY}`; } const body = isUsingOpenWebUI ? { model, messages: [{ role: "user", content: prompt }] } : { model, prompt, stream: false }; const response = await fetch(OLLAMA_API_URL, { method: "POST", headers, body: JSON.stringify(body), }); if (!response.ok) throw new Error( `Ollama request failed: ${response.status} ${response.statusText}`, ); const data = await response.json(); const rawText = isUsingOpenWebUI ? data.choices?.[0]?.message?.content : data.response; if (!rawText) throw new Error("No response from Ollama"); const cleaned = cleanText(rawText); console.log( `[${stepName}] Received: ${rawText.length} chars, ~${rawText.split(/\s+/).length} words`, ); return cleaned; } catch (err) { console.warn(`[${stepName}] Attempt ${attempt} failed: ${err.message}`); if (attempt === retries) throw err; const delay = 1000 * Math.pow(2, attempt) + Math.random() * 500; console.log(`Retrying in ${Math.round(delay / 1000)}s...`); await sleep(delay); } } } export async function callOllama( prompt, model = OLLAMA_MODEL, retries = 5, stepName = "unknown", ) { const apiType = inferApiType(OLLAMA_API_URL); return callOllamaBase(prompt, model, retries, stepName, apiType); } export async function callOllamaExplicit( prompt, model = OLLAMA_MODEL, retries = 5, stepName = "unknown", apiType = "direct", ) { return callOllamaBase(prompt, model, retries, stepName, apiType); }