feat(admin): Dropdown „Zentrales KI-Modell" — erprobte Presets ruck-zuck umstellen
Neue Karte in Admin › System › Status: ein Dropdown kuratierter, erprobter
Modelle, das Provider + Modell in einem Klick als Laufzeit-Override umstellt
(wirkt sofort, kein Neustart).
- Presets in config/models.yaml (Label, optional note, set:{key:value} aus
RUNTIME_SETTABLE, default:true = Config-Standard). Loader app/model_presets.py
(nur erlaubte LLM-Keys: default_llm_provider, openrouter_llm_model,
local_llm_model).
- Endpunkte: GET /api/admin/models (Presets + aktuell aktives LLM + active_index),
POST /api/admin/models/select (Default -> Overrides löschen, sonst Override-Keys
setzen) — nutzt set_/delete_config_override + invalidate_cache, mit Audit-Log.
- Frontend: Karte zeigt Dropdown + „Aktiv: <provider> · <modell>"; Auswahl wirkt
sofort und lädt den Status neu. Bump app.js v=52 -> v=53.
- Startliste: Mistral Small 24B (Standard), DeepSeek V4 Flash/Pro, Ollama Gemma 3.
Tests: Laden/Default-aktiv/Select-setzt-Override/Default-löscht/404/Auth.
263 Offline-Tests grün. Live via Headless-Chrome verifiziert (Auswahl ->
Override gesetzt, Provider-Karte spiegelt das Modell).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2ccfd7f6d6
commit
18ad442c94
6 changed files with 243 additions and 2 deletions
|
|
@ -1570,13 +1570,36 @@ async function loadStatus() {
|
|||
const container = $("#status-content");
|
||||
container.innerHTML = '<p class="text-sm text-slate-400">lade …</p>';
|
||||
try {
|
||||
const [cfg, met, runtimeCfg] = await Promise.all([
|
||||
const [cfg, met, runtimeCfg, models] = await Promise.all([
|
||||
fetch("/api/config").then((r) => r.json()),
|
||||
fetch("/api/metrics").then((r) => r.json()),
|
||||
adminFetch("/api/admin/config"),
|
||||
adminFetch("/api/admin/models"),
|
||||
]);
|
||||
const cfgByKey = Object.fromEntries((runtimeCfg || []).map((d) => [d.key, d.effective_value]));
|
||||
const llmModel = cfgByKey["openrouter_llm_model"] || "—";
|
||||
|
||||
// Dropdown „Zentrales KI-Modell" (erprobte Presets, Auswahl wirkt sofort).
|
||||
const presets = (models && models.presets) || [];
|
||||
const activeIdx = models ? models.active_index : null;
|
||||
const cur = (models && models.current) || {};
|
||||
const curProv = cur.default_llm_provider || "—";
|
||||
const curModel = curProv === "openrouter"
|
||||
? (cur.openrouter_llm_model || "—") : (cur.local_llm_model || "—");
|
||||
const modelOptions = presets.map((p) =>
|
||||
`<option value="${p.index}"${p.index === activeIdx ? " selected" : ""}>${escHtml(p.label)}</option>`).join("");
|
||||
const modelCard = presets.length ? `
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4">
|
||||
<h3 class="font-semibold text-sm mb-1">Zentrales KI-Modell</h3>
|
||||
<p class="text-xs text-slate-400 mb-3">Erprobte Modelle — Auswahl wirkt sofort (kein Neustart).</p>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<select id="model-preset" class="text-sm rounded-lg border border-slate-300 dark:border-slate-600 bg-transparent px-3 py-1.5">
|
||||
${modelOptions}
|
||||
</select>
|
||||
<span id="model-preset-msg" class="text-xs text-slate-400"></span>
|
||||
</div>
|
||||
<p class="text-xs text-slate-400 mt-2">Aktiv: <span class="font-mono text-slate-600 dark:text-slate-300">${escHtml(curProv)} · ${escHtml(curModel)}</span></p>
|
||||
</div>` : "";
|
||||
const route = cfg.default_route || {};
|
||||
const counters = met.counters || {};
|
||||
const turnsTotal = counters["turns_total"] || 0;
|
||||
|
|
@ -1602,6 +1625,8 @@ async function loadStatus() {
|
|||
</dl>
|
||||
</div>
|
||||
|
||||
${modelCard}
|
||||
|
||||
<!-- Provider -->
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4">
|
||||
<h3 class="font-semibold text-sm mb-3">Provider (Standard-Route)</h3>
|
||||
|
|
@ -1633,12 +1658,29 @@ async function loadStatus() {
|
|||
</dl>
|
||||
</div>
|
||||
`;
|
||||
wireModelPicker();
|
||||
} catch (e) {
|
||||
console.error("loadStatus error:", e);
|
||||
container.innerHTML = '<p class="text-sm text-red-500">Fehler beim Laden.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown „Zentrales KI-Modell": Auswahl wendet ein Preset an (Laufzeit-Override).
|
||||
function wireModelPicker() {
|
||||
const sel = $("#model-preset");
|
||||
const msg = $("#model-preset-msg");
|
||||
if (!sel) return;
|
||||
sel.addEventListener("change", async () => {
|
||||
msg.textContent = "stelle um …";
|
||||
sel.disabled = true;
|
||||
const res = await adminFetch("/api/admin/models/select", "POST", { index: +sel.value });
|
||||
sel.disabled = false;
|
||||
if (res === null) { msg.textContent = "Fehler beim Umstellen"; return; }
|
||||
msg.textContent = "✓ " + (res.selected || "umgestellt");
|
||||
loadStatus(); // Karte + „Aktiv"-Anzeige aktualisieren
|
||||
});
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════
|
||||
// TAB: WARTUNG (Konfiguration)
|
||||
// ════════════════════════════════════════
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue