feat(admin): LLM-/GPU-Status-Karte (read-only) — Plan-Schritt 2
- app/admin_llm.py: read-only Statusabfragen ohne sudo (docker ps, ollama ps, nvidia-smi, systemctl --user) mit sicheren Defaults bei fehlenden Tools. - GET /api/admin/llm/status (require_admin): Backend, Modell, Backend-Status, geladene Ollama-Modelle, GPU-Auslastung, Gateway-Dienst-Status. - Admin Status-Tab: LLM-Backend-Karte mit GPU-Balken. - Tests: Auth-Gate + Antwortschema (160 grün). - Doku §7.5: Status-Tab um LLM/GPU-Karte ergänzt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bd20a308de
commit
97ae0a5d34
6 changed files with 216 additions and 3 deletions
|
|
@ -956,13 +956,56 @@ async function loadEmergencyEvents() {
|
|||
// ════════════════════════════════════════
|
||||
// TAB: STATUS
|
||||
// ════════════════════════════════════════
|
||||
// LLM-/GPU-Status-Karte (read-only) für den Status-Tab.
|
||||
function renderLlmCard(llm) {
|
||||
if (!llm) return "";
|
||||
const backendLabel = { ollama: "Ollama", llamacpp: "llama.cpp", unknown: "—" }[llm.backend] || llm.backend;
|
||||
const dot = (on) => `<span class="h-2.5 w-2.5 rounded-full ${on ? "bg-emerald-500" : "bg-slate-400"}"></span>`;
|
||||
const row = (label, val) =>
|
||||
`<dt class="text-slate-500 dark:text-slate-400">${label}</dt>
|
||||
<dd class="font-mono text-xs text-slate-700 dark:text-slate-300">${escHtml(val ?? "—")}</dd>`;
|
||||
|
||||
const loaded = (llm.ollama_loaded || []).map((m) =>
|
||||
`${escHtml(m.name)} <span class="text-slate-400">(${escHtml(m.processor || "")})</span>`
|
||||
).join(", ") || "—";
|
||||
|
||||
const gpuBars = (llm.gpus || []).map((g) => `
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="w-10 shrink-0 text-xs text-slate-500 dark:text-slate-400">GPU ${g.index}</span>
|
||||
<div class="flex-1 h-3 bg-slate-100 dark:bg-slate-700 rounded-full overflow-hidden">
|
||||
<div class="h-full ${g.percent > 85 ? "bg-red-500" : "bg-blue-500 dark:bg-blue-600"} rounded-full" style="width:${g.percent}%"></div>
|
||||
</div>
|
||||
<span class="w-28 text-right text-xs font-mono text-slate-500 dark:text-slate-400">${g.used_mib} / ${g.total_mib} MiB</span>
|
||||
</div>`).join("") || '<p class="text-xs text-slate-400">keine GPU-Daten (nvidia-smi)</p>';
|
||||
|
||||
return `
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4">
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
${dot(true)}<h3 class="font-semibold text-sm">LLM-Backend</h3>
|
||||
<span class="text-xs rounded-full bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300 px-2 py-0.5 font-medium">${escHtml(backendLabel)}</span>
|
||||
</div>
|
||||
<dl class="grid grid-cols-2 gap-x-8 gap-y-1.5 text-sm mb-3">
|
||||
${row("Modell", llm.model)}
|
||||
${row("URL", llm.base_url)}
|
||||
${row("Ollama erreichbar", llm.ollama_reachable ? "ja" : "nein")}
|
||||
${row("llama.cpp läuft", llm.llamacpp_running ? "ja" : "nein")}
|
||||
${row("Geladene Modelle", loaded)}
|
||||
${row("Gateway-Dienst", llm.gateway_service_active ? "aktiv (systemd)" : "Vordergrund/aus")}
|
||||
</dl>
|
||||
<div class="space-y-2 pt-2 border-t border-slate-100 dark:border-slate-700">
|
||||
${gpuBars}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async function loadStatus() {
|
||||
const container = $("#status-content");
|
||||
container.innerHTML = '<p class="text-sm text-slate-400">lade …</p>';
|
||||
try {
|
||||
const [cfg, met] = await Promise.all([
|
||||
const [cfg, met, llm] = await Promise.all([
|
||||
fetch("/api/config").then((r) => r.json()),
|
||||
fetch("/api/metrics").then((r) => r.json()),
|
||||
adminFetch("/api/admin/llm/status"),
|
||||
]);
|
||||
const route = cfg.default_route || {};
|
||||
const counters = met.counters || {};
|
||||
|
|
@ -1000,6 +1043,8 @@ async function loadStatus() {
|
|||
</dl>
|
||||
</div>
|
||||
|
||||
${renderLlmCard(llm)}
|
||||
|
||||
<!-- Laufzeit-Metriken -->
|
||||
<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">Laufzeit-Metriken</h3>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue