feat(settings): AI model profiles management — native llama.cpp + custom OpenAI-compatible providers, default model selection, connection test

This commit is contained in:
dev
2026-09-17 11:54:08 +03:00
parent bbfde902a5
commit efe2a42d1d
5 changed files with 427 additions and 10 deletions
+38 -3
View File
@@ -70,16 +70,51 @@ function createEntryAutoChecker({ pool, getSetting, logAudit, aiUrl, defaultProm
}
}
async function resolveActiveProfile() {
try {
const active = await getSetting('ai_active_profile', 'native');
if (active && active !== 'native') {
const raw = await getSetting('ai_profiles', '[]');
const list = JSON.parse(raw || '[]');
if (Array.isArray(list)) {
return list.find(p => p && p.id === active && p.base_url && p.model) || null;
}
}
} catch (e) {}
return null;
}
function normalizeOpenAiBase(base) {
let b = String(base || '').trim().replace(/\/+$/, '');
if (!/^https?:\/\//i.test(b)) return null;
if (!/\/v1$/i.test(b)) b += '/v1';
return b;
}
async function callModel(text, systemPrompt) {
const profile = await resolveActiveProfile();
const headers = { 'Content-Type': 'application/json' };
let url;
let model;
if (profile) {
const base = normalizeOpenAiBase(profile.base_url);
if (!base) throw new Error('Некорректный base_url профиля ИИ');
url = `${base}/chat/completions`;
model = profile.model;
if (profile.api_key) headers.Authorization = `Bearer ${profile.api_key}`;
} else {
url = `${AI_URL.replace(/\/+$/, '')}/v1/chat/completions`;
model = MODEL;
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
try {
const res = await fetch(`${AI_URL}/v1/chat/completions`, {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
headers,
signal: controller.signal,
body: JSON.stringify({
model: MODEL,
model,
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: text },