Feat: Pool-Auslastungs-Mini-Bar in der Dashboard-Modelle-Karte

Schlanke Glance-Anzeige der echten Speicher-Pool-Belegung (gtt_used/gtt_total
inkl. KV aus sysStatus.gpu) oben in der Modelle-Karte — teal/amber/rot ab 70/88 %.
Keine Karten-Doppelung: die volle interaktive VRAM-Bar + „Alle entladen" bleibt
im Modell-Manager.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-29 19:33:36 +02:00
parent c1fac1e688
commit a894a4e952
6 changed files with 113 additions and 92 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -7,8 +7,8 @@
<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-DSZM1C9y.js"></script> <script type="module" crossorigin src="/assets/index-PXdvVWCT.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-UDlAwulM.css"> <link rel="stylesheet" crossorigin href="/assets/index-B_KoUJpZ.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from "react" import { useEffect, useRef, useState } from "react"
import { Boxes, Zap, Moon } from "lucide-react" import { Boxes, Zap, Moon } from "lucide-react"
import { useModels, useTokenStats } from "@/lib/queries" import { useModels, useTokenStats, useSystemStatus } from "@/lib/queries"
import { fmtSize } from "@/lib/format" import { fmtSize, gb } from "@/lib/format"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { ROLES, roleTone } from "@/components/models/ModelBadges" import { ROLES, roleTone } from "@/components/models/ModelBadges"
@@ -10,9 +10,18 @@ import { ROLES, roleTone } from "@/components/models/ModelBadges"
export function ModelsCard() { export function ModelsCard() {
const { data } = useModels(2_000) const { data } = useModels(2_000)
const { data: stats } = useTokenStats(2_000) const { data: stats } = useTokenStats(2_000)
const { data: sysStatus } = useSystemStatus()
const models = data?.models ?? [] const models = data?.models ?? []
const running = data?.running ?? [] const running = data?.running ?? []
// Echte Pool-Belegung (Unified-RAM inkl. KV) — schlanke Glance-Bar. Die volle
// Segment-Bar + „Alle entladen" bleibt im Modell-Manager.
const gttTotal = sysStatus?.gpu?.gtt_total || sysStatus?.gpu?.vram_total || 0
const gttUsed = sysStatus?.gpu?.gtt_used || 0
const poolPct = gttTotal > 0 ? Math.min(100, (gttUsed / gttTotal) * 100) : 0
const poolBar = poolPct >= 88 ? "bg-red-500" : poolPct >= 70 ? "bg-amber-500" : "bg-teal-500"
const poolText = poolPct >= 88 ? "text-red-400" : poolPct >= 70 ? "text-amber-400" : "text-foreground"
// „Inferenz aktiv": total_tokens seit letztem Poll gestiegen → es wird generiert. // „Inferenz aktiv": total_tokens seit letztem Poll gestiegen → es wird generiert.
const prev = useRef<number | null>(null) const prev = useRef<number | null>(null)
const [generating, setGenerating] = useState(false) const [generating, setGenerating] = useState(false)
@@ -46,6 +55,18 @@ export function ModelsCard() {
</span> </span>
</div> </div>
{gttTotal > 0 && (
<div className="mb-4">
<div className="flex items-center justify-between text-[10px] font-mono text-muted-foreground/80 mb-1">
<span className="uppercase tracking-wider font-semibold">Speicher-Pool</span>
<span><span className={cn("font-bold", poolText)}>{gb(gttUsed)}</span> / {gb(gttTotal)} GB belegt</span>
</div>
<div className="h-1.5 w-full rounded-full bg-background/50 border border-border/30 overflow-hidden">
<div className={cn("h-full rounded-full transition-all duration-500", poolBar)} style={{ width: `${poolPct}%` }} />
</div>
</div>
)}
<div className="space-y-2 flex-1"> <div className="space-y-2 flex-1">
{ROLES.map((role) => { {ROLES.map((role) => {
const m = models.find((x) => x.role === role) const m = models.find((x) => x.role === role)