feat: admin allowlists for chat and image models (users see the model pill once configured)
This commit is contained in:
parent
73955dd5f9
commit
b21080f2a6
2 changed files with 76 additions and 1 deletions
|
|
@ -292,6 +292,20 @@
|
|||
<button id="btn-test-assistant-chat-model" class="btn-sm btn-primary" type="button"><i class="fas fa-vial"></i> Test</button>
|
||||
</div>
|
||||
<div id="assistant-chat-test-result" style="font-size:12px;color:var(--g500);"></div>
|
||||
<div class="admin-row" style="align-items:flex-start;">
|
||||
<strong class="admin-row-label">Model availability</strong>
|
||||
<div style="flex:1;display:flex;flex-direction:column;gap:8px;min-width:0;">
|
||||
<p style="margin:0;font-size:12px;color:var(--g500);">Models users may select in the Clinical Assistant. Leave a list empty to keep only the configured model.</p>
|
||||
<div>
|
||||
<div style="font-size:12px;font-weight:600;color:var(--g600);margin-bottom:2px;">Chat models</div>
|
||||
<div id="assistant-allowed-chat-models" aria-label="Chat models available to the Clinical Assistant"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:12px;font-weight:600;color:var(--g600);margin-bottom:2px;">Image models</div>
|
||||
<div id="assistant-allowed-image-models" aria-label="Image models available to the Clinical Assistant"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="border:1px solid var(--g100);border-radius:8px;padding:10px;display:flex;gap:10px;align-items:center;justify-content:space-between;flex-wrap:wrap;">
|
||||
<div>
|
||||
<div style="font-size:13px;font-weight:600;color:var(--g700);">Starter prompt pool</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,52 @@
|
|||
export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||
let configState = 'idle';
|
||||
const IMAGE_MODEL_FALLBACKS = ['openai-gpt-image-1', 'openai-gpt-image-1-mini', 'openai-gpt-image-1.5', 'openai-dall-e-3'];
|
||||
let savedChatAllowed = [];
|
||||
let savedImageAllowed = [];
|
||||
let chatRoster = [];
|
||||
let imageRoster = [];
|
||||
|
||||
function parseAssistantList(value) {
|
||||
return String(value || '').split(',').map(function(s) { return s.trim(); }).filter(Boolean);
|
||||
}
|
||||
function renderAssistantCheckboxList(containerId, candidates, saved) {
|
||||
var container = document.getElementById(containerId);
|
||||
if (!container) return;
|
||||
var ids = candidates.slice();
|
||||
saved.forEach(function(id) { if (ids.indexOf(id) === -1) ids.push(id); });
|
||||
ids = ids.filter(Boolean).filter(function(id, i, all) { return all.indexOf(id) === i; }).sort();
|
||||
container.innerHTML = '';
|
||||
if (!ids.length) {
|
||||
var empty = document.createElement('p');
|
||||
empty.style.cssText = 'margin:0;font-size:12px;color:var(--g500);';
|
||||
empty.textContent = 'No models available. Add or enable models first.';
|
||||
container.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
ids.forEach(function(id) {
|
||||
var row = document.createElement('label');
|
||||
row.style.cssText = 'display:flex;align-items:center;gap:6px;font-size:12px;padding:2px 0;';
|
||||
var box = document.createElement('input');
|
||||
box.type = 'checkbox';
|
||||
box.value = id;
|
||||
box.checked = saved.indexOf(id) !== -1;
|
||||
var span = document.createElement('span');
|
||||
span.textContent = id;
|
||||
row.appendChild(box);
|
||||
row.appendChild(span);
|
||||
container.appendChild(row);
|
||||
});
|
||||
}
|
||||
function checkedAssistantModels(containerId) {
|
||||
var container = document.getElementById(containerId);
|
||||
if (!container) return [];
|
||||
return Array.prototype.map.call(container.querySelectorAll('input[type="checkbox"]:checked'), function(box) { return box.value; });
|
||||
}
|
||||
function renderAssistantImageModelCheckboxes(models) {
|
||||
imageRoster = (Array.isArray(models) ? models : []).map(function(m) { return m.id; }).concat(IMAGE_MODEL_FALLBACKS, [window._assistantImageModelValue]);
|
||||
renderAssistantCheckboxList('assistant-allowed-image-models', imageRoster, savedImageAllowed);
|
||||
}
|
||||
|
||||
let imageModelsLoading = false;
|
||||
|
||||
document.addEventListener('tabChanged', function(e) {
|
||||
|
|
@ -80,6 +127,11 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
|||
chatSelect.value = savedChatModel;
|
||||
}
|
||||
window._assistantImageModelValue = cfg['clinical_assistant.image_model'] || '';
|
||||
savedChatAllowed = parseAssistantList(cfg['clinical_assistant.allowed_models']);
|
||||
savedImageAllowed = parseAssistantList(cfg['clinical_assistant.allowed_image_models']);
|
||||
chatRoster = chatOptions().map(function(o) { return o.value; }).filter(Boolean).concat([cfg['clinical_assistant.chat_model'] || '']);
|
||||
renderAssistantCheckboxList('assistant-allowed-chat-models', chatRoster, savedChatAllowed);
|
||||
renderAssistantImageModelCheckboxes([]);
|
||||
renderAssistantImageModels([], window._assistantImageModelValue);
|
||||
setValue('assistant-search-limit', cfg['clinical_assistant.search_limit'] || '8');
|
||||
setValue('assistant-context-chars', cfg['clinical_assistant.context_chars'] || '1400');
|
||||
|
|
@ -135,6 +187,7 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
|||
.then(function(data) {
|
||||
if (!data || data.success === false || !Array.isArray(data.models) ||
|
||||
!data.models.every(function(m) { return m && typeof m.id === 'string' && m.id; })) throw new Error('Invalid image models');
|
||||
renderAssistantImageModelCheckboxes(data.models);
|
||||
renderAssistantImageModels(data.models, sel.value);
|
||||
})
|
||||
.catch(function() {
|
||||
|
|
@ -304,7 +357,9 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
|||
putAssistantConfig('clinical_assistant.chat_model', getValue('assistant-chat-model')),
|
||||
putAssistantConfig('clinical_assistant.search_limit', getValue('assistant-search-limit') || '8'),
|
||||
putAssistantConfig('clinical_assistant.context_chars', getValue('assistant-context-chars') || '1400'),
|
||||
putAssistantConfig('clinical_assistant.translate_provider', getValue('assistant-translate-provider') || 'libretranslate')
|
||||
putAssistantConfig('clinical_assistant.translate_provider', getValue('assistant-translate-provider') || 'libretranslate'),
|
||||
putAssistantConfig('clinical_assistant.allowed_models', checkedAssistantModels('assistant-allowed-chat-models').join(',')),
|
||||
putAssistantConfig('clinical_assistant.allowed_image_models', checkedAssistantModels('assistant-allowed-image-models').join(','))
|
||||
]).then(function() {
|
||||
if (status) status.textContent = '';
|
||||
showToast('Assistant settings saved', 'success');
|
||||
|
|
@ -322,6 +377,12 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
|||
return data;
|
||||
});
|
||||
}
|
||||
function chatOptions() {
|
||||
var chat = document.getElementById('assistant-chat-model');
|
||||
var out = [];
|
||||
if (chat) Array.prototype.forEach.call(chat.options, function(o) { out.push({ value: o.value }); });
|
||||
return out;
|
||||
}
|
||||
function getValue(id) { var el = document.getElementById(id); return el ? el.value.trim() : ''; }
|
||||
function setValue(id, value) { var el = document.getElementById(id); if (el) el.value = value; }
|
||||
const escAssistant = adminEscapeHtml;
|
||||
|
|
|
|||
Loading…
Reference in a new issue