Fix model management: empty LiteLLM list, always reload panel, clear-all
- LITELLM_MODELS = [] — no hardcoded models, global selector now only shows what admin has actually added via Search API - getAvailableModelsWithOverrides: for LiteLLM returns only custom list - Remove toggle safety check — admin can disable any/all models freely - Admin panel always reloads on tab open (was cached, showing stale data) - Add 'Clear all models' button for LiteLLM to wipe and start fresh - Add POST /config/models/clear-all endpoint
This commit is contained in:
parent
683afeea0b
commit
58c8f1c549
3 changed files with 41 additions and 31 deletions
|
|
@ -579,11 +579,9 @@
|
|||
// ============================================================
|
||||
(function() {
|
||||
|
||||
var modelsLoaded = false;
|
||||
|
||||
document.addEventListener('tabChanged', function(e) {
|
||||
if (e.detail && e.detail.tab === 'admin') {
|
||||
if (!modelsLoaded) { loadAdminModels(); modelsLoaded = true; }
|
||||
loadAdminModels();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -591,6 +589,7 @@
|
|||
if (e.target.closest('#btn-discover-models')) discoverModels();
|
||||
if (e.target.closest('#btn-add-custom-model')) addCustomModel();
|
||||
if (e.target.closest('#btn-save-default-model')) saveDefaultModel();
|
||||
if (e.target.closest('#btn-clear-all-models')) clearAllModels();
|
||||
});
|
||||
|
||||
// Allow Enter key to trigger search
|
||||
|
|
@ -640,9 +639,11 @@
|
|||
var container = document.getElementById('admin-builtin-models');
|
||||
if (container) {
|
||||
if (data.litellmHint) {
|
||||
container.innerHTML = '<p style="color:var(--g500);font-size:13px;padding:8px;background:var(--g50);border-radius:6px;">' +
|
||||
'<i class="fas fa-info-circle"></i> LiteLLM has no built-in model list — your available models depend on your proxy configuration. ' +
|
||||
'Use <strong>Search API</strong> below to discover what models your LiteLLM proxy exposes, then add them.</p>';
|
||||
container.innerHTML = '<div style="padding:10px 12px;background:var(--g50);border-radius:6px;font-size:13px;color:var(--g600);">' +
|
||||
'<p style="margin:0 0 8px;"><i class="fas fa-info-circle" style="color:var(--blue);"></i> <strong>LiteLLM mode:</strong> No built-in models. ' +
|
||||
'Use <strong>Search API</strong> below to discover models from your proxy, then add them.</p>' +
|
||||
'<button id="btn-clear-all-models" class="btn-sm" style="background:var(--red-light);color:var(--red);border:none;border-radius:6px;padding:4px 12px;font-size:12px;cursor:pointer;">' +
|
||||
'<i class="fas fa-trash"></i> Clear all added models</button></div>';
|
||||
} else if (data.models.length === 0) {
|
||||
container.innerHTML = '<p style="color:var(--g400);font-size:13px;">No built-in models for this provider.</p>';
|
||||
} else {
|
||||
|
|
@ -846,6 +847,21 @@
|
|||
.catch(function() { showToast('Request failed', 'error'); });
|
||||
}
|
||||
|
||||
function clearAllModels() {
|
||||
if (!confirm('Remove all added models? You will need to re-add them via Search API.')) return;
|
||||
Promise.all([
|
||||
fetch('/api/admin/config/models/clear-all', { method: 'POST', headers: getAuthHeaders() })
|
||||
.then(function(r) { return r.json(); })
|
||||
]).then(function(results) {
|
||||
if (results[0].success) {
|
||||
showToast('All models cleared', 'info');
|
||||
loadAdminModels();
|
||||
} else {
|
||||
showToast(results[0].error || 'Failed', 'error');
|
||||
}
|
||||
}).catch(function() { showToast('Request failed', 'error'); });
|
||||
}
|
||||
|
||||
function saveDefaultModel() {
|
||||
var sel = document.getElementById('admin-default-model');
|
||||
if (!sel || !sel.value) { showToast('Select a model', 'error'); return; }
|
||||
|
|
|
|||
|
|
@ -248,17 +248,6 @@ router.put('/config/models/toggle', async function(req, res) {
|
|||
disabled = disabled.filter(function(id) { return id !== modelId; });
|
||||
} else {
|
||||
if (!disabled.includes(modelId)) disabled.push(modelId);
|
||||
|
||||
// Safety: prevent disabling all models
|
||||
var { getAvailableModels } = require('../utils/models');
|
||||
var allModels = getAvailableModels();
|
||||
var customRaw = await db.getSetting('models.custom') || '[]';
|
||||
var custom;
|
||||
try { custom = JSON.parse(customRaw); } catch(e) { custom = []; }
|
||||
var enabledCount = allModels.filter(function(m) { return !disabled.includes(m.id); }).length + custom.length;
|
||||
if (enabledCount === 0) {
|
||||
return res.status(400).json({ error: 'Cannot disable all models. At least one must remain enabled.' });
|
||||
}
|
||||
}
|
||||
|
||||
await db.setSetting('models.disabled', JSON.stringify(disabled));
|
||||
|
|
@ -328,6 +317,17 @@ router.delete('/config/models/custom/:modelId(*)', async function(req, res) {
|
|||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
|
||||
// ── POST clear all custom/discovered models ───────────────────────────────
|
||||
router.post('/config/models/clear-all', async function(req, res) {
|
||||
try {
|
||||
await db.setSetting('models.custom', '[]');
|
||||
await db.setSetting('models.disabled', '[]');
|
||||
await db.setSetting('models.default', '');
|
||||
logger.audit(req.user.id, 'admin_models_clear_all', 'Cleared all custom models and disabled list', req, { category: 'admin' });
|
||||
res.json({ success: true });
|
||||
} catch (e) { res.status(500).json({ error: e.message }); }
|
||||
});
|
||||
|
||||
// ── GET discover models from provider API ─────────────────────────────────
|
||||
router.get('/config/models/discover', async function(req, res) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -135,17 +135,9 @@ var VERTEX_MODELS = [
|
|||
vertexId: 'meta/llama-3.1-405b-instruct-maas' },
|
||||
];
|
||||
|
||||
// ============================================================
|
||||
// LITELLM MODELS (default set — admin can discover more via API)
|
||||
// ============================================================
|
||||
var LITELLM_MODELS = [
|
||||
{ id: 'gpt-4o', name: 'GPT-4o', cost: '~$0.01', tag: 'SMART', category: 'premium' },
|
||||
{ id: 'gpt-4o-mini', name: 'GPT-4o Mini', cost: '~$0.003', tag: 'FAST', category: 'smart' },
|
||||
{ id: 'vendor-model-sonnet-4-6-20250514', name: 'vendor model Sonnet 4.6', cost: '~$0.015', tag: 'PREMIUM', category: 'premium' },
|
||||
{ id: 'vendor-model-haiku-4-5-20251001', name: 'vendor model Haiku 4.5', cost: '~$0.003', tag: 'FAST', category: 'smart' },
|
||||
{ id: 'gemini/gemini-2.5-flash', name: 'Gemini 2.5 Flash', cost: '~$0.001', tag: 'VALUE', category: 'fast' },
|
||||
{ id: 'gemini/gemini-2.5-pro', name: 'Gemini 2.5 Pro', cost: '~$0.01', tag: 'SMART', category: 'premium' },
|
||||
];
|
||||
// LiteLLM has NO built-in models — everything is discovered from the proxy via admin panel.
|
||||
// This array is intentionally empty. Do not add models here.
|
||||
var LITELLM_MODELS = [];
|
||||
|
||||
function getAvailableModels() {
|
||||
switch (activeProvider) {
|
||||
|
|
@ -167,7 +159,7 @@ function getDefaultModel() {
|
|||
case 'bedrock': return 'anthropic/vendor-model-sonnet-4-6';
|
||||
case 'azure': return process.env.AZURE_DEPLOYMENT_NAME || 'gpt-4o-mini';
|
||||
case 'vertex': return 'gemini-2.5-flash';
|
||||
case 'litellm': return 'gpt-4o-mini';
|
||||
case 'litellm': return ''; // No default — set via admin panel or LITELLM_DEFAULT_MODEL env
|
||||
case 'openrouter':
|
||||
default: return 'google/gemini-2.5-flash';
|
||||
}
|
||||
|
|
@ -178,7 +170,7 @@ function getFallbackModel() {
|
|||
case 'bedrock': return 'anthropic/vendor-model-haiku-4-5';
|
||||
case 'azure': return process.env.AZURE_DEPLOYMENT_NAME || 'gpt-4o-mini';
|
||||
case 'vertex': return 'gemini-2.0-flash';
|
||||
case 'litellm': return 'gpt-4o';
|
||||
case 'litellm': return ''; // No fallback — use whatever the user has configured
|
||||
case 'openrouter':
|
||||
default: return 'deepseek/deepseek-chat-v3-0324';
|
||||
}
|
||||
|
|
@ -217,8 +209,10 @@ async function getAvailableModelsWithOverrides(db) {
|
|||
try { disabled = JSON.parse(disabledRaw); } catch(e) { disabled = []; }
|
||||
try { custom = JSON.parse(customRaw); } catch(e) { custom = []; }
|
||||
|
||||
// For LiteLLM: no built-ins exist — the model list IS the custom/discovered list
|
||||
if (activeProvider === 'litellm') return custom;
|
||||
|
||||
var result = baseModels.filter(function(m) { return !disabled.includes(m.id); });
|
||||
// Add custom models
|
||||
custom.forEach(function(m) {
|
||||
if (!result.find(function(r) { return r.id === m.id; })) result.push(m);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue