import { HardDrive } from "lucide-react" import type { ModelInfo } from "@/lib/api" import { fmtSize, gb } from "@/lib/format" import { roleMeta } from "@/lib/roleMeta" import { cn } from "@/lib/utils" import { roleBarColor } from "./roleColors" const B = 1024 ** 3 const short = (name: string) => name.split("/").pop()?.replace(/\.gguf$/i, "") ?? name // Maschinenraum-Speicherleiste — das Signatur-Element. EIN großer Balken „Arbeitsspeicher // · 124 GB", farbig aufgeteilt nach geladenem Modell (nach Rolle eingefärbt) + freier Rest. // Man sieht LIVE, wohin die GB gehen. Ein Klick auf ein Segment wählt das Modell rechts. export function MaschinenraumBar({ running, capacityBytes, realUsedBytes, selected, onSelect, }: { running: ModelInfo[] capacityBytes: number realUsedBytes: number selected?: string | null onSelect?: (name: string) => void }) { const capacity = capacityBytes > 2 * B ? capacityBytes : 124 * B const weights = running.reduce((a, m) => a + (m.size_bytes || 0), 0) // Ehrliche Rechnung: „frei" geht von der REALEN Belegung aus (inkl. KV-Cache), nicht // nur von den Dateigrößen — vorher konnte die Leiste viel „frei" zeigen, während der // Speicher praktisch voll war (Review 15.07.). Die Differenz wird als eigenes // Betriebs-Segment (KV-Cache/Overhead) sichtbar statt verschwiegen. const overheadBytes = Math.max(0, realUsedBytes - weights) const freeBytes = Math.max(0, capacity - Math.max(weights, realUsedBytes)) const freePct = (freeBytes / capacity) * 100 const overheadPct = (overheadBytes / capacity) * 100 return (
Modell-Speicher (GPU-Pool)
{gb(capacity)} GB gesamt
{gb(weights)} GB Modell-Gewichte
{overheadBytes > 0 &&
{gb(overheadBytes)} GB KV-Cache & Betrieb
}
{gb(freeBytes)} GB frei
{/* Der Balken */}
{running.length === 0 ? (
Kein Modell geladen — Auto-Swap holt bei Anfrage automatisch das passende.
) : ( <> {running.map((m) => { const pct = ((m.size_bytes || 0) / capacity) * 100 const c = roleBarColor(m.role) const isSel = selected === m.name return ( ) })} {overheadPct > 1.5 && (
KV
)} {freePct > 1.5 && (
frei
)} )}
{/* Legende */} {running.length > 0 && (
{running.map((m) => { const c = roleBarColor(m.role) return ( ) })} {overheadBytes > 0 && ( KV-Cache & Betrieb · {gb(overheadBytes)} GB )} frei · {gb(freeBytes)} GB
)}
) }