Ampel / ampel (push) Failing after 21s
Suche aus Merkliste (deploy/radar-watchlist.json) und Hugging-Face-Entdeckung; nur Kandidaten mit Bild-Projektor, die neben das Warm-Set passen (<= 115 GB inkl. KV-Cache). Nachtlauf mc2-radar.timer 00:30, Tests nur bis 02:30 (um 03:00 kommt NerdQuiz), hoechstens ein neuer Kandidat pro Woche, Notbremse 02:35, RuntimeMaxSec als letzte Sicherung. Pruefstand als Modul (deploy/bench/pruefstand.py): Tempo, Werkzeuge, Deutsch/JSON bzw. Programmieraufgaben und Bild-Probe gegen das heutige Modell der Rolle. Durchgefallene werden geloescht, Bestandene gemeldet und erst nach "Uebernehmen" getauscht. Beim Uebernehmen wandern die Zweitrollen mit (fast beim Hirn, heavy beim Coder), und das Warm-Set des Stewards wird selbst umgestellt statt als Handgriff zu bleiben. Modell-Code im Pruefstand darf keine Prozesse starten (RLIMIT_NPROC=0). Radar steht im Flugplan und unter Waechter-Aufsicht; deploy.sh spielt seine Units ein. Oberflaeche: Vergleichswerte je Kandidat, Rueckfrage vor Uebernehmen und Verwerfen, Status auf Deutsch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
4.5 KiB
TypeScript
103 lines
4.5 KiB
TypeScript
// Reine Anzeige-Logik der Cockpit-Seiten — getrennt von den Komponenten, damit sie ohne
|
||
// DOM testbar bleibt und Vites Hot-Reload die Komponenten-Dateien sauber tauschen kann.
|
||
|
||
import type { UpdateDetails } from "./abfragen"
|
||
import type { Modell, Start } from "./typen"
|
||
|
||
// --- Rundinstrument -----------------------------------------------------------------
|
||
// 240°-Bogen wie ein Zeigerinstrument, Mittelpunkt (100|95) in einer 200×150-Zeichnung.
|
||
const MX = 100
|
||
const MY = 95
|
||
const START = 210 // Grad, links unten
|
||
const SPANNE = 240
|
||
|
||
function punkt(grad: number, r: number): [number, number] {
|
||
const rad = (grad * Math.PI) / 180
|
||
return [MX + r * Math.cos(rad), MY - r * Math.sin(rad)]
|
||
}
|
||
|
||
const r2 = (n: number) => Math.round(n * 100) / 100
|
||
|
||
/** SVG-Pfad eines Bogens von Anteil `von` bis `bis` (0…1) auf Radius `r`. */
|
||
export function bogenPfad(von: number, bis: number, r: number): string {
|
||
const a = Math.max(0, Math.min(1, von))
|
||
const b = Math.max(a, Math.min(1, bis))
|
||
const [x0, y0] = punkt(START - SPANNE * a, r)
|
||
const [x1, y1] = punkt(START - SPANNE * b, r)
|
||
const gross = (b - a) * SPANNE > 180 ? 1 : 0
|
||
return `M ${r2(x0)} ${r2(y0)} A ${r} ${r} 0 ${gross} 1 ${r2(x1)} ${r2(y1)}`
|
||
}
|
||
|
||
/** Farbe der Temperatur: grün bis 70 °C, dann Bernstein, ab 85 °C rot. */
|
||
export function temperaturFarbe(grad: number | null | undefined) {
|
||
if (grad == null) return "var(--text-3)"
|
||
if (grad >= 85) return "var(--rot)"
|
||
if (grad >= 70) return "var(--bernstein)"
|
||
return "var(--gruen)"
|
||
}
|
||
|
||
// --- Hauptwarnleuchte ---------------------------------------------------------------
|
||
|
||
/** Was die Hauptwarnleuchte sagt. Der schweigende Wächter geht allem anderen vor. */
|
||
export function hauptleuchte(z: Start["zustand"]) {
|
||
if (!z.waechter_wach) {
|
||
return { art: "stumm" as const, oben: "Wächter", mitte: "SCHWEIGT", unten: "Seit über 5 Minuten kein Prüflauf" }
|
||
}
|
||
if (z.update_laeuft) {
|
||
return { art: "info" as const, oben: "Wartung", mitte: "UPDATE LÄUFT", unten: "Hinweise ruhen bis zum Ende" }
|
||
}
|
||
if (z.anzahl === 0) {
|
||
return { art: "ok" as const, oben: "Alles", mitte: "IN ORDNUNG", unten: "Keine offenen Hinweise" }
|
||
}
|
||
const mitte = `${z.anzahl} HINWEIS${z.anzahl === 1 ? "" : "E"}`
|
||
return z.stufe === "rot"
|
||
? { art: "rot" as const, oben: "Störung", mitte, unten: "Drücken zum Ansehen" }
|
||
: { art: "gelb" as const, oben: "Achtung", mitte, unten: "Drücken zum Ansehen" }
|
||
}
|
||
|
||
// --- Updates ------------------------------------------------------------------------
|
||
|
||
/** „Läuft“ und „Neu“ aus den Update-Details lesbar machen. */
|
||
export function versionen(id: string, d: UpdateDetails | undefined): { laeuft: string; neu: string | null } {
|
||
if (!d) return { laeuft: "…", neu: null }
|
||
if (id === "os") return { laeuft: "Ubuntu", neu: d.count ? `${d.count} Pakete` : null }
|
||
// Hermes kommt aus git: „Neu“ ist die Zahl der Änderungen seit dem eingespielten Stand.
|
||
if (id === "hermes") {
|
||
return {
|
||
laeuft: d.installed_version ? `v${d.installed_version}` : "–",
|
||
neu: d.behind ? `+${d.behind.toLocaleString("de-DE")} Änderungen` : null,
|
||
}
|
||
}
|
||
const pre = id === "engine" ? "b" : "v"
|
||
const laeuft = d.installed_build != null ? `${pre}${d.installed_build}` : "–"
|
||
const neuer = d.latest_build != null && d.installed_build != null && d.latest_build > d.installed_build
|
||
return { laeuft, neu: neuer ? `${pre}${d.latest_build}` : null }
|
||
}
|
||
|
||
// --- Modelle ------------------------------------------------------------------------
|
||
|
||
/** Rolle eines Modells aus seinen llama-swap-Aliasen. */
|
||
export function rolleVon(m: Modell): "hirn" | "coder" | null {
|
||
const a = m.aliases.map((x) => x.toLowerCase())
|
||
if (a.includes("hermes") || a.includes("fast")) return "hirn"
|
||
if (a.includes("coder")) return "coder"
|
||
return null
|
||
}
|
||
|
||
// --- Modell-Radar ---------------------------------------------------------------------
|
||
|
||
/** Status eines Radar-Kandidaten in Worten fürs Cockpit. */
|
||
export const RADAR_STATUS_TEXT: Record<string, string> = {
|
||
neu: "Neu", wartet: "Wartet", getestet: "Gemessen", bestanden: "Bestanden",
|
||
durchgefallen: "Durchgefallen", uebernommen: "Übernommen", verworfen: "Verworfen",
|
||
}
|
||
|
||
/** Messwert des Radar-Vergleichs lesbar machen (Zahlen deutsch, eine Nachkommastelle). */
|
||
export function messwert(wert: unknown, einheit = ""): string {
|
||
if (wert == null || wert === "") return "–"
|
||
const text = typeof wert === "number"
|
||
? wert.toLocaleString("de-DE", { maximumFractionDigits: wert < 10 ? 1 : 0 })
|
||
: String(wert)
|
||
return einheit ? `${text} ${einheit}` : text
|
||
}
|