fix: delegated model-selection persistence, admin-settable conversation budget (DB overrides env), unbounded image poll
This commit is contained in:
parent
ddc835b952
commit
5e6de10d0a
4 changed files with 39 additions and 26 deletions
|
|
@ -336,7 +336,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="admin-row">
|
<div class="admin-row">
|
||||||
<strong class="admin-row-label">Conversation input budget</strong>
|
<strong class="admin-row-label">Conversation input budget</strong>
|
||||||
<p id="assistant-conversation-budget" role="status" style="margin:0;font-size:13px;color:var(--g600);">Loading server budget metadata...</p>
|
<div style="flex:1;display:flex;flex-direction:column;gap:4px;min-width:0;">
|
||||||
|
<input id="assistant-conversation-budget" type="number" min="1000" max="1000000" class="admin-control" placeholder="e.g. 120000" aria-label="Conversation input budget (UTF-16 code units)">
|
||||||
|
<p id="assistant-conversation-budget-meta" role="status" style="margin:0;font-size:12px;color:var(--g500);">UTF-16 code units. Leave empty to use CLINICAL_ASSISTANT_CONVERSATION_CHARS.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section aria-labelledby="clinical-prompts-heading">
|
<section aria-labelledby="clinical-prompts-heading">
|
||||||
<h4 id="clinical-prompts-heading">Clinical Assistant prompts</h4>
|
<h4 id="clinical-prompts-heading">Clinical Assistant prompts</h4>
|
||||||
|
|
|
||||||
|
|
@ -136,10 +136,15 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
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');
|
||||||
setValue('assistant-translate-provider', ['libretranslate', 'deepl'].includes(cfg['clinical_assistant.translate_provider']) ? cfg['clinical_assistant.translate_provider'] : 'libretranslate');
|
setValue('assistant-translate-provider', ['libretranslate', 'deepl'].includes(cfg['clinical_assistant.translate_provider']) ? cfg['clinical_assistant.translate_provider'] : 'libretranslate');
|
||||||
var budgetLabel = document.getElementById('assistant-conversation-budget');
|
var budgetInput = document.getElementById('assistant-conversation-budget');
|
||||||
if (budgetLabel) budgetLabel.textContent =
|
if (budgetInput && budget && Number.isInteger(budget.limit)) {
|
||||||
budget.limit.toLocaleString() + ' ' + budget.unit + ' (' + budget.measure + ') — ' + budget.env +
|
var cfgBudget = cfg['clinical_assistant.conversation_chars'];
|
||||||
(budget.source === 'environment' ? ' (environment).' : ' (server default; environment unset).');
|
budgetInput.value = cfgBudget ? String(cfgBudget) : String(budget.limit);
|
||||||
|
}
|
||||||
|
var budgetMeta = document.getElementById('assistant-conversation-budget-meta');
|
||||||
|
if (budgetMeta) budgetMeta.textContent = budget && budget.source === 'environment'
|
||||||
|
? 'Currently from CLINICAL_ASSISTANT_CONVERSATION_CHARS (' + budget.limit.toLocaleString() + '). Set a value here to override it; clear it to use the environment again.'
|
||||||
|
: 'UTF-16 code units. Leave empty to use CLINICAL_ASSISTANT_CONVERSATION_CHARS.';
|
||||||
configState = 'ready';
|
configState = 'ready';
|
||||||
updateAssistantLoadState();
|
updateAssistantLoadState();
|
||||||
loadAssistantImageModels();
|
loadAssistantImageModels();
|
||||||
|
|
@ -358,6 +363,7 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
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.conversation_chars', getValue('assistant-conversation-budget')),
|
||||||
putAssistantConfig('clinical_assistant.allowed_models', checkedAssistantModels('assistant-allowed-chat-models').join(',')),
|
putAssistantConfig('clinical_assistant.allowed_models', checkedAssistantModels('assistant-allowed-chat-models').join(',')),
|
||||||
putAssistantConfig('clinical_assistant.allowed_image_models', checkedAssistantModels('assistant-allowed-image-models').join(','))
|
putAssistantConfig('clinical_assistant.allowed_image_models', checkedAssistantModels('assistant-allowed-image-models').join(','))
|
||||||
]).then(function() {
|
]).then(function() {
|
||||||
|
|
|
||||||
|
|
@ -192,12 +192,12 @@ import {
|
||||||
imageModel: data.imageModel || ''
|
imageModel: data.imageModel || ''
|
||||||
};
|
};
|
||||||
bindModelSelects();
|
bindModelSelects();
|
||||||
fillModelSelect(document.getElementById('assistant-chat-model-select'), statusChoices.allowedChatModels, statusChoices.chatModel, selectedChatModel, 'ped_assistant_chat_model');
|
fillModelSelect(document.getElementById('assistant-chat-model-select'), statusChoices.allowedChatModels, statusChoices.chatModel, selectedChatModel, 'ped_assistant_chat_model', 'chat');
|
||||||
var chatSel = document.getElementById('assistant-chat-model-select');
|
var chatSel = document.getElementById('assistant-chat-model-select');
|
||||||
selectedChatModel = chatSel ? chatSel.value : selectedChatModel;
|
selectedChatModel = chatSel ? chatSel.value : selectedChatModel;
|
||||||
var imageSel = document.getElementById('assistant-image-model-select');
|
var imageSel = document.getElementById('assistant-image-model-select');
|
||||||
if (imageSel) {
|
if (imageSel) {
|
||||||
fillModelSelect(imageSel, statusChoices.allowedImageModels, statusChoices.imageModel, selectedImageModel, 'ped_assistant_image_model');
|
fillModelSelect(imageSel, statusChoices.allowedImageModels, statusChoices.imageModel, selectedImageModel, 'ped_assistant_image_model', 'image');
|
||||||
selectedImageModel = imageSel.value || selectedImageModel;
|
selectedImageModel = imageSel.value || selectedImageModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1165,7 +1165,7 @@ import {
|
||||||
var select = modal.querySelector('#create-image-chat');
|
var select = modal.querySelector('#create-image-chat');
|
||||||
if (select) select.selectedIndex = 0;
|
if (select) select.selectedIndex = 0;
|
||||||
bindModelSelects();
|
bindModelSelects();
|
||||||
fillModelSelect(document.getElementById('assistant-image-model-select'), statusChoices.allowedImageModels, statusChoices.imageModel, selectedImageModel, 'ped_assistant_image_model');
|
fillModelSelect(document.getElementById('assistant-image-model-select'), statusChoices.allowedImageModels, statusChoices.imageModel, selectedImageModel, 'ped_assistant_image_model', 'image');
|
||||||
renderCreateImageHistory();
|
renderCreateImageHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1296,8 +1296,9 @@ import {
|
||||||
else localStorage.removeItem(key);
|
else localStorage.removeItem(key);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
function fillModelSelect(select, allowed, configured, saved, saveKind) {
|
function fillModelSelect(select, allowed, configured, saved, saveKind, kind) {
|
||||||
if (!select) return;
|
if (!select) return;
|
||||||
|
if (kind) select.setAttribute('data-model-select-kind', kind);
|
||||||
select.innerHTML = '';
|
select.innerHTML = '';
|
||||||
var def = document.createElement('option');
|
var def = document.createElement('option');
|
||||||
def.value = '';
|
def.value = '';
|
||||||
|
|
@ -1320,23 +1321,19 @@ import {
|
||||||
if (pill) pill.hidden = !show;
|
if (pill) pill.hidden = !show;
|
||||||
}
|
}
|
||||||
function bindModelSelects() {
|
function bindModelSelects() {
|
||||||
var chatSel = document.getElementById('assistant-chat-model-select');
|
// Delegated persistence: any chat/image model select saves immediately,
|
||||||
if (chatSel && !chatSel.dataset.bound) {
|
// even when the popup recreates its element.
|
||||||
chatSel.dataset.bound = '1';
|
if (document.dataset.modelSelectsBound) return;
|
||||||
chatSel.addEventListener('change', function() {
|
document.dataset.modelSelectsBound = '1';
|
||||||
selectedChatModel = chatSel.value || '';
|
document.addEventListener('change', function(e) {
|
||||||
saveModelSelection('ped_assistant_chat_model', selectedChatModel);
|
var sel = e.target && e.target.closest ? e.target.closest('[data-model-select-kind]') : null;
|
||||||
|
if (!sel) return;
|
||||||
|
var kind = sel.getAttribute('data-model-select-kind');
|
||||||
|
var value = sel.value || '';
|
||||||
|
if (kind === 'chat') { selectedChatModel = value; saveModelSelection('ped_assistant_chat_model', value); }
|
||||||
|
if (kind === 'image') { selectedImageModel = value; saveModelSelection('ped_assistant_image_model', value); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var imageSel = document.getElementById('assistant-image-model-select');
|
|
||||||
if (imageSel && !imageSel.dataset.bound) {
|
|
||||||
imageSel.dataset.bound = '1';
|
|
||||||
imageSel.addEventListener('change', function() {
|
|
||||||
selectedImageModel = imageSel.value || '';
|
|
||||||
saveModelSelection('ped_assistant_image_model', selectedImageModel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Saved-chat options: Rename, Pin, Export, Delete (ChatGPT-style menu) ──
|
// ── Saved-chat options: Rename, Pin, Export, Delete (ChatGPT-style menu) ──
|
||||||
function pinnedChatIds() {
|
function pinnedChatIds() {
|
||||||
|
|
|
||||||
|
|
@ -755,7 +755,14 @@ function isUsefulIndexedTopicExample(item) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConversationLimit() {
|
async function getConversationLimit() {
|
||||||
|
// Admin-set value wins over the environment so the administrator can test
|
||||||
|
// the warning/refusal behavior with a lower limit.
|
||||||
|
var override = await getSetting('clinical_assistant.conversation_chars', '');
|
||||||
|
if (override !== '') {
|
||||||
|
var parsed = parseInt(override, 10);
|
||||||
|
if (Number.isInteger(parsed) && parsed >= 1000 && parsed <= 1000000) return parsed;
|
||||||
|
}
|
||||||
return conversationBudget(process.env).limit;
|
return conversationBudget(process.env).limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue