Feat: Gedächtnis-Tab überarbeitet — Sigma.js-Graph (skaliert), Liste als Default + gruppiert
Graph: reagraph (three.js, schwer, hing Renderer) → Sigma.js v3 + graphology (graph-optimiertes WebGL, skaliert auf tausende Knoten), im App-Look: Kategorie-Farben, Knotengröße nach Verknüpfungen, Hover-Highlight (Nachbarn hervor, Rest dimmt), Legende. Liste ist jetzt Default + nach Kategorie gruppierte Sektionen (statt flacher Wand). reagraph deinstalliert → leichteres Bundle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,7 @@ def updates() -> dict:
|
||||
|
||||
@router.get("/maintenance/update-details")
|
||||
def update_details(kind: str) -> dict:
|
||||
if kind not in ("os", "engine", "hermes"):
|
||||
if kind not in ("os", "engine", "swap", "hermes"):
|
||||
raise HTTPException(400, "Unbekannte Update-Art.")
|
||||
return maintenance.update_details(kind)
|
||||
|
||||
@@ -52,6 +52,14 @@ def engine_update(body: SudoReq) -> dict:
|
||||
return res
|
||||
|
||||
|
||||
@router.post("/maintenance/swap-update")
|
||||
def swap_update(body: SudoReq) -> dict:
|
||||
res = maintenance.swap_update_job(body.sudo_password)
|
||||
if not res:
|
||||
raise HTTPException(400, "Kein Router-Update-Befehl gesetzt (MC_SWAP_UPDATE_CMD).")
|
||||
return res
|
||||
|
||||
|
||||
@router.post("/maintenance/hermes-update")
|
||||
def hermes_update() -> dict:
|
||||
return maintenance.hermes_update_job()
|
||||
|
||||
@@ -30,6 +30,14 @@ ENGINE_PATH = os.environ.get("MC_ENGINE_PATH", "/opt/llamacpp-vulkan")
|
||||
ENGINE_REPO = os.environ.get("MC_ENGINE_REPO", "ggml-org/llama.cpp")
|
||||
_engine_cache = {"ts": 0.0, "avail": False}
|
||||
|
||||
# Router = llama-swap (mostlygeek): proxyt Anfragen und wechselt die Modelle heiß. Eigenes
|
||||
# Upstream-Projekt mit eigenem Release-Zyklus → getrennt von der Engine geführt.
|
||||
SWAP_UPDATE_CMD = os.environ.get(
|
||||
"MC_SWAP_UPDATE_CMD", f"sudo bash {_REPO_ROOT}/deploy/update-swap.sh")
|
||||
SWAP_BIN = os.environ.get("MC_SWAP_BIN", "/usr/local/bin/llama-swap")
|
||||
SWAP_REPO = os.environ.get("MC_SWAP_REPO", "mostlygeek/llama-swap")
|
||||
_swap_cache = {"ts": 0.0, "avail": False}
|
||||
|
||||
|
||||
def _installed_engine_build() -> int | None:
|
||||
"""Build-Nummer der installierten llama-server-Binary (z.B. 9821), oder None.
|
||||
@@ -88,6 +96,39 @@ def _engine_update_available() -> bool:
|
||||
return avail
|
||||
|
||||
|
||||
def _installed_swap_version() -> int | None:
|
||||
"""Versions-Nummer der installierten llama-swap-Binary (z.B. 228), oder None."""
|
||||
if not os.path.exists(SWAP_BIN):
|
||||
return None
|
||||
try:
|
||||
out = subprocess.run([SWAP_BIN, "--version"], capture_output=True, text=True, timeout=15)
|
||||
txt = (out.stdout or "") + (out.stderr or "")
|
||||
if (m := re.search(r"version:\s*(\d+)", txt)) or (m := re.search(r"\bv?(\d{2,})\b", txt)):
|
||||
return int(m.group(1))
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def _swap_update_available() -> bool:
|
||||
now = time.time()
|
||||
if now - _swap_cache["ts"] < 3600:
|
||||
return _swap_cache["avail"]
|
||||
avail = False
|
||||
try:
|
||||
rel = httpx.get(f"https://api.github.com/repos/{SWAP_REPO}/releases/latest",
|
||||
timeout=6, headers={"User-Agent": "MissionControl2"}).json()
|
||||
tag = str(rel.get("tag_name", ""))
|
||||
latest = int(m.group(1)) if (m := re.search(r"(\d{2,})", tag)) else None
|
||||
installed = _installed_swap_version()
|
||||
if latest is not None and installed is not None:
|
||||
avail = latest > installed
|
||||
except Exception:
|
||||
avail = False
|
||||
_swap_cache.update(ts=now, avail=avail)
|
||||
return avail
|
||||
|
||||
|
||||
_comp_cache = {"ts": 0.0, "data": []}
|
||||
|
||||
|
||||
@@ -244,6 +285,7 @@ def _last_apt_update() -> float | None:
|
||||
def updates() -> dict:
|
||||
ups = model_upgrades()
|
||||
return {"os": _os_upgradable(), "engine": 1 if _engine_update_available() else 0,
|
||||
"swap": 1 if _swap_update_available() else 0,
|
||||
"models": len(ups), "model_list": ups, "last_check": _last_apt_update(),
|
||||
"components": _components_cached()}
|
||||
|
||||
@@ -289,6 +331,26 @@ def engine_update_details() -> dict:
|
||||
return info
|
||||
|
||||
|
||||
def swap_update_details() -> dict:
|
||||
"""Installierte vs. neueste llama-swap-Version + Release-Name/-Notizen/-Link."""
|
||||
info: dict = {"kind": "swap", "installed_build": _installed_swap_version(),
|
||||
"latest_build": None, "latest_tag": None, "name": None,
|
||||
"url": None, "body": None}
|
||||
try:
|
||||
rel = httpx.get(f"https://api.github.com/repos/{SWAP_REPO}/releases/latest",
|
||||
timeout=8, headers={"User-Agent": "MissionControl2"}).json()
|
||||
tag = str(rel.get("tag_name", ""))
|
||||
info["latest_tag"] = tag
|
||||
info["latest_build"] = int(m.group(1)) if (m := re.search(r"(\d{2,})", tag)) else None
|
||||
info["name"] = rel.get("name") or tag
|
||||
info["url"] = rel.get("html_url")
|
||||
body = (rel.get("body") or "").strip()
|
||||
info["body"] = body[:2000] if body else None
|
||||
except Exception as exc: # noqa: BLE001
|
||||
info["error"] = str(exc)
|
||||
return info
|
||||
|
||||
|
||||
def hermes_update_details() -> dict:
|
||||
"""Commits, die ein Hermes-Update einspielen würde (HEAD..origin/<branch>)."""
|
||||
info: dict = {"kind": "hermes", "branch": None, "behind": 0, "commits": []}
|
||||
@@ -319,6 +381,7 @@ def hermes_update_details() -> dict:
|
||||
|
||||
def update_details(kind: str) -> dict:
|
||||
return {"os": os_update_details, "engine": engine_update_details,
|
||||
"swap": swap_update_details,
|
||||
"hermes": hermes_update_details}.get(kind, lambda: {"error": "unbekannt"})()
|
||||
|
||||
|
||||
@@ -429,6 +492,22 @@ def engine_update_job(sudo_password: str | None = None) -> dict | None:
|
||||
return {"ok": True, "job_id": job_id}
|
||||
|
||||
|
||||
def swap_update_job(sudo_password: str | None = None) -> dict | None:
|
||||
if not SWAP_UPDATE_CMD:
|
||||
return None
|
||||
if err := check_sudo_needs_password(sudo_password):
|
||||
return err
|
||||
|
||||
def on_done():
|
||||
_swap_cache.update(ts=0.0, avail=False) # Cache leeren → frischer Versions-Vergleich
|
||||
|
||||
# update-swap.sh läuft via sudo als root, ersetzt die Binary und startet llama-swap neu.
|
||||
job_id = jobengine.start_job(["bash", "-c", SWAP_UPDATE_CMD],
|
||||
"Router-Update (llama-swap)",
|
||||
on_done=on_done, sudo_password=sudo_password)
|
||||
return {"ok": True, "job_id": job_id}
|
||||
|
||||
|
||||
def hermes_update_job() -> dict:
|
||||
"""Hermes-Agent aktualisieren wie die CLI (`hermes update` = git pull + Deps), danach
|
||||
den Gateway neu starten. Davor ein Sicherheits-Backup (unser deploy/backup.sh). Kein sudo
|
||||
|
||||
Reference in New Issue
Block a user