soyailabs / static /js /enabled_models.js
GitHub Actions
Auto-deploy from GitHub Actions - 2026-05-22 02:27:52
544f940
/* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
* SOY AI Labs โ€” ํ‘œ์ค€ AI ๋ชจ๋ธ ์…€๋ ‰ํ„ฐ ๋กœ๋”
*
* ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€(`๊ด€๋ฆฌ > AI ์„ค์ • > ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ AI ๋ชฉ๋ก`)์—์„œ ํ™œ์„ฑํ™”ํ•œ ๋ชจ๋ธ๋งŒ
* ํ™”๋ฉด ๋“œ๋กญ๋‹ค์šด์— ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์‚ฌ์šฉ์ž/์ฐฝ์ž‘ ํ™”๋ฉด์€ ์ด ๋ชจ๋“ˆ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
*
* ํ‘œ์ค€ API: GET /api/enabled-models
* ์‘๋‹ต: {
* success: true,
* models: [{ name: "gemini:xxx" | "ollama-xxx", type: "gemini" | "ollama" }],
* default_analysis_model: "...",
* default_answer_model: "..."
* }
*
* ์‚ฌ์šฉ ์˜ˆ:
* SoyEnabledModels.populate(document.getElementById('modelSelect'), {
* kind: 'analysis', // 'analysis' | 'answer' (๊ธฐ๋ณธ๊ฐ’ ์ž๋™์„ ํƒ์šฉ)
* preset: 'gemini:xxx', // ์šฐ์„  ์„ ํƒํ•  ๋ชจ๋ธ (์„ ํƒ)
* placeholder: '๋ชจ๋ธ ์„ ํƒ', // ์ฒซ ์˜ต์…˜์˜ ๋ผ๋ฒจ (์„ ํƒ, ์—†์œผ๋ฉด ์˜ต์…˜ ๋ฏธ์ถ”๊ฐ€)
* includeAll: false, // '์ „์ฒด ๋ชจ๋ธ' ์˜ต์…˜ ์ถ”๊ฐ€ ์—ฌ๋ถ€ (๊ธฐ๋ณธ false)
* strip: true, // gemini: ์ ‘๋‘์–ด ์ œ๊ฑฐํ•˜์—ฌ ํ‘œ์‹œ (๊ธฐ๋ณธ true)
* typeFilter: ['gemini'], // ํŠน์ • ํƒ€์ž…๋งŒ ํ‘œ์‹œ (์„ ํƒ, ์˜ˆ: ['gemini'])
* onChange: function (val) { ... } // ์„ ํƒ ๋ณ€๊ฒฝ ์ฝœ๋ฐฑ (์„ ํƒ)
* });
* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
(function (global) {
'use strict';
var STATE = { cache: null, inflight: null };
function fetchEnabledModels(force) {
if (!force && STATE.cache) return Promise.resolve(STATE.cache);
if (STATE.inflight) return STATE.inflight;
STATE.inflight = fetch('/api/enabled-models', { credentials: 'include' })
.then(function (r) {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.json();
})
.then(function (data) {
STATE.cache = {
models: Array.isArray(data.models) ? data.models : [],
default_analysis_model: data.default_analysis_model || '',
default_answer_model: data.default_answer_model || '',
};
return STATE.cache;
})
.finally(function () { STATE.inflight = null; });
return STATE.inflight;
}
function escapeHtml(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, function (m) {
return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[m];
});
}
function displayName(name, strip) {
if (!name) return '';
return (strip !== false && name.indexOf('gemini:') === 0) ? name.substring(7) : name;
}
function buildHtml(models, opts) {
var parts = [];
if (opts.placeholder) {
parts.push('<option value="">' + escapeHtml(opts.placeholder) + '</option>');
}
if (opts.includeAll) {
parts.push('<option value="all">โœจ ์ „์ฒด ๋ชจ๋ธ (๋ชจ๋“  ๊ณต๊ฐœ ์›น์†Œ์„ค)</option>');
}
var gemini = models.filter(function (m) { return m.type === 'gemini'; });
var ollama = models.filter(function (m) { return m.type === 'ollama'; });
var etc = models.filter(function (m) { return m.type !== 'gemini' && m.type !== 'ollama'; });
function group(label, list) {
if (!list.length) return;
parts.push('<optgroup label="' + escapeHtml(label) + '">');
list.forEach(function (m) {
parts.push('<option value="' + escapeHtml(m.name) + '">' +
escapeHtml(displayName(m.name, opts.strip)) + '</option>');
});
parts.push('</optgroup>');
}
group('โœจ Gemini', gemini);
group('๐Ÿค– Ollama', ollama);
group('๊ธฐํƒ€', etc);
return parts.join('');
}
/**
* ์…€๋ ‰ํŠธ ์š”์†Œ๋ฅผ ํ‘œ์ค€ ๋ชจ๋ธ ๋ชฉ๋ก์œผ๋กœ ์ฑ„์›๋‹ˆ๋‹ค.
* ๋ฐ˜ํ™˜: Promise โ€” ์„ฑ๊ณต ์‹œ ์„ ํƒ๋œ ๋ชจ๋ธ๋ช…(string|null)์„ resolve
*/
function populate(selectEl, opts) {
opts = opts || {};
if (!selectEl) return Promise.resolve(null);
var prevPlaceholder = selectEl.innerHTML;
selectEl.disabled = true;
if (!selectEl.dataset.soyOriginalPlaceholder) {
selectEl.innerHTML = '<option value="">' + escapeHtml(opts.loadingText || '๋ชจ๋ธ ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ') + '</option>';
}
return fetchEnabledModels(opts.force).then(function (data) {
// ํƒ€์ž… ํ•„ํ„ฐ ์ ์šฉ (์˜ˆ: ์ฝ˜ํ‹ฐ ์–ด์‹œ์ŠคํŠธ๋Š” ['gemini']๋งŒ)
var models = data.models;
if (Array.isArray(opts.typeFilter) && opts.typeFilter.length) {
models = models.filter(function (m) { return opts.typeFilter.indexOf(m.type) !== -1; });
}
var html = '';
if (!models.length) {
html = '<option value="">ํ™œ์„ฑํ™”๋œ AI ๋ชจ๋ธ์ด ์—†์Šต๋‹ˆ๋‹ค (๊ด€๋ฆฌ > AI ์„ค์ •)</option>';
selectEl.innerHTML = html;
selectEl.disabled = false;
return null;
}
html = buildHtml(models, opts);
selectEl.innerHTML = html;
// ๊ธฐ๋ณธ ์„ ํƒ๊ฐ’ ๊ฒฐ์ •: preset โ†’ kind ๊ธฐ๋ฐ˜ default โ†’ ์ฒซ ์˜ต์…˜
var fallbackKey = (opts.kind === 'analysis') ? 'default_analysis_model' : 'default_answer_model';
var candidates = [opts.preset, data[fallbackKey]].filter(Boolean);
var chosen = null;
for (var i = 0; i < candidates.length; i++) {
if (models.some(function (m) { return m.name === candidates[i]; })) {
chosen = candidates[i];
break;
}
}
if (!chosen && opts.includeAll) chosen = 'all';
if (chosen) selectEl.value = chosen;
else if (!opts.placeholder && selectEl.options.length > 0) {
// ํ”Œ๋ ˆ์ด์Šคํ™€๋”๊ฐ€ ์—†์œผ๋ฉด ์ฒซ ์‹ค์ œ ์˜ต์…˜ ์„ ํƒ
for (var j = 0; j < selectEl.options.length; j++) {
if (selectEl.options[j].value) { selectEl.value = selectEl.options[j].value; break; }
}
chosen = selectEl.value || null;
}
selectEl.disabled = false;
if (typeof opts.onChange === 'function' && chosen) {
try { opts.onChange(chosen); } catch (e) { /* ignore */ }
}
return chosen;
}).catch(function (err) {
console.warn('[SoyEnabledModels] ๋กœ๋“œ ์‹คํŒจ:', err);
selectEl.innerHTML = prevPlaceholder.indexOf('option') === -1
? '<option value="">๋ชจ๋ธ ๋ชฉ๋ก ๋กœ๋“œ ์‹คํŒจ</option>'
: prevPlaceholder;
selectEl.disabled = false;
return null;
});
}
/** ์—ฌ๋Ÿฌ ์…€๋ ‰ํŠธ๋ฅผ ํ•œ ๋ฒˆ์˜ API ํ˜ธ์ถœ๋กœ ๋™์‹œ์— ์ฑ„์›๋‹ˆ๋‹ค. */
function populateAll(targets) {
return fetchEnabledModels().then(function () {
return Promise.all((targets || []).map(function (t) {
return populate(t.element, t.options || {});
}));
});
}
function refresh() {
STATE.cache = null;
return fetchEnabledModels(true);
}
global.SoyEnabledModels = {
populate: populate,
populateAll: populateAll,
fetch: fetchEnabledModels,
refresh: refresh,
};
})(window);