Feat: Brain-Rolle (hermes) als erstklassige, dauer-warme Hirn-Rolle

- ROLE_IDS (llamaswap, sources) + UI-Rollenlisten (ModelBadges, Discover,
  ModelBrowse, Cockpit-Slot-Grid) um `hermes` erweitert: gemma erscheint als
  „Hirn", nicht mehr als scout.
- Neuer set_ttl-Helper; set_agent_brain erzwingt ttl:0 (neu + idempotent beim
  Re-Setzen) und liefert eine weiche Budget-Warnung (kein Hard-Block) bei OOM.
- brain_status/brain_model_name: zeigen das echte Hirn (Rolle hermes / Hermes
  model.default) statt hart `fast` (Bugfix Health-/Ready-Check).
- POST /api/models/{id}/role delegiert die Rolle `hermes` an den warm-bewussten
  Flow (Alias + brains-Gruppe + ttl 0 + Hermes-Config + Gateway-Restart).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-30 17:01:26 +02:00
parent 087eb2259d
commit dd99401f3e
11 changed files with 360 additions and 284 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -7,7 +7,7 @@
<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-B3e8J_PY.js"></script>
<script type="module" crossorigin src="/assets/index-CLiy0fO1.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DoqQd4Zi.css">
</head>
<body>
@@ -1,8 +1,9 @@
import { type Fit } from "@/lib/api"
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"]
// Die kanonischen Serving-Rollen (identisch zu Backend sources.py/llamaswap.ROLE_IDS).
// `hermes` = Lucys Agent-Hirn (warm + ko-resident); UI-Label „Hirn".
export const ROLES = ["fast", "heavy", "coder", "vision", "hermes", "scout"]
// Rolle → Farbton. EINE Quelle der Wahrheit für alle Rollen-Badges (Cockpit, ModelsCard,
// …). `hermes` = Agent-Hirn (taucht als geladenes Modell auf).
+6 -3
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, roleTone } from "@/components/models/ModelBadges"
import { getBrandInfo, roleTone, ROLES } from "@/components/models/ModelBadges"
import { SpecDraftModal } from "@/components/models/SpecDraftModal"
import { LaneEditor } from "@/components/models/LaneEditor"
@@ -107,11 +107,14 @@ export function Cockpit() {
async function handleRoleChange(role: string, modelName: string) {
try {
await api(`/api/models/${encodeURIComponent(modelName)}/role`, {
// Bei der Hirn-Rolle (hermes) läuft serverseitig der warm-bewusste Flow (Alias + brains-
// Gruppe + ttl 0 + Hermes-Config + Gateway-Restart) und liefert ggf. eine Budget-Warnung.
const res = await api<{ warning?: string | null }>(`/api/models/${encodeURIComponent(modelName)}/role`, {
method: "POST",
body: JSON.stringify({ role: role || null }),
})
reload()
if (res?.warning) showAlert("Hirn gesetzt — Hinweis", res.warning)
} catch (e: any) {
showAlert("Fehler", `Fehler beim Zuweisen der Rolle: ${e.message || e}`)
}
@@ -311,7 +314,7 @@ export function Cockpit() {
Gateway Steckplatz-Belegung (Slot-Zuweisung)
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3">
{["fast", "heavy", "coder", "vision", "scout"].map((role) => {
{ROLES.map((role) => {
const m = models.find((x) => x.role === role)
const isWarm = m ? running.includes(m.name) : false
+7 -2
View File
@@ -1,5 +1,5 @@
import { useState } from "react"
import { Download, Star, Layers, Check, Zap, Eye, Code, Brain, Compass, ChevronDown, ChevronUp } from "lucide-react"
import { Download, Star, Layers, Check, Zap, Eye, Code, Brain, BrainCircuit, Compass, ChevronDown, ChevronUp } from "lucide-react"
import { api } from "@/lib/api"
import { useModels, useUpdates, useDiscover } from "@/lib/queries"
import { cn } from "@/lib/utils"
@@ -7,7 +7,7 @@ import { fmtBytes } from "@/lib/format"
import { FitBadge } from "@/components/models/ModelBadges"
import { ModelBrowse } from "./ModelBrowse"
// Die 5 kanonischen Rollen (identisch zu sources.py / ModelBadges.ROLES).
// Die kanonischen Rollen (identisch zu sources.py / ModelBadges.ROLES).
const ROLE_METADATA: Record<string, { title: string; desc: string; icon: any }> = {
fast: {
title: "Schnelles Alltags-Hirn",
@@ -29,6 +29,11 @@ const ROLE_METADATA: Record<string, { title: string; desc: string; icon: any }>
desc: "Verarbeitet Bilder, Screenshots und visuelle Diagramme in multimodalen Chats.",
icon: Eye
},
hermes: {
title: "Lucys Hirn (Agent)",
desc: "Lucys dauer-warmes Agent-Hirn mit Tool-Calling — bleibt ko-resident, lädt nie kalt nach.",
icon: BrainCircuit
},
scout: {
title: "Multimodal-Allrounder",
desc: "Multimodaler Allrounder für schnelle Antworten, Übersetzungen und Alltag.",
+1 -1
View File
@@ -13,7 +13,7 @@ const CHIPS: { key: string; label: string; q: string }[] = [
{ key: "reasoning", label: "Reasoning", q: "reasoning" },
{ key: "small", label: "Klein (≤4B)", q: "3B" },
]
const ROLE_OPTS = ["fast", "heavy", "coder", "vision", "scout"]
const ROLE_OPTS = ["fast", "heavy", "coder", "vision", "hermes", "scout"]
const _nf = new Intl.NumberFormat(undefined, { notation: "compact", maximumFractionDigits: 1 })
function fmtN(n: number): string {