dienste: eigene Seite /box/dienste mit Tabelle wie die Geräte im Homelab
Die Schublade „Dienste und Protokolle" war eine schlichte Liste (Punkt, Name, „läuft", zwei Knöpfe). Jetzt eine Seite im Bereich KI-Box, gebaut wie die Geräte-Tabelle im Homelab-Cockpit: je Dienst Zustand in Worten (läuft, schläft, gestoppt, gescheitert, antwortet nicht), CPU, Speicher, „Läuft seit" und Neustarts; Probleme oben. „Protokoll" klappt unter der Zeile auf (das Neueste unten im Blick), Neu starten / Starten / Wecken fragen vorher nach. Entscheidungen: - Seite statt Schublade: die Tabelle braucht die Breite, und der Eintrag steht jetzt wie die anderen Seiten in der Seitenleiste. Schublade samt onDienste-Weg entfernt. - Speicher = MemoryCurrent als Zahl mit Einheit; der Balken zeigt den echten Anteil am Arbeitsspeicher der Box, keine erfundenen Prozente. Bei der Engine zählt der Grafikspeicher (GTT) mit — dort liegen die Modelle, und systemd rechnet ihn keinem Dienst zu (25.09.: 4,3 GB laut systemd, 27,6 GB Modelle). - CPU = Anteil an allen Kernen aus CPUUsageNSec zwischen zwei Abfragen (die Seite fragt alle 10 s). Fehlt eine brauchbare Probe, misst die Box einmal 0,5 s nach. Ruhiges Blau ohne Warnfarben: ein Dienst hat keine Grenze, was zählt, zeigt der Zustand. - Neustarts = NRestarts (automatische Neustarts nach Absturz), am Handy nur, wenn es welche gab; mehr als 0 rückt den Dienst nach oben. - Backend: Liste und Kennzahlen in services/dienste.py (ein systemctl show je Bereich, auf Windows harmlos leer), Router dünn. Die Felder der MCP-Werkzeuge bleiben; waechter.dienst_zustand war nur für die alte Liste da und ist weg. - Tabelle ab xl, Knöpfe ab 1400 px nebeneinander, darunter Karten mit zwei bzw. vier Spalten; bis 320 px keine waagrechte Scrollleiste. Tests: backend/tests/test_dienste.py (echte systemctl-show-Ausgabe der Box), frontend lib/dienste.test.ts und views/Dienste.test.tsx. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0df593c05a
commit
cf01a5bb85
@@ -6,18 +6,12 @@ zurückgegeben statt zu crashen.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import httpx
|
||||
from config import GATEWAY_URL, HERMES_API_URL, LLAMA_SWAP_URL, V1_UPSTREAM, VOICE_SERVICE_URL
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from services import backup as backup_svc
|
||||
from services import maintenance, waechter
|
||||
from services.agent import agent_status
|
||||
from services.gateway import gateway_reachable
|
||||
from services.llamaswap import engine_reachable, list_models
|
||||
from services import dienste, maintenance
|
||||
from services.llamaswap import list_models
|
||||
from services.pricing import compute_savings
|
||||
from services.system import system_status
|
||||
from services.token_stats import get_stats
|
||||
@@ -31,76 +25,12 @@ def status() -> dict:
|
||||
return system_status()
|
||||
|
||||
|
||||
def _voice_reachable() -> bool:
|
||||
try:
|
||||
return httpx.get(f"{VOICE_SERVICE_URL}/health", timeout=2).status_code == 200
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _user_unit_active(unit: str) -> bool:
|
||||
"""systemd-User-Unit ohne eigenen HTTP-Port (z. B. mc2-steward) — Zustand via is-active.
|
||||
Auf Windows/Dev schlägt das harmlos fehl (False)."""
|
||||
if os.name != "posix":
|
||||
return False
|
||||
try:
|
||||
r = subprocess.run(["systemctl", "--user", "is-active", unit],
|
||||
capture_output=True, text=True, timeout=3)
|
||||
return r.stdout.strip() == "active"
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
@router.get("/system/services")
|
||||
def services() -> dict:
|
||||
"""Aggregierte Erreichbarkeit aller Stack-Dienste — die EINE Quelle für Cockpit-Kachel,
|
||||
Schubladen-Liste und Log-Auswahl (Frontend rendert diese Liste, keine UI-Kopie mehr).
|
||||
`scope`: user|system (Restart-Weg), `unit`: echter systemd-Name (Restart/Logs/Matching)."""
|
||||
a = agent_status()
|
||||
gw_url = f"{GATEWAY_URL}/v1"
|
||||
rows = [
|
||||
{"name": "Engine (llama-swap)", "unit": "llama-swap", "url": LLAMA_SWAP_URL,
|
||||
"ok": engine_reachable(), "scope": "system"},
|
||||
]
|
||||
# Seit UMBAU v3 P1 ist der LLM-Gateway ein EIGENER Prozess — ohne diese Zeile war er
|
||||
# in der Dienste-Ampel unsichtbar (Kachel "6/6 grün" bei totem Denkpfad).
|
||||
if V1_UPSTREAM:
|
||||
rows.append({"name": "LLM-Gateway (Denkpfad)", "unit": "mc2-gateway", "url": V1_UPSTREAM,
|
||||
"ok": gateway_reachable(), "scope": "user"})
|
||||
# MC2 selbst antwortet gerade auf diesen Request — ok=True ist hier ehrlich;
|
||||
# die Zeile existiert für Restart/Logs, nicht als Erreichbarkeits-Orakel.
|
||||
rows.append({"name": "Box-Wart (MC2)", "unit": "mission-control-2", "url": gw_url,
|
||||
"ok": True, "scope": "user"})
|
||||
else:
|
||||
rows.append({"name": "Gateway (integriert)", "unit": "mission-control-2", "url": gw_url,
|
||||
"ok": gateway_reachable(), "scope": "user"})
|
||||
rows += [
|
||||
{"name": "Wächter (Steward)", "unit": "mc2-steward", "url": "",
|
||||
"ok": _user_unit_active("mc2-steward"), "scope": "user"},
|
||||
{"name": "Hermes-Gateway (Agent & Gedächtnis)", "unit": "hermes-gateway", "url": HERMES_API_URL,
|
||||
"ok": a["gateway_reachable"], "scope": "user"},
|
||||
{"name": "Hermes-Dashboard", "unit": "hermes-builtin-ui", "url": a["hermes_ui_url"],
|
||||
"ok": a["hermes_ui_reachable"], "scope": "user"},
|
||||
{"name": "Spracherkennung (Desktop-Lucy)", "unit": "voice-service", "url": VOICE_SERVICE_URL,
|
||||
"ok": _voice_reachable(), "scope": "user"},
|
||||
# Lucys Stimme (pocket-tts) spricht Telegram und Lucy-Desktop — bis 09/2026 fehlte
|
||||
# sie hier; die Box-Konsole ist mit dem Box-Wart-Umbau aus MC2 raus.
|
||||
{"name": "Lucys Stimme", "unit": "lucy-stimme", "url": "",
|
||||
"ok": _user_unit_active("lucy-stimme"), "scope": "user"},
|
||||
]
|
||||
# Bewusst abgeschaltete Dienste (24.09.2026: Spracherkennung bis zur Android-App) sind kein
|
||||
# Fehler — die Oberfläche zeigt sie als „schläft“ mit Knopf zum Wecken statt rot.
|
||||
for row in rows:
|
||||
row["schlaeft"] = (not row["ok"] and row["scope"] == "user"
|
||||
and waechter.schlaeft(waechter.dienst_zustand(row["unit"])))
|
||||
return {
|
||||
"services": rows,
|
||||
"links": {
|
||||
"engine_ui": f"{LLAMA_SWAP_URL}/ui",
|
||||
"gateway": gw_url,
|
||||
"hermes_ui": a["hermes_ui_url"],
|
||||
},
|
||||
}
|
||||
"""Alle Dienste der Box mit Erreichbarkeit, Zustand und Kennzahlen (Speicher, CPU, Laufzeit,
|
||||
Neustarts) — die EINE Quelle für die Seite „Dienste und Protokolle“ und die MCP-Werkzeuge.
|
||||
Logik in services/dienste.py; `scope`: user|system (Restart-Weg), `unit`: echter systemd-Name."""
|
||||
return dienste.liste()
|
||||
|
||||
|
||||
@router.post("/system/backup")
|
||||
|
||||
Reference in New Issue
Block a user