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>
|
<button id="btn-test-assistant-chat-model" class="btn-sm btn-primary" type="button"><i class="fas fa-vial"></i> Test</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="assistant-chat-test-result" style="font-size:12px;color:var(--g500);"></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 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>
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--g700);">Starter prompt pool</div>
|
<div style="font-size:13px;font-weight:600;color:var(--g700);">Starter prompt pool</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,52 @@
|
||||||
export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
let configState = 'idle';
|
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;
|
let imageModelsLoading = false;
|
||||||
|
|
||||||
document.addEventListener('tabChanged', function(e) {
|
document.addEventListener('tabChanged', function(e) {
|
||||||
|
|
@ -80,6 +127,11 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
chatSelect.value = savedChatModel;
|
chatSelect.value = savedChatModel;
|
||||||
}
|
}
|
||||||
window._assistantImageModelValue = cfg['clinical_assistant.image_model'] || '';
|
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);
|
renderAssistantImageModels([], window._assistantImageModelValue);
|
||||||
setValue('assistant-search-limit', cfg['clinical_assistant.search_limit'] || '8');
|
setValue('assistant-search-limit', cfg['clinical_assistant.search_limit'] || '8');
|
||||||
setValue('assistant-context-chars', cfg['clinical_assistant.context_chars'] || '1400');
|
setValue('assistant-context-chars', cfg['clinical_assistant.context_chars'] || '1400');
|
||||||
|
|
@ -135,6 +187,7 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
if (!data || data.success === false || !Array.isArray(data.models) ||
|
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');
|
!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);
|
renderAssistantImageModels(data.models, sel.value);
|
||||||
})
|
})
|
||||||
.catch(function() {
|
.catch(function() {
|
||||||
|
|
@ -304,7 +357,9 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
putAssistantConfig('clinical_assistant.chat_model', getValue('assistant-chat-model')),
|
putAssistantConfig('clinical_assistant.chat_model', getValue('assistant-chat-model')),
|
||||||
putAssistantConfig('clinical_assistant.search_limit', getValue('assistant-search-limit') || '8'),
|
putAssistantConfig('clinical_assistant.search_limit', getValue('assistant-search-limit') || '8'),
|
||||||
putAssistantConfig('clinical_assistant.context_chars', getValue('assistant-context-chars') || '1400'),
|
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() {
|
]).then(function() {
|
||||||
if (status) status.textContent = '';
|
if (status) status.textContent = '';
|
||||||
showToast('Assistant settings saved', 'success');
|
showToast('Assistant settings saved', 'success');
|
||||||
|
|
@ -322,6 +377,12 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
return data;
|
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 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; }
|
function setValue(id, value) { var el = document.getElementById(id); if (el) el.value = value; }
|
||||||
const escAssistant = adminEscapeHtml;
|
const escAssistant = adminEscapeHtml;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue