Feat: Agent-Hirn bleibt warm (Re-Warm-Waechter) + Updates-Karte konsolidiert

Punkt 1 - Hirn warm: brains-Gruppe wird bei on-demand-Last ausserhalb der Gruppe
verdraengt; persist verhindert nur Idle-Unload, nicht Gruppen-Swap -> Hirn blieb bis zum
naechsten llama-swap-Neustart kalt. Neu: services/warmer.py als Hintergrund-Task (FastAPI
lifespan) prueft periodisch llama-swap /running; ist die Box idle, pingt es das Hirn
(Rolle hermes) vor. Waehrend aktiver Last (irgendwas geladen) haelt es sich raus.
Justierbar via MC_REWARM_* (ENABLED/INTERVAL/MODEL). Kein sudo, im Repo, deployt normal.

Punkt 2 - Updates-Doppelung: Aktionen gab es auf der Karte UND im Pflege-Drawer.
UpdatesCard zeigt jetzt nur noch die Status-Ampel + 'Updates verwalten & Pflege'-Button
(oeffnet den Drawer). Alle Aktionen (OS/Engine/Reboot/Modell-Upgrade) leben im Drawer mit
Job-Fortschritt -> keine Dublette, kuerzere Karte, Sudo-Modal raus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-27 17:12:49 +02:00
parent 89cdc60b6e
commit 694aff9801
7 changed files with 235 additions and 426 deletions
+18 -1
View File
@@ -6,8 +6,10 @@ setzt eine no-cache-Middleware. Im Dev läuft das Frontend über den Vite-Dev-
Server (proxyt /api hierher), daher CORS für localhost offen.
"""
import asyncio
import logging
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
@@ -17,6 +19,7 @@ from starlette.requests import Request
from config import FRONTEND_DIST, VERSION
from routers import agent, connect, gateway_proxy, health, maintenance, memory, models, routing, system
from services import warmer
# Zentrales Logging — Level via MC_LOG_LEVEL (INFO default). Eine Konfiguration
# für alle Module (logging.getLogger(__name__)).
@@ -24,8 +27,22 @@ logging.basicConfig(
level=os.environ.get("MC_LOG_LEVEL", "INFO").upper(),
format="%(asctime)s %(levelname)-7s %(name)s: %(message)s",
)
log = logging.getLogger(__name__)
app = FastAPI(title="Mission Control 2.0", version=VERSION)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Hintergrund-Tasks an den App-Lebenszyklus binden: Re-Warm-Wächter fürs Agent-Hirn."""
task = asyncio.create_task(warmer.rewarm_loop()) if warmer.ENABLED else None
if task:
log.info("Hirn-Re-Warm-Wächter aktiv (Intervall %ss, Modell '%s')", warmer.INTERVAL, warmer.MODEL)
try:
yield
finally:
if task:
task.cancel()
app = FastAPI(title="Mission Control 2.0", version=VERSION, lifespan=lifespan)
# Dev: Vite-Dev-Server (5173) ruft das Backend per /api auf.
app.add_middleware(