mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-29 04:10:00 +08:00
fix(models): stop auto-filling fallback chain and add clear-all button
The fallback editor's "Add" buttons appeared to do nothing because applyDefaultModel auto-populated defaults.model.fallbacks with every non-primary model whenever the list was empty (and the same for defaults.models). After any save with an empty chain, the chain got filled with all 17+ candidates; the candidate pool became "No candidate models available" and any subsequent Add click hit the early return `if (modelConfig.fallbacks.includes(full)) return`. Even worse, the auto-fill made it impossible for users to keep an empty fallback chain on purpose: deleting all chips would silently get replaced with every model on the next debounced autosave. Remove the auto-fill in applyDefaultModel. An empty fallback chain is a valid configuration that means "no implicit fallback"; Gateway already surfaces a clear primary-model error in that case. normalizeDefaultModel Selection still cleans up invalid/duplicate entries. Also add a small "Clear All" button next to the active chain title so users can drop the entire fallback list in one click instead of removing chips one by one (especially useful for the existing bloated 17-fallback state created by the old auto-fill path). ## Verification - node --check src/pages/models.js - node --check src/locales/modules/models.js - npm run build - Playwright repro: open /#/models → Clear All → fallbacks on disk go from 17 → 0 → click Add on one candidate → fallbacks on disk are exactly that one entry, no longer auto-expanded back to 17.
This commit is contained in:
@@ -191,4 +191,7 @@ export default {
|
||||
setAsPrimary: _('设为主用', 'Set as Primary', '設為主用'),
|
||||
remove: _('移除', 'Remove', '移除'),
|
||||
add: _('加入', 'Add', '加入'),
|
||||
addAll: _('全部加入', 'Add All', '全部加入'),
|
||||
clearAll: _('清空全部', 'Clear All', '清空全部'),
|
||||
confirmClearAll: _('确定清空所有备选模型?主模型不会被影响。', 'Clear all fallback models? Primary model is not affected.', '確定清空所有備選模型?主模型不會被影響。'),
|
||||
}
|
||||
|
||||
@@ -301,7 +301,10 @@ function renderFallbackWaterfall(state) {
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px;">
|
||||
<div style="background: var(--bg-tertiary); padding: 12px; border-radius: var(--radius-md); border: 1px solid var(--border-color);">
|
||||
<div style="font-size: var(--font-size-xs); font-weight: bold; margin-bottom: 8px; color: var(--text-tertiary);">${t('models.activeChainTitle')}</div>
|
||||
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom: 8px;">
|
||||
<div style="font-size: var(--font-size-xs); font-weight: bold; color: var(--text-tertiary);">${t('models.activeChainTitle')}</div>
|
||||
${currentFallbacks.length > 0 ? `<button class="btn btn-xs btn-secondary btn-clear-all-fb" style="padding:1px 6px; font-size:10px;">${t('models.clearAll')}</button>` : ''}
|
||||
</div>
|
||||
<div id="active-fallback-list" style="display: flex; flex-direction: column; gap: 4px; min-height: 50px;">
|
||||
${currentFallbacks.map((f, i) => `
|
||||
<div class="fallback-chain-item" data-id="${f}" style="display: flex; align-items: center; justify-content: space-between; background: var(--bg-primary); padding: 6px 10px; border-radius: 4px; border: 1px solid var(--border-color);">
|
||||
@@ -398,6 +401,22 @@ function bindWaterfallActions(page, state) {
|
||||
}
|
||||
})
|
||||
|
||||
// 清空全部备选
|
||||
const clearAllBtn = container.querySelector('.btn-clear-all-fb')
|
||||
if (clearAllBtn) {
|
||||
clearAllBtn.onclick = async () => {
|
||||
const yes = await showConfirm(t('models.confirmClearAll'))
|
||||
if (!yes) return
|
||||
const modelConfig = ensureDefaultModelConfig(state)
|
||||
if (!modelConfig.fallbacks.length) return
|
||||
pushUndo(state)
|
||||
modelConfig.fallbacks = []
|
||||
renderDefaultBar(page, state)
|
||||
updateUndoBtn(page, state)
|
||||
autoSave(state)
|
||||
}
|
||||
}
|
||||
|
||||
// 折叠候选服务商
|
||||
container.querySelectorAll('.candidate-provider-header').forEach(header => {
|
||||
header.onclick = () => {
|
||||
@@ -1131,20 +1150,13 @@ function applyDefaultModel(state) {
|
||||
const defaults = state.config.agents.defaults
|
||||
if (!defaults.model) defaults.model = {}
|
||||
defaults.model.primary = primary
|
||||
if (!Array.isArray(defaults.model.fallbacks)) defaults.model.fallbacks = []
|
||||
if (!defaults.models || typeof defaults.models !== 'object' || Array.isArray(defaults.models)) defaults.models = {}
|
||||
|
||||
// fallbacks / models 仅在为空时初始化(首次安装友好),不再每次保存都覆盖
|
||||
// 避免用户精心维护的精简 fallback 链被重写,且随模型增多不断膨胀 (fixes #190)
|
||||
if (!defaults.model.fallbacks || defaults.model.fallbacks.length === 0) {
|
||||
const allModels = collectAllModels(state.config)
|
||||
defaults.model.fallbacks = allModels.filter(m => m.full !== primary).map(m => m.full)
|
||||
}
|
||||
if (!defaults.models || Object.keys(defaults.models).length === 0) {
|
||||
const allModels = collectAllModels(state.config)
|
||||
const modelsMap = {}
|
||||
modelsMap[primary] = {}
|
||||
for (const m of allModels) { if (m.full !== primary) modelsMap[m.full] = {} }
|
||||
defaults.models = modelsMap
|
||||
}
|
||||
// 注意:不再在 fallbacks/models 为空时自动塞入"全部可用模型"。
|
||||
// 旧逻辑会导致用户清空备选链或新增主模型后,一次保存就把所有候选自动加进 fallbacks/models,
|
||||
// 用户点"加入"时模型已经全部在备选链里 → 候选池空 → "加入"按钮显示"无可用候选模型",
|
||||
// 看起来就像"加入按钮没效果"。空备选链是合法状态:此时 Gateway 主模型失败直接报错,不做隐式 fallback。
|
||||
normalizeDefaultModelSelection(state.config)
|
||||
|
||||
// 注意:不再强制同步到各 agent 的 model.primary
|
||||
|
||||
Reference in New Issue
Block a user