Feat: Hirn-Bereitschaft im /api/health + Frontend-Warnung
/api/health liefert jetzt brain:{role,model,ready} (echter /running-Check —
ein abgestuerztes/nicht geladenes Agent-Hirn 'fast' erscheint dort nicht).
Frontend zeigt 'Hirn offline (model)' + Amber-Punkt, statt dass der Ausfall
nur als App-Fehler ('Provider returned an empty stream') auftaucht.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,4 +15,7 @@ def health() -> dict:
|
|||||||
"version": VERSION,
|
"version": VERSION,
|
||||||
"engine_reachable": llamaswap.engine_reachable(),
|
"engine_reachable": llamaswap.engine_reachable(),
|
||||||
"gateway_reachable": gateway.gateway_reachable(),
|
"gateway_reachable": gateway.gateway_reachable(),
|
||||||
|
# Echte Hirn-Bereitschaft: Engine kann erreichbar sein, das Agent-Hirn ('fast') aber tot
|
||||||
|
# (Crash/OOM nach Engine-Update). Das wäre sonst ein silent fail (App-Fehler statt Status).
|
||||||
|
"brain": llamaswap.brain_status(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -432,6 +432,26 @@ def delete_model(model_id: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def brain_model_name() -> str | None:
|
||||||
|
"""Modellname des Agent-Hirns = das Modell mit llama-swap-Alias/Rolle 'fast'."""
|
||||||
|
for m in list_models():
|
||||||
|
names = {str(a).lower() for a in (m.get("aliases") or [])}
|
||||||
|
if m.get("role"):
|
||||||
|
names.add(str(m["role"]).lower())
|
||||||
|
if "fast" in names:
|
||||||
|
return m["name"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def brain_status() -> dict:
|
||||||
|
"""Ist das Agent-Hirn ('fast') WIRKLICH geladen & bereit? Prüft /running — ein abgestürztes
|
||||||
|
Modell (z.B. OOM/Crash nach Engine-Update) erscheint dort NICHT als running. Fängt damit den
|
||||||
|
Fall 'Engine erreichbar, aber Hirn tot', den engine_reachable() nicht sieht (silent fail)."""
|
||||||
|
name = brain_model_name()
|
||||||
|
running = get_running_models()
|
||||||
|
return {"role": "fast", "model": name, "ready": bool(name and name in running)}
|
||||||
|
|
||||||
|
|
||||||
def get_running_models() -> list[str]:
|
def get_running_models() -> list[str]:
|
||||||
"""Fragt den /running Endpunkt von llama-swap ab. Gibt die Namen der geladenen
|
"""Fragt den /running Endpunkt von llama-swap ab. Gibt die Namen der geladenen
|
||||||
Modelle zurück. Neuere llama-swap-Versionen liefern Objekte ({model, state, ...})
|
Modelle zurück. Neuere llama-swap-Versionen liefern Objekte ({model, state, ...})
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>Mission Control 2.0</title>
|
<title>Mission Control 2.0</title>
|
||||||
<script type="module" crossorigin src="/assets/index-4xoYNTUx.js"></script>
|
<script type="module" crossorigin src="/assets/index-BcdAxUp3.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BFWF7uFR.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-BFWF7uFR.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
+16
-2
@@ -119,16 +119,30 @@ export default function App() {
|
|||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"h-2.5 w-2.5 rounded-full ring-2 ring-black/40",
|
"h-2.5 w-2.5 rounded-full ring-2 ring-black/40",
|
||||||
health ? (health.engine_reachable ? "bg-emerald-500 animate-pulse" : "bg-amber-500") : "bg-red-500"
|
health
|
||||||
)} title={health ? `Engine ${health.engine_reachable ? "online" : "offline"}` : "Backend offline"} />
|
? (!health.engine_reachable ? "bg-amber-500"
|
||||||
|
: (health.brain && !health.brain.ready ? "bg-amber-500 animate-pulse" : "bg-emerald-500 animate-pulse"))
|
||||||
|
: "bg-red-500"
|
||||||
|
)} title={health
|
||||||
|
? (!health.engine_reachable ? "Engine offline"
|
||||||
|
: (health.brain && !health.brain.ready ? `Hirn offline (${health.brain.model ?? "fast"})` : "Engine + Hirn online"))
|
||||||
|
: "Backend offline"} />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2 text-left">
|
<div className="space-y-2 text-left">
|
||||||
{health ? (
|
{health ? (
|
||||||
|
<>
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<span className={cn("h-2 w-2 rounded-full animate-pulse", health.engine_reachable ? "bg-emerald-500" : "bg-amber-500")} />
|
<span className={cn("h-2 w-2 rounded-full animate-pulse", health.engine_reachable ? "bg-emerald-500" : "bg-amber-500")} />
|
||||||
<span className="truncate">Engine {health.engine_reachable ? "online" : "offline"}</span>
|
<span className="truncate">Engine {health.engine_reachable ? "online" : "offline"}</span>
|
||||||
</span>
|
</span>
|
||||||
|
{health.brain && !health.brain.ready && (
|
||||||
|
<span className="flex items-center gap-2 text-amber-400" title={`Agent-Hirn '${health.brain.model ?? "fast"}' lädt nicht/abgestürzt`}>
|
||||||
|
<span className="h-2 w-2 rounded-full bg-amber-500 animate-pulse" />
|
||||||
|
<span className="truncate">Hirn offline{health.brain.model ? ` (${health.brain.model})` : ""}</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<span className="h-2 w-2 rounded-full bg-red-500" /> <span className="truncate">Backend offline</span>
|
<span className="h-2 w-2 rounded-full bg-red-500" /> <span className="truncate">Backend offline</span>
|
||||||
|
|||||||
@@ -351,6 +351,7 @@ export interface Health {
|
|||||||
version: string
|
version: string
|
||||||
engine_reachable: boolean
|
engine_reachable: boolean
|
||||||
gateway_reachable: boolean
|
gateway_reachable: boolean
|
||||||
|
brain?: { role: string; model: string | null; ready: boolean }
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenStats {
|
export interface TokenStats {
|
||||||
|
|||||||
Reference in New Issue
Block a user