fix: the assistant settings page says what saves what
Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 50s
Forgejo Docker Build / Root app tests (push) Successful in 1m2s
Forgejo Android APK / Build signed APK (push) Successful in 2m15s
Forgejo Docker Build / Build Docker image (push) Successful in 15s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 50s
Forgejo Docker Build / Root app tests (push) Successful in 1m2s
Forgejo Android APK / Build signed APK (push) Successful in 2m15s
Forgejo Docker Build / Build Docker image (push) Successful in 15s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
The card holds more than one Save button and nothing said so. "Save image settings" is injected directly above "Save model & retrieval settings", with no indication of where one stops and the other starts, and the page saves nothing automatically. It now states that up front, and the bottom button says which settings it applies. "Retry loading settings" sat beside Save looking like an ordinary control, because it did: a bare button with the hidden attribute, which the browser's own [hidden] rule could not hide once .btn-sm set a display. It is now inside an error message that exists only on failure, says what failed, and says that nothing typed has been lost. The status line used to read "Settings ready." forever, which answers a question nobody asks. It now reports the thing an admin actually wants to know when they come back: whether the last save went through, and at what time. A toast is gone in three seconds; this stays on the page. The signed-out preview moves to Feature Flags, where it belongs. It was a second checkbox under a row labelled "Sources", followed by two paragraphs, the first about preview and the second about citations — so neither paragraph clearly belonged to either checkbox. It is stored as feature.assistant_preview now, with the old clinical_assistant.preview_enabled still honoured when the new key has never been written. That also means an ordinary admin can toggle it under ADMIN_LOCKDOWN: clinical_assistant.* is locked, and putting a day-to-day switch behind host access was never the intent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
This commit is contained in:
parent
85dbbc1a4a
commit
96a6a353fc
7 changed files with 100 additions and 35 deletions
|
|
@ -119,8 +119,17 @@
|
||||||
<option value="false">Disabled</option>
|
<option value="false">Disabled</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:12px;">
|
||||||
|
<label style="font-size:13px;font-weight:600;min-width:160px;">Signed-out preview:</label>
|
||||||
|
<select id="cms-flag-assistant-preview" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
|
||||||
|
<option value="true">Enabled</option>
|
||||||
|
<option value="false">Disabled</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p style="margin:0;font-size:12px;color:var(--g500);">Lets a visitor who is not signed in try the Clinical Assistant. They can ask questions and read the answer; everything else asks them to sign in. Nothing is saved to any account, because a preview visitor has no account.</p>
|
||||||
<div>
|
<div>
|
||||||
<button id="btn-save-flags" class="btn-sm btn-primary">Save Flags</button>
|
<button id="btn-save-flags" class="btn-sm btn-primary"><i class="fas fa-floppy-disk"></i> Save feature flags</button>
|
||||||
|
<span id="cms-flags-status" role="status" style="font-size:12px;color:var(--g500);margin-left:8px;"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -316,9 +325,15 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3><i class="fas fa-brain"></i> Clinical Assistant / Learning</h3>
|
<h3><i class="fas fa-brain"></i> Clinical Assistant / Learning</h3>
|
||||||
<span style="font-size:12px;color:var(--g500);">Model and prompt settings</span>
|
<span style="font-size:12px;color:var(--g500);">Model, retrieval and prompt settings</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
|
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
|
||||||
|
<p style="margin:0;padding:8px 10px;background:var(--g100);border-radius:6px;font-size:12px;color:var(--g600);">
|
||||||
|
Nothing on this page saves by itself. Each <strong>Save</strong> button applies only the
|
||||||
|
settings above it, up to the previous Save. This card has two: image settings have their
|
||||||
|
own, and everything else is saved by <strong>Save model & retrieval settings</strong>
|
||||||
|
at the bottom.
|
||||||
|
</p>
|
||||||
<div class="admin-row">
|
<div class="admin-row">
|
||||||
<label for="assistant-chat-model" class="admin-row-label">Chat model</label>
|
<label for="assistant-chat-model" class="admin-row-label">Chat model</label>
|
||||||
<select id="assistant-chat-model" class="admin-control"></select>
|
<select id="assistant-chat-model" class="admin-control"></select>
|
||||||
|
|
@ -362,18 +377,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="admin-row">
|
<div class="admin-row">
|
||||||
<strong class="admin-row-label">Sources</strong>
|
<strong class="admin-row-label">Citations</strong>
|
||||||
<div style="flex:1;display:flex;flex-direction:column;gap:4px;min-width:0;">
|
<div style="flex:1;display:flex;flex-direction:column;gap:4px;min-width:0;">
|
||||||
<label style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
<label style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
<input type="checkbox" id="assistant-show-sources" checked>
|
<input type="checkbox" id="assistant-show-sources" checked>
|
||||||
Show the Sources panel and numbered citations
|
Show the Sources panel and numbered citations
|
||||||
</label>
|
</label>
|
||||||
<label style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
<p style="margin:0;font-size:12px;color:var(--g500);">Display only. The AI prompt, retrieval and the stored answer are byte-for-byte identical either way, so turning this off cannot change what the model says. Citations are hidden, not removed — turning it back on restores them.</p>
|
||||||
<input type="checkbox" id="assistant-preview-enabled">
|
|
||||||
Let signed-out visitors preview the assistant
|
|
||||||
</label>
|
|
||||||
<p style="margin:0;font-size:12px;color:var(--g500);">Preview is read-only: visitors can try the assistant, and anything else asks them to sign in or create an account.</p>
|
|
||||||
<p style="margin:0;font-size:12px;color:var(--g500);">Display only. The AI prompt, retrieval and the stored answer are byte-for-byte identical either way, so turning this off cannot change what the model says. Citations are hidden, not removed — turning it back on restores them.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="admin-row">
|
<div class="admin-row">
|
||||||
|
|
@ -398,10 +408,18 @@
|
||||||
<div id="cms-learning-prompts">Loading Learning prompts...</div>
|
<div id="cms-learning-prompts">Loading Learning prompts...</div>
|
||||||
</section>
|
</section>
|
||||||
<section id="workflow-image-settings" aria-label="Workflow image settings"></section>
|
<section id="workflow-image-settings" aria-label="Workflow image settings"></section>
|
||||||
<div>
|
<div id="assistant-config-error" role="alert" hidden
|
||||||
<button id="btn-save-assistant-config" class="btn-sm btn-primary" disabled><i class="fas fa-floppy-disk"></i> Save Model & Retrieval Settings</button>
|
style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 12px;border:1px solid var(--red);border-radius:8px;background:rgba(220,38,38,0.06);">
|
||||||
<button id="btn-retry-assistant-config" class="btn-sm btn-ghost" type="button" hidden>Retry loading settings</button>
|
<span style="font-size:13px;color:var(--g700);">
|
||||||
<span id="assistant-admin-status" role="status" style="font-size:12px;color:var(--g500);margin-left:8px;"></span>
|
<i class="fas fa-triangle-exclamation" style="color:var(--red);"></i>
|
||||||
|
These settings could not be loaded, so saving is disabled. Nothing you have typed has been lost.
|
||||||
|
</span>
|
||||||
|
<button id="btn-retry-assistant-config" class="btn-sm btn-ghost" type="button">Try again</button>
|
||||||
|
</div>
|
||||||
|
<div style="border-top:1px solid var(--g100);padding-top:12px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
|
||||||
|
<button id="btn-save-assistant-config" class="btn-sm btn-primary" disabled><i class="fas fa-floppy-disk"></i> Save model & retrieval settings</button>
|
||||||
|
<span style="font-size:12px;color:var(--g500);">Applies everything in this card except the image settings above.</span>
|
||||||
|
<span id="assistant-admin-status" role="status" style="font-size:12px;color:var(--g500);"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -378,6 +378,12 @@ function adminTabActive() {
|
||||||
var flagNextcloud = document.getElementById('cms-flag-nextcloud');
|
var flagNextcloud = document.getElementById('cms-flag-nextcloud');
|
||||||
if (flagReadAloud) flagReadAloud.value = cfg['feature.read_aloud'] !== undefined ? cfg['feature.read_aloud'] : 'true';
|
if (flagReadAloud) flagReadAloud.value = cfg['feature.read_aloud'] !== undefined ? cfg['feature.read_aloud'] : 'true';
|
||||||
if (flagNextcloud) flagNextcloud.value = cfg['feature.nextcloud'] !== undefined ? cfg['feature.nextcloud'] : 'true';
|
if (flagNextcloud) flagNextcloud.value = cfg['feature.nextcloud'] !== undefined ? cfg['feature.nextcloud'] : 'true';
|
||||||
|
var flagPreview = document.getElementById('cms-flag-assistant-preview');
|
||||||
|
// Falls back to the key this setting used to live under, so the switch
|
||||||
|
// shows the real state on an installation that set it before the move.
|
||||||
|
if (flagPreview) flagPreview.value =
|
||||||
|
cfg['feature.assistant_preview'] !== undefined ? cfg['feature.assistant_preview'] :
|
||||||
|
(cfg['clinical_assistant.preview_enabled'] !== undefined ? cfg['clinical_assistant.preview_enabled'] : 'false');
|
||||||
|
|
||||||
// Auto-delete setting
|
// Auto-delete setting
|
||||||
var autoDeleteDays = cfg['site.auto_delete_days'] || '7';
|
var autoDeleteDays = cfg['site.auto_delete_days'] || '7';
|
||||||
|
|
@ -419,12 +425,24 @@ function adminTabActive() {
|
||||||
var readAloud = document.getElementById('cms-flag-read-aloud').value;
|
var readAloud = document.getElementById('cms-flag-read-aloud').value;
|
||||||
var nextcloud = document.getElementById('cms-flag-nextcloud').value;
|
var nextcloud = document.getElementById('cms-flag-nextcloud').value;
|
||||||
|
|
||||||
|
var preview = (document.getElementById('cms-flag-assistant-preview') || {}).value;
|
||||||
|
var status = document.getElementById('cms-flags-status');
|
||||||
|
if (status) status.textContent = 'Saving...';
|
||||||
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
putConfig('feature.read_aloud', readAloud),
|
putConfig('feature.read_aloud', readAloud),
|
||||||
putConfig('feature.nextcloud', nextcloud)
|
putConfig('feature.nextcloud', nextcloud),
|
||||||
|
// A feature flag, stored under feature.* like the others. The assistant
|
||||||
|
// still reads the old clinical_assistant.preview_enabled key when this one
|
||||||
|
// has never been set, so existing installations keep their setting.
|
||||||
|
putConfig('feature.assistant_preview', preview === 'true' ? 'true' : 'false')
|
||||||
]).then(function() {
|
]).then(function() {
|
||||||
|
if (status) status.textContent = 'Saved.';
|
||||||
showToast('Feature flags saved', 'success');
|
showToast('Feature flags saved', 'success');
|
||||||
}).catch(function() { showToast('Save failed', 'error'); });
|
}).catch(function() {
|
||||||
|
if (status) status.textContent = 'Not saved.';
|
||||||
|
showToast('Save failed', 'error');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- EMAIL TEMPLATES ----
|
// ---- EMAIL TEMPLATES ----
|
||||||
|
|
|
||||||
|
|
@ -91,13 +91,18 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
function updateAssistantLoadState() {
|
function updateAssistantLoadState() {
|
||||||
var save = document.getElementById('btn-save-assistant-config');
|
var save = document.getElementById('btn-save-assistant-config');
|
||||||
if (save) save.disabled = configState !== 'ready';
|
if (save) save.disabled = configState !== 'ready';
|
||||||
var retry = document.getElementById('btn-retry-assistant-config');
|
// The retry lives inside an error message rather than sitting beside Save
|
||||||
if (retry) retry.hidden = configState !== 'failed';
|
// looking like an ordinary control, which is how it read before.
|
||||||
|
var errorBox = document.getElementById('assistant-config-error');
|
||||||
|
if (errorBox) errorBox.hidden = configState !== 'failed';
|
||||||
var status = document.getElementById('assistant-admin-status');
|
var status = document.getElementById('assistant-admin-status');
|
||||||
if (status) status.textContent = configState === 'failed' ?
|
// The failure case is spelled out in the error box above, so repeating it
|
||||||
'Settings load failed. Drafts are unchanged. Retry loading settings or revisit the Admin tab.' :
|
// here would only be noise.
|
||||||
configState !== 'ready' ? 'Loading assistant settings...' :
|
if (status && configState !== 'ready') {
|
||||||
'Settings ready.';
|
status.textContent = configState === 'failed' ? '' : 'Loading settings...';
|
||||||
|
} else if (status && !/^Saved |^Not saved/.test(status.textContent)) {
|
||||||
|
status.textContent = 'Settings loaded. Unsaved changes are kept until you press Save.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAssistantAdmin() {
|
function loadAssistantAdmin() {
|
||||||
|
|
@ -170,8 +175,6 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
if (saved === undefined || saved === '') saved = cfg['clinical_assistant.citations_enabled']; // legacy key
|
if (saved === undefined || saved === '') saved = cfg['clinical_assistant.citations_enabled']; // legacy key
|
||||||
sourcesBox.checked = String(saved) !== 'false';
|
sourcesBox.checked = String(saved) !== 'false';
|
||||||
}
|
}
|
||||||
var previewBox = document.getElementById('assistant-preview-enabled');
|
|
||||||
if (previewBox) previewBox.checked = String(cfg['clinical_assistant.preview_enabled']) === 'true';
|
|
||||||
var budgetInput = document.getElementById('assistant-conversation-budget');
|
var budgetInput = document.getElementById('assistant-conversation-budget');
|
||||||
var cfgBudget = cfg['clinical_assistant.conversation_chars'];
|
var cfgBudget = cfg['clinical_assistant.conversation_chars'];
|
||||||
if (budgetInput) {
|
if (budgetInput) {
|
||||||
|
|
@ -322,16 +325,17 @@ export function initClinicalAssistantAdmin(adminEscapeHtml) {
|
||||||
putAssistantConfig('clinical_assistant.translate_provider', getValue('assistant-translate-provider') || 'libretranslate'),
|
putAssistantConfig('clinical_assistant.translate_provider', getValue('assistant-translate-provider') || 'libretranslate'),
|
||||||
putAssistantConfig('clinical_assistant.show_sources',
|
putAssistantConfig('clinical_assistant.show_sources',
|
||||||
(document.getElementById('assistant-show-sources') || {}).checked === false ? 'false' : 'true'),
|
(document.getElementById('assistant-show-sources') || {}).checked === false ? 'false' : 'true'),
|
||||||
putAssistantConfig('clinical_assistant.preview_enabled',
|
|
||||||
(document.getElementById('assistant-preview-enabled') || {}).checked === true ? 'true' : 'false'),
|
|
||||||
|
|
||||||
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() {
|
||||||
if (status) status.textContent = '';
|
// A toast is gone in three seconds. Whether these settings are saved is
|
||||||
|
// exactly the question an admin has when they come back to this page, so
|
||||||
|
// the answer stays on the page.
|
||||||
|
if (status) status.textContent = 'Saved ' + new Date().toLocaleTimeString() + '.';
|
||||||
showToast('Assistant settings saved', 'success');
|
showToast('Assistant settings saved', 'success');
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
if (status) status.textContent = '';
|
if (status) status.textContent = 'Not saved. Nothing was changed.';
|
||||||
showToast(err.message || 'Save failed', 'error');
|
showToast(err.message || 'Save failed', 'error');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,17 @@ var PREVIEW_PATHS = new Set([
|
||||||
]);
|
]);
|
||||||
var PREVIEW_USER = Object.freeze({ id: null, preview: true, role: 'preview' });
|
var PREVIEW_USER = Object.freeze({ id: null, preview: true, role: 'preview' });
|
||||||
|
|
||||||
|
// It is a feature flag and now lives with the others under feature.*, which also
|
||||||
|
// means an ordinary admin can still toggle it under ADMIN_LOCKDOWN — the
|
||||||
|
// clinical_assistant.* prefix is locked, and burying a day-to-day switch behind
|
||||||
|
// host access was not the intent.
|
||||||
|
//
|
||||||
|
// The old key is still honoured when the new one has never been written, so an
|
||||||
|
// installation that enabled preview before the move keeps it enabled.
|
||||||
async function previewEnabled() {
|
async function previewEnabled() {
|
||||||
return String(await getSetting('clinical_assistant.preview_enabled', 'false')) === 'true';
|
var flag = await getSetting('feature.assistant_preview', '');
|
||||||
|
if (String(flag) === '') flag = await getSetting('clinical_assistant.preview_enabled', 'false');
|
||||||
|
return String(flag) === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
router.use(async function(req, res, next) {
|
router.use(async function(req, res, next) {
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,13 @@ test('native admin initializer preserves lazy navigation, assistant actions and
|
||||||
const writes = () => calls.filter(c => c.options.method === 'PUT');
|
const writes = () => calls.filter(c => c.options.method === 'PUT');
|
||||||
const save = document.getElementById('btn-save-assistant-config');
|
const save = document.getElementById('btn-save-assistant-config');
|
||||||
save.click(); await tick();
|
save.click(); await tick();
|
||||||
assert.equal(writes().length, 9);
|
// Eight, not nine: the signed-out preview is a feature flag and is saved by
|
||||||
|
// the Feature Flags card now, not by this button.
|
||||||
|
assert.equal(writes().length, 8);
|
||||||
|
assert.equal(writes().filter(c => /preview/.test(c.url)).length, 0,
|
||||||
|
'this button no longer writes the preview flag');
|
||||||
assert.deepEqual(writes().map(c => c.url.split('/').pop()).sort(), [
|
assert.deepEqual(writes().map(c => c.url.split('/').pop()).sort(), [
|
||||||
'clinical_assistant.allowed_image_models', 'clinical_assistant.allowed_models', 'clinical_assistant.chat_model', 'clinical_assistant.context_chars', 'clinical_assistant.conversation_chars', 'clinical_assistant.preview_enabled', 'clinical_assistant.search_limit', 'clinical_assistant.show_sources', 'clinical_assistant.translate_provider'
|
'clinical_assistant.allowed_image_models', 'clinical_assistant.allowed_models', 'clinical_assistant.chat_model', 'clinical_assistant.context_chars', 'clinical_assistant.conversation_chars', 'clinical_assistant.search_limit', 'clinical_assistant.show_sources', 'clinical_assistant.translate_provider'
|
||||||
]);
|
]);
|
||||||
assert.ok(toasts.some(([message, kind]) => message === 'Assistant settings saved' && kind === 'success'));
|
assert.ok(toasts.some(([message, kind]) => message === 'Assistant settings saved' && kind === 'success'));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,8 @@ test('native admin and assistant modules retain budget, table/source identity an
|
||||||
document.getElementById('btn-save-assistant-config').click();
|
document.getElementById('btn-save-assistant-config').click();
|
||||||
await tick();
|
await tick();
|
||||||
assert.equal(limit, 2000);
|
assert.equal(limit, 2000);
|
||||||
assert.equal(calls.filter(call => call.options.method === 'PUT').length, 9, 'one native admin initializer; prompts are not generic setting saves');
|
// Eight since the signed-out preview moved to the Feature Flags card.
|
||||||
|
assert.equal(calls.filter(call => call.options.method === 'PUT').length, 8, 'one native admin initializer; prompts are not generic setting saves');
|
||||||
assert.equal(calls.some(call => call.url.endsWith('/config/clinical_assistant.conversation_chars')), true, 'the conversation budget is an admin-settable override');
|
assert.equal(calls.some(call => call.url.endsWith('/config/clinical_assistant.conversation_chars')), true, 'the conversation budget is an admin-settable override');
|
||||||
document.dispatchEvent(new window.CustomEvent('tabChanged', { detail: { tab: 'assistant' } }));
|
document.dispatchEvent(new window.CustomEvent('tabChanged', { detail: { tab: 'assistant' } }));
|
||||||
await tick(); await tick(); await tick();
|
await tick(); await tick(); await tick();
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,15 @@ test('assistant config GET503 plus Save makes zero PUTs; failed retry preserves
|
||||||
await forceAssistantSave(ui);
|
await forceAssistantSave(ui);
|
||||||
assert.equal(writes(ui).length, 0, 'failed configuration must never write defaults');
|
assert.equal(writes(ui).length, 0, 'failed configuration must never write defaults');
|
||||||
assert.equal(ui.document.getElementById('btn-save-assistant-config').disabled, true);
|
assert.equal(ui.document.getElementById('btn-save-assistant-config').disabled, true);
|
||||||
assert.match(setting(ui, 'admin-status').textContent, /failed|unavailable/i);
|
// The failure is stated in an error box now rather than in a status line
|
||||||
|
// beside Save, where a bare 'Retry loading settings' button read like an
|
||||||
|
// ordinary control that was always there.
|
||||||
|
const errorBox = ui.document.getElementById('assistant-config-error');
|
||||||
|
assert.equal(errorBox.hidden, false, 'a failed load has to say so');
|
||||||
|
assert.match(errorBox.textContent, /could not be loaded/i);
|
||||||
|
assert.match(errorBox.textContent, /nothing you have typed has been lost/i);
|
||||||
|
assert.ok(ui.document.getElementById('btn-retry-assistant-config'),
|
||||||
|
'and the retry has to be reachable from inside it');
|
||||||
assert.equal(setting(ui, 'chat-model').options.length, 0);
|
assert.equal(setting(ui, 'chat-model').options.length, 0);
|
||||||
assert.equal(ui.document.getElementById('workflow-image-settings').children.length, 0, 'image settings load only after config success');
|
assert.equal(ui.document.getElementById('workflow-image-settings').children.length, 0, 'image settings load only after config success');
|
||||||
assert.equal(ui.calls.some(c => c.url.endsWith('/image-models/discover')), false);
|
assert.equal(ui.calls.some(c => c.url.endsWith('/image-models/discover')), false);
|
||||||
|
|
@ -315,8 +323,11 @@ test('assistant config GET503 plus Save makes zero PUTs; failed retry preserves
|
||||||
assert.equal(setting(ui, 'conversation-budget').value, '', 'no saved override, so the field stays empty');
|
assert.equal(setting(ui, 'conversation-budget').value, '', 'no saved override, so the field stays empty');
|
||||||
assert.equal(setting(ui, 'conversation-budget').placeholder, '240000', 'environment metadata shows as the placeholder');
|
assert.equal(setting(ui, 'conversation-budget').placeholder, '240000', 'environment metadata shows as the placeholder');
|
||||||
assert.equal(setting(ui, 'conversation-budget').readOnly, false);
|
assert.equal(setting(ui, 'conversation-budget').readOnly, false);
|
||||||
assert.match(setting(ui, 'admin-status').textContent, /ready/i);
|
// Idle no longer claims 'ready'. What an admin actually needs to know is
|
||||||
assert.equal(retry.hidden, true);
|
// whether their change was applied, so the line reports that instead.
|
||||||
|
assert.match(setting(ui, 'admin-status').textContent, /Settings loaded|Saved /i);
|
||||||
|
// The error box hides on a successful load; the retry inside it goes with it.
|
||||||
|
assert.equal(ui.document.getElementById('assistant-config-error').hidden, true);
|
||||||
adminVisit(ui); adminVisit(ui); await tick();
|
adminVisit(ui); adminVisit(ui); await tick();
|
||||||
assert.equal(ui.calls.filter(c => c.url === '/api/admin/config').length, 3, 'ready revisits neither reload nor add handlers');
|
assert.equal(ui.calls.filter(c => c.url === '/api/admin/config').length, 3, 'ready revisits neither reload nor add handlers');
|
||||||
setting(ui, 'search-limit').value = '19';
|
setting(ui, 'search-limit').value = '19';
|
||||||
|
|
@ -328,7 +339,7 @@ test('assistant config GET503 plus Save makes zero PUTs; failed retry preserves
|
||||||
['clinical_assistant.conversation_chars', ''],
|
['clinical_assistant.conversation_chars', ''],
|
||||||
['clinical_assistant.search_limit', '19'], ['clinical_assistant.context_chars', '2300'],
|
['clinical_assistant.search_limit', '19'], ['clinical_assistant.context_chars', '2300'],
|
||||||
['clinical_assistant.translate_provider', 'libretranslate'],
|
['clinical_assistant.translate_provider', 'libretranslate'],
|
||||||
['clinical_assistant.show_sources', 'true'], ['clinical_assistant.preview_enabled', 'false'],
|
['clinical_assistant.show_sources', 'true'],
|
||||||
['clinical_assistant.allowed_models', ''], ['clinical_assistant.allowed_image_models', '']
|
['clinical_assistant.allowed_models', ''], ['clinical_assistant.allowed_image_models', '']
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue