Files
scrollsmith/src/ollamaClient.js
T
keligrubb 69d1be9415
Release / generate-dungeon (push) Failing after 40s
Release / upload-to-gitea-release (push) Has been skipped
update dependencies and change default model (#24)
Reviewed-on: #24
2026-07-10 23:38:28 +00:00

142 lines
4.3 KiB
JavaScript

import { cleanText } from "./textUtils.js";
const OLLAMA_API_URL = process.env.OLLAMA_API_URL;
export const OLLAMA_MODEL = "qwen3.6-35b-a3b";
function effectiveModel(explicit) {
if (explicit !== undefined && explicit !== null) return explicit;
return process.env.OLLAMA_MODEL || OLLAMA_MODEL;
}
export async function initializeModel() {
if (process.env.OLLAMA_MODEL) return process.env.OLLAMA_MODEL;
try {
const apiUrl = process.env.OLLAMA_API_URL;
const isOpenWebUI = apiUrl?.includes("/api/chat/completions");
const baseUrl = apiUrl?.replace(/\/api\/.*$/, "");
const url = isOpenWebUI ? `${baseUrl}/api/v1/models` : `${baseUrl}/api/tags`;
const headers = isOpenWebUI && process.env.OLLAMA_API_KEY
? { "Authorization": `Bearer ${process.env.OLLAMA_API_KEY}` }
: {};
const res = await fetch(url, { headers });
if (res.ok) {
const data = await res.json();
const model = isOpenWebUI
? data.data?.[0]?.id || data.data?.[0]?.name
: data.models?.[0]?.name;
if (model) {
process.env.OLLAMA_MODEL = model;
console.log(`Using default model: ${model}`);
return model;
}
}
} catch {
// fall through to warn below
}
console.warn(`Could not fetch default model, using: ${OLLAMA_MODEL}`);
return OLLAMA_MODEL;
}
export { cleanText };
export function inferApiType(url) {
if (!url) return "ollama-generate";
if (url.includes("/api/chat/completions")) return "open-webui";
if (url.includes("/api/chat")) return "ollama-chat";
return "ollama-generate";
}
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function callOllamaBase(prompt, model, retries, stepName, apiType) {
const isUsingOpenWebUI = apiType === "open-webui";
const isUsingOllamaChat = apiType === "ollama-chat";
const resolvedModel = effectiveModel(model);
const attempts = Array.from({ length: retries }, (_, index) => index + 1);
for (const attempt of attempts) {
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 =
isUsingOpenWebUI && process.env.OLLAMA_API_KEY
? {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.OLLAMA_API_KEY}`,
}
: { "Content-Type": "application/json" };
const body = isUsingOpenWebUI || isUsingOllamaChat
? { model: resolvedModel, messages: [{ role: "user", content: prompt }] }
: { model: resolvedModel, prompt, stream: false };
const response = await fetch(OLLAMA_API_URL, {
method: "POST",
headers,
body: JSON.stringify(body),
});
if (!response.ok) {
const errorData = await response.text().catch(() => null);
const errorDetails = errorData ? `: ${errorData}` : "";
throw new Error(
`Ollama request failed: ${response.status} ${response.statusText}${errorDetails}`,
);
}
const data = await response.json();
const rawText = isUsingOpenWebUI
? data.choices?.[0]?.message?.content
: isUsingOllamaChat
? data.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,
retries = 5,
stepName = "unknown",
) {
const apiType = inferApiType(OLLAMA_API_URL);
return callOllamaBase(prompt, model, retries, stepName, apiType);
}
export async function callOllamaExplicit(
prompt,
model,
retries = 5,
stepName = "unknown",
apiType = "ollama-generate",
) {
return callOllamaBase(prompt, model, retries, stepName, apiType);
}