Fix: "Modelle finden" bevorzugt faehigste passende Modelle (kein Downgrade) + Rollen-Farben zentral

- discover.rank_runnable: rankt jetzt fit-level > params_b(desc) > downloads -> fuer die
  128GB-Box wird das faehigste passende (MoE-)Modell empfohlen statt kleiner Populaer-Modelle.
  Top-4-Anzeige nutzt dasselbe Ranking.
- maintenance.model_upgrades: schlaegt KEIN Downgrade mehr vor (rec.params_b >= installiert*0.95).
  Behebt: fast 35B-A3B -> 4B wurde faelschlich als Upgrade angeboten.
- Frontend: Rollen-Farb-Mapping zentralisiert in ModelBadges (ROLE_TONE/roleTone);
  Cockpit/RolesCard/ActiveModelsCard nutzen es statt eigener Duplikate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-27 02:35:21 +02:00
parent 38f0394166
commit 8bb4e11f31
11 changed files with 444 additions and 430 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="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-CrPS-3Fn.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CjZVMvhL.css">
<script type="module" crossorigin src="/assets/index-oijhNb41.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Cx3ZKkHL.css">
</head>
<body>
<div id="root"></div>
@@ -3,15 +3,7 @@ import { Activity, Zap, Moon } from "lucide-react"
import { useModels, useTokenStats } from "@/lib/queries"
import { fmtSize } from "@/lib/format"
import { cn } from "@/lib/utils"
const ROLE_TONE: Record<string, string> = {
fast: "bg-cyan-500/15 text-cyan-400 border-cyan-500/25",
heavy: "bg-amber-500/15 text-amber-400 border-amber-500/25",
coder: "bg-violet-500/15 text-violet-400 border-violet-500/25",
vision: "bg-pink-500/15 text-pink-400 border-pink-500/25",
scout: "bg-teal-500/15 text-teal-400 border-teal-500/25",
hermes: "bg-indigo-500/15 text-indigo-400 border-indigo-500/25",
}
import { roleTone } from "@/components/models/ModelBadges"
export function ActiveModelsCard() {
const { data } = useModels(2_000)
@@ -65,7 +57,7 @@ export function ActiveModelsCard() {
<div className="flex items-center gap-1.5">
{m.role && (
<span className={cn("text-[8px] font-semibold uppercase px-1.5 py-0.5 rounded font-mono border tracking-wider shrink-0",
ROLE_TONE[m.role] || "bg-slate-500/15 text-slate-300 border-slate-500/25")}>
roleTone(m.role))}>
{m.role}
</span>
)}
@@ -1,8 +1,7 @@
import { Layers } from "lucide-react"
import { useModels } from "@/lib/queries"
import { cn } from "@/lib/utils"
const ROLES = ["fast", "heavy", "coder", "vision", "scout"]
import { ROLES, roleTone } from "@/components/models/ModelBadges"
export function RolesCard() {
const { data } = useModels(3_000)
@@ -37,12 +36,7 @@ export function RolesCard() {
<div className="min-w-0 flex-1 mr-2">
<div className="flex items-center gap-2 min-w-0">
<span className={cn("text-[9px] font-semibold uppercase px-1.5 py-0.5 rounded font-mono border tracking-wider shrink-0",
role === "fast" ? "bg-cyan-500/15 text-cyan-400 border-cyan-500/25" :
role === "heavy" ? "bg-amber-500/15 text-amber-400 border-amber-500/25" :
role === "coder" ? "bg-violet-500/15 text-violet-400 border-violet-500/25" :
role === "vision" ? "bg-pink-500/15 text-pink-400 border-pink-500/25" :
"bg-teal-500/15 text-teal-400 border-teal-500/25"
)}>
roleTone(role))}>
{role}
</span>
@@ -4,6 +4,20 @@ import { cn } from "@/lib/utils"
// Die 5 kanonischen Serving-Rollen (identisch zu Backend sources.py/llamaswap.ROLE_IDS).
export const ROLES = ["fast", "heavy", "coder", "vision", "scout"]
// Rolle → Farbton. EINE Quelle der Wahrheit für alle Rollen-Badges (Cockpit, RolesCard,
// ActiveModelsCard, …). `hermes` = Agent-Hirn (taucht als geladenes Modell auf).
export const ROLE_TONE: Record<string, string> = {
fast: "bg-cyan-500/15 text-cyan-400 border-cyan-500/25",
heavy: "bg-amber-500/15 text-amber-400 border-amber-500/25",
coder: "bg-violet-500/15 text-violet-400 border-violet-500/25",
vision: "bg-pink-500/15 text-pink-400 border-pink-500/25",
scout: "bg-teal-500/15 text-teal-400 border-teal-500/25",
hermes: "bg-indigo-500/15 text-indigo-400 border-indigo-500/25",
}
export const roleTone = (role?: string | null) =>
(role && ROLE_TONE[role]) || "bg-slate-500/15 text-slate-300 border-slate-500/25"
export function FitBadge({ fit }: { fit: Fit }) {
const tone = {
perfect: "bg-emerald-500/15 text-emerald-400 border border-emerald-500/20",
+2 -7
View File
@@ -6,7 +6,7 @@ import { useDialog } from "@/lib/useDialog"
import { CapsChips } from "@/components/CapsChips"
import { cn } from "@/lib/utils"
import { fmtSize, fmtCtx } from "@/lib/format"
import { getBrandInfo, ROLES } from "@/components/models/ModelBadges"
import { getBrandInfo, ROLES, roleTone } from "@/components/models/ModelBadges"
import { SpecDraftModal } from "@/components/models/SpecDraftModal"
export function Cockpit() {
@@ -611,12 +611,7 @@ export function Cockpit() {
>
<div className="flex items-center justify-between">
<span className={cn("text-[9px] font-bold uppercase tracking-wider font-mono px-1.5 py-0.5 rounded border",
role === "fast" ? "bg-cyan-500/10 text-cyan-400 border-cyan-500/20" :
role === "heavy" ? "bg-amber-500/10 text-amber-400 border-amber-500/20" :
role === "coder" ? "bg-violet-500/10 text-violet-400 border-violet-500/20" :
role === "vision" ? "bg-pink-500/10 text-pink-400 border-pink-500/20" :
"bg-teal-500/10 text-teal-400 border-teal-500/20"
)}>{role}</span>
roleTone(role))}>{role}</span>
{isWarm && <span className="h-1.5 w-1.5 rounded-full bg-emerald-400 animate-pulse" />}
</div>