modelle: „Wer nutzt die Modelle“ neu — Kennzahlen, Tabelle je Absender, lesbare Säulendiagramme

Der User: „Viel zu dünn. Und den Graphen kann ja kein Mensch lesen.“ Vorher drei Balken und ein winziges
Stunden-Histogramm ohne lesbare Achsen, halb so breit neben dem Radar.

- Eigene Ansicht views/ModellNutzung.tsx in voller Breite (Radar darunter, ebenfalls volle Breite).
- Kennzahlen: Anfragen mit Schnitt je vollem Tag, heute/gestern, typische Antwort mit „9 von 10 bis“, Fehler
  (ab 1 % bernstein, ab 5 % rot, immer mit Wort dazu, nie nur Farbe).
- Tabelle je Absender: Anfragen mit Anteil-Balken, Antwortzeiten, Rechenzeit mit Anteil (NerdQuiz stellt
  92 % der Anfragen, aber nur 75 % der Rechenzeit), Fehler in Worten („Modell nicht erreichbar (502)“),
  letzte Anfrage. Schmal als Karten mit Beschriftung.
- Neuer Baustein components/diagramme/Saeulen.tsx für „je Tag (7 Tage)“ und „je Stunde (24 h)“: gestapelt je
  Absender, glatte y-Achse, Wochentag/Datum bzw. Uhrzeit an der x-Achse (Stunden ausgedünnt auf 3 bzw. 6 h),
  Legende, Summen auf den Tagessäulen (bei den Stunden nur die höchste), Anzeige per Zeiger, Finger und
  Pfeiltasten, darunter „Werte als Tabelle“. Auf dem Handy steht die Anzeige unter dem Diagramm, sonst
  verdeckte sie das halbe Bild. Säulen höchstens 24 px, 2 px Luft zwischen den Stücken statt Rändern.
- Farben: neue Tokens --reihe-3 (Gelb) und --reihe-weitere (Grau; ab der vierten Farbe fasst „Weitere“
  zusammen). Mit dem Paletten-Prüfer gegen #15181b gemessen: jedes Paar auch bei Rot-Grün-Schwäche getrennt.
  Türkis fiel durch (neben Magenta bei Grünschwäche ΔE 1,6), Violett gleicht der KI-Box-Farbe.
  Die Farbe folgt dem Absender (feste Reihe der Box), nicht dem Rang.
- „Wann welches Modell geladen wurde“ eingeklappt darunter: Anzahl, zuletzt, davor — mit Rolle (Hirn, Coder,
  Bild-Zwilling) statt nur Dateiname.
- useBreite aus Verlauf.tsx nach lib/breite.ts, damit beide Diagramme ihn teilen.
- Ältere Stände der Box ohne die neuen Felder zeigen weiter Anzahlen und Verläufe, Zeiten als „–“ — die
  Homelab-Instanz fragt über den Partner und kann einen anderen Stand haben.

frontend/dist ist bewusst nicht gebaut (kommt beim Zusammenführen).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-25 13:26:45 +02:00
co-authored by Claude Opus 5.5
parent 7834de9592
commit dfe0971180
14 changed files with 1275 additions and 65 deletions
+69
View File
@@ -0,0 +1,69 @@
import type { Nutzung } from "@/lib/typen"
// Testdaten für „Wer nutzt die Modelle“: die echten Zahlen der Box vom 25.09.2026, 13:03 (aus dem Journal von
// llama-swap), bei den Stunden und Ladezeiten gekürzt.
const NQ = "NerdQuiz auf Arcane"
const LUCY = "Lucy und OpenChamber über MC2"
const PC = "NerdQuiz-Tests vom PC"
/** 24 Stunden ab gestern 14:00; NerdQuiz nachts viel, Lucy tagsüber etwas. */
function stunden(): NonNullable<Nutzung["stunden"]> {
const beginn = Date.parse("2026-09-24T14:00:00+02:00")
return Array.from({ length: 24 }, (_, i) => {
const d = new Date(beginn + i * 3_600_000)
const h = (14 + i) % 24
const je: Record<string, number> = {}
if (h === 3) je[NQ] = 139
else if (h === 4) je[NQ] = 81
else if (h !== 1 && h !== 2 && h !== 5) je[NQ] = 5
if (h === 10) je[LUCY] = 8
if (h === 11) je[LUCY] = 12
// ISO mit Versatz wie von der Box (im September gilt +02:00)
const iso = new Intl.DateTimeFormat("sv-SE", {
timeZone: "Europe/Berlin", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit",
second: "2-digit",
}).format(d).replace(" ", "T")
return { beginn: `${iso}+02:00`, je_absender: je }
})
}
export function nutzungBeispiel(): Nutzung {
return {
tage: 7,
seit: "2026-09-19T00:00:00+02:00",
stand: "2026-09-25T13:03:59+02:00",
absender: [
{ name: NQ, anfragen: 4032, reihe: 0, fehler: 1, fehler_codes: { "502": 1 }, dauer_typisch_s: 1.231,
dauer_p90_s: 3.305, dauer_summe_s: 6517.5, zuletzt: "2026-09-25T12:55:04+02:00" },
{ name: LUCY, anfragen: 325, reihe: 1, fehler: 0, fehler_codes: {}, dauer_typisch_s: 4.469,
dauer_p90_s: 15.422, dauer_summe_s: 2136.7, zuletzt: "2026-09-25T12:53:24+02:00" },
{ name: PC, anfragen: 7, reihe: 2, fehler: 0, fehler_codes: {}, dauer_typisch_s: 2.465,
dauer_p90_s: 23.433, dauer_summe_s: 37.1, zuletzt: "2026-09-21T17:46:47+02:00" },
],
gesamt: { anfragen: 4364, fehler: 1, fehler_codes: { "502": 1 }, dauer_typisch_s: 1.27, dauer_p90_s: 4.037,
dauer_summe_s: 8691.3 },
je_tag: {
"2026-09-19": { [NQ]: 535, [LUCY]: 15 },
"2026-09-20": { [PC]: 1, [NQ]: 589, [LUCY]: 37 },
"2026-09-21": { [NQ]: 520, [LUCY]: 14, [PC]: 6 },
"2026-09-22": { [NQ]: 659, [LUCY]: 15 },
"2026-09-23": { [NQ]: 678, [LUCY]: 77 },
"2026-09-24": { [NQ]: 657, [LUCY]: 117 },
"2026-09-25": { [NQ]: 394, [LUCY]: 50 },
},
stunden: stunden(),
letzte_24h: Array.from({ length: 24 }, (_, h) => ({ stunde: String(h).padStart(2, "0"), je_absender: {} })),
zuletzt_geladen: {
"Qwen3.6-35B-A3B": "2026-09-25T11:25:46+02:00",
"Qwen3.6-35B-A3B-Bild": "2026-09-25T00:35:29+02:00",
"Qwen3.8-27B": "2026-09-24T13:03:13+02:00",
},
ladevorgaenge: [
{ modell: "Qwen3.6-35B-A3B", anzahl: 14,
zeiten: ["2026-09-25T11:25:46+02:00", "2026-09-25T11:25:09+02:00", "2026-09-25T06:09:02+02:00", "2026-09-24T16:03:40+02:00"] },
{ modell: "Qwen3.6-35B-A3B-Bild", anzahl: 8, zeiten: ["2026-09-25T00:35:29+02:00", "2026-09-24T13:18:34+02:00"] },
{ modell: "Qwen3.8-27B", anzahl: 3, zeiten: ["2026-09-24T13:03:13+02:00"] },
],
}
}