fix(dashboard): preserve unsaved model tier selections across refresh
This commit is contained in:
@@ -13,6 +13,10 @@ let _lastBriefingTestAt = null;
|
||||
let _assistantSaveState = null;
|
||||
let _lastAssistantConfig = null;
|
||||
let _assistantManualOverrides = new Set();
|
||||
let _assistantModelDefaultsDraft = null;
|
||||
|
||||
const MODEL_DEFAULT_TASK_KEYS = ['compaction', 'memory_extraction', 'classification', 'tool_summarisation', 'complex_reasoning'];
|
||||
const MODEL_DEFAULT_TIER_KEYS = ['default', 'fast', 'complex', 'local'];
|
||||
|
||||
function formatUptime(seconds) {
|
||||
const d = Math.floor(seconds / 86400);
|
||||
@@ -666,6 +670,7 @@ async function triggerDailyBriefingTest(jobName, statusEl) {
|
||||
function updateAssistantHealth(configData) {
|
||||
const el = document.getElementById('ops-assistant-health');
|
||||
if (!el) {return;}
|
||||
_assistantModelDefaultsDraft = readAssistantModelDefaultsDraft(el) ?? _assistantModelDefaultsDraft;
|
||||
|
||||
const snapshot = getAssistantStateSnapshot(configData);
|
||||
|
||||
@@ -692,7 +697,7 @@ function updateAssistantHealth(configData) {
|
||||
: 'not configured';
|
||||
const briefingReady = dailyBriefing && Boolean(briefingOutput?.channel && briefingOutput?.peer);
|
||||
const playbookLikeReady = announce || (memoryDaily && memoryProactive);
|
||||
const modelTier = configData?.agents?.primary_tier ?? 'default';
|
||||
const modelTier = _assistantModelDefaultsDraft?.primaryTier ?? configData?.agents?.primary_tier ?? 'default';
|
||||
const delegation = configData?.agents?.delegation ?? {};
|
||||
const backgroundModels = configData?.agents?.background_models ?? {};
|
||||
const tiers = configData?.models ?? {};
|
||||
@@ -787,10 +792,10 @@ function updateAssistantHealth(configData) {
|
||||
<div class="text-sm font-semibold text-zinc-50 mb-3">Model Tier Defaults</div>
|
||||
<div class="text-sm text-zinc-500 mb-3">Tier provider/model definitions</div>
|
||||
<div class="space-y-3 mb-4">
|
||||
${['default', 'fast', 'complex', 'local'].map((tier) => {
|
||||
${MODEL_DEFAULT_TIER_KEYS.map((tier) => {
|
||||
const cfg = tiers?.[tier] ?? {};
|
||||
const provider = cfg.provider ?? tiers?.default?.provider ?? 'openai';
|
||||
const model = cfg.model ?? '';
|
||||
const provider = _assistantModelDefaultsDraft?.tiers?.[tier]?.provider ?? cfg.provider ?? tiers?.default?.provider ?? 'openai';
|
||||
const model = _assistantModelDefaultsDraft?.tiers?.[tier]?.model ?? cfg.model ?? '';
|
||||
return `
|
||||
<div class="p-3 border border-zinc-800 rounded-md bg-zinc-950/60">
|
||||
<div class="text-sm text-zinc-50 mb-2">${escapeHtml(tier)} tier</div>
|
||||
@@ -822,8 +827,9 @@ function updateAssistantHealth(configData) {
|
||||
<div class="space-y-3">
|
||||
${taskRows.map((task) => {
|
||||
const background = backgroundModels?.[task.key] ?? {};
|
||||
const delegationTier = delegation?.[task.key] ?? 'fast';
|
||||
const backgroundEnabled = Boolean(backgroundModels?.[task.key] && background?.enabled !== false);
|
||||
const draftTask = _assistantModelDefaultsDraft?.tasks?.[task.key] ?? {};
|
||||
const delegationTier = draftTask.delegationTier ?? delegation?.[task.key] ?? 'fast';
|
||||
const backgroundEnabled = draftTask.backgroundEnabled ?? Boolean(backgroundModels?.[task.key] && background?.enabled !== false);
|
||||
return `
|
||||
<div class="p-3 border border-zinc-800 rounded-md bg-zinc-950/60">
|
||||
<div class="text-sm text-zinc-50 mb-2">${escapeHtml(task.label)}</div>
|
||||
@@ -841,17 +847,21 @@ function updateAssistantHealth(configData) {
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-zinc-500">Provider</span>
|
||||
<select id="assist-bg-${task.key}-provider" class="bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-md px-2 py-1.5 text-sm focus:border-blue-500 outline-none">
|
||||
${providerOption(background?.provider ?? tiers?.default?.provider ?? 'openai')}
|
||||
${providerOption(draftTask.provider ?? background?.provider ?? tiers?.default?.provider ?? 'openai')}
|
||||
</select>
|
||||
</label>
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-zinc-500">Model</span>
|
||||
${modelDataList(`assist-bg-${task.key}-model`, background?.provider ?? tiers?.default?.provider ?? 'openai', background?.model ?? '')}
|
||||
${modelDataList(
|
||||
`assist-bg-${task.key}-model`,
|
||||
draftTask.provider ?? background?.provider ?? tiers?.default?.provider ?? 'openai',
|
||||
draftTask.model ?? background?.model ?? '',
|
||||
)}
|
||||
</label>
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-zinc-500">Fallback tier</span>
|
||||
<select id="assist-bg-${task.key}-fallback" class="bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-md px-2 py-1.5 text-sm focus:border-blue-500 outline-none">
|
||||
${tierOption(background?.fallback_tier ?? 'fast')}
|
||||
${tierOption(draftTask.fallbackTier ?? background?.fallback_tier ?? 'fast')}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
@@ -928,7 +938,7 @@ function updateAssistantHealth(configData) {
|
||||
});
|
||||
}
|
||||
|
||||
const taskRowsForModels = ['compaction', 'memory_extraction', 'classification', 'tool_summarisation', 'complex_reasoning'];
|
||||
const taskRowsForModels = MODEL_DEFAULT_TASK_KEYS;
|
||||
for (const task of taskRowsForModels) {
|
||||
const providerSelect = el.querySelector(`#assist-bg-${task}-provider`);
|
||||
if (!providerSelect) {continue;}
|
||||
@@ -996,11 +1006,11 @@ function updateAssistantHealth(configData) {
|
||||
};
|
||||
_assistantManualOverrides.add('automation.daily_briefing.enabled');
|
||||
} else if (action === 'save-model-defaults') {
|
||||
const tasks = ['compaction', 'memory_extraction', 'classification', 'tool_summarisation', 'complex_reasoning'];
|
||||
const tasks = MODEL_DEFAULT_TASK_KEYS;
|
||||
patches = {
|
||||
'agents.primary_tier': (el.querySelector('#assist-primary-tier')?.value ?? 'default'),
|
||||
};
|
||||
const tiers = ['default', 'fast', 'complex', 'local'];
|
||||
const tiers = MODEL_DEFAULT_TIER_KEYS;
|
||||
for (const tier of tiers) {
|
||||
const provider = (el.querySelector(`#assist-tier-${tier}-provider`)?.value ?? '').trim();
|
||||
const model = (el.querySelector(`#assist-tier-${tier}-model`)?.value ?? '').trim();
|
||||
@@ -1033,7 +1043,12 @@ function updateAssistantHealth(configData) {
|
||||
}
|
||||
}
|
||||
if (!patches) {return;}
|
||||
await applyAssistantPatch(patches, statusEl);
|
||||
const patchResult = await applyAssistantPatch(patches, statusEl);
|
||||
if (action === 'save-model-defaults' && patchResult.applied.length > 0 && patchResult.rejected.length === 0) {
|
||||
_assistantModelDefaultsDraft = null;
|
||||
} else if (action === 'save-model-defaults') {
|
||||
_assistantModelDefaultsDraft = readAssistantModelDefaultsDraft(el) ?? _assistantModelDefaultsDraft;
|
||||
}
|
||||
// Force immediate refresh of slow sections after applying.
|
||||
const refreshed = await fetchSlow(_dashboardClient);
|
||||
if (refreshed) {
|
||||
@@ -1052,6 +1067,38 @@ function updateAssistantHealth(configData) {
|
||||
});
|
||||
}
|
||||
|
||||
function readAssistantModelDefaultsDraft(rootEl) {
|
||||
const primaryTier = rootEl.querySelector('#assist-primary-tier')?.value;
|
||||
if (!primaryTier) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tiers = {};
|
||||
for (const tier of MODEL_DEFAULT_TIER_KEYS) {
|
||||
const provider = (rootEl.querySelector(`#assist-tier-${tier}-provider`)?.value ?? '').trim();
|
||||
const model = (rootEl.querySelector(`#assist-tier-${tier}-model`)?.value ?? '').trim();
|
||||
if (!provider && !model) {continue;}
|
||||
tiers[tier] = { provider, model };
|
||||
}
|
||||
|
||||
const tasks = {};
|
||||
for (const task of MODEL_DEFAULT_TASK_KEYS) {
|
||||
tasks[task] = {
|
||||
delegationTier: rootEl.querySelector(`#assist-delegation-${task}`)?.value ?? 'fast',
|
||||
backgroundEnabled: Boolean(rootEl.querySelector(`#assist-bg-${task}-enabled`)?.checked),
|
||||
provider: (rootEl.querySelector(`#assist-bg-${task}-provider`)?.value ?? '').trim(),
|
||||
model: (rootEl.querySelector(`#assist-bg-${task}-model`)?.value ?? '').trim(),
|
||||
fallbackTier: rootEl.querySelector(`#assist-bg-${task}-fallback`)?.value ?? 'fast',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
primaryTier,
|
||||
tiers,
|
||||
tasks,
|
||||
};
|
||||
}
|
||||
|
||||
function _updateChannels(channelsData) {
|
||||
const el = document.getElementById('ops-channels');
|
||||
if (!el) {return;}
|
||||
@@ -1248,5 +1295,6 @@ export const DashboardPage = {
|
||||
_assistantSaveState = null;
|
||||
_lastAssistantConfig = null;
|
||||
_assistantManualOverrides = new Set();
|
||||
_assistantModelDefaultsDraft = null;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user