Feat: Engine-Update-Mechanismus + Update-Checks (Hermes Agent/AnythingLLM) + WebUI->AnythingLLM
- Engine-Update: deploy/update-engine.sh laedt neuesten ggml-org Vulkan-Build + restart; MC_ENGINE_UPDATE_CMD verdrahtet, engine_update_job nutzt es (Script macht Restart selbst). - Update-Checks: Hermes Agent (NousResearch/hermes-agent, Release-Datum vs. installiertes Commit) + AnythingLLM (Mintplex-Labs/anything-llm, neueste Version + /api/ping-Health), 1h-Cache. Neues Feld /api/maintenance/updates.components; UpdatesCard zeigt beide Zeilen. - WebUI -> AnythingLLM: agent_status.webui_* zeigt jetzt auf ANYTHINGLLM_URL (192.168.178.155:3001, /api/ping); alle "Hermes WebUI"-Buttons/Labels (AgentView, AgentStatusCard, nav, GuideView, Services-Liste) -> "AnythingLLM". Lokaler hermes-webui-Dienst bleibt als Service-Control im SystemDrawer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,13 +15,17 @@ from datetime import datetime
|
||||
import httpx
|
||||
import psutil
|
||||
|
||||
from services import discover, jobengine, llamaswap
|
||||
from config import ANYTHINGLLM_REPO, ANYTHINGLLM_URL, HERMES_AGENT_REPO
|
||||
from services import discover, jobengine, llamaswap, system
|
||||
|
||||
# System-Dienste (root, via sudo -n NOPASSWD) vs. User-Dienste (systemctl --user).
|
||||
SYSTEM_SERVICES = {"llama-swap"}
|
||||
USER_SERVICES = {"mission-control-2", "hermes-gateway", "hermes-dashboard", "hermes-webui"}
|
||||
|
||||
ENGINE_UPDATE_CMD = os.environ.get("MC_ENGINE_UPDATE_CMD", "")
|
||||
# Engine-Update: lädt den neuesten Vulkan-Build (deploy/update-engine.sh, läuft als root).
|
||||
_REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
ENGINE_UPDATE_CMD = os.environ.get(
|
||||
"MC_ENGINE_UPDATE_CMD", f"sudo bash {_REPO_ROOT}/deploy/update-engine.sh")
|
||||
# Engine = offizieller Vulkan-Build von ggml-org/llama.cpp (RADV auf Strix Halo).
|
||||
ENGINE_PATH = os.environ.get("MC_ENGINE_PATH", "/opt/llamacpp-vulkan")
|
||||
ENGINE_REPO = os.environ.get("MC_ENGINE_REPO", "ggml-org/llama.cpp")
|
||||
@@ -82,6 +86,74 @@ def _engine_update_available() -> bool:
|
||||
return avail
|
||||
|
||||
|
||||
_comp_cache = {"ts": 0.0, "data": []}
|
||||
|
||||
|
||||
def _gh_latest(repo: str) -> dict | None:
|
||||
"""Neuestes GitHub-Release {tag, published} oder None."""
|
||||
try:
|
||||
rel = httpx.get(f"https://api.github.com/repos/{repo}/releases/latest",
|
||||
timeout=6, headers={"User-Agent": "MissionControl2"}).json()
|
||||
return {"tag": str(rel.get("tag_name", "")), "published": rel.get("published_at")}
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _commit_ts(path: str) -> int | None:
|
||||
try:
|
||||
p = subprocess.run(["git", "-C", os.path.expanduser(path), "log", "-1", "--format=%ct"],
|
||||
capture_output=True, text=True, timeout=8)
|
||||
return int(p.stdout.strip()) if p.returncode == 0 and p.stdout.strip() else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _hermes_agent_update() -> dict:
|
||||
"""Hermes-Agent: neuestes Release vs. installiertes git-Commit-Datum."""
|
||||
info = {"key": "hermes_agent", "name": "Hermes Agent", "current": None,
|
||||
"latest": None, "update": False, "reachable": None}
|
||||
git = system.find_hermes_agent_git()
|
||||
if git:
|
||||
info["current"] = git.get("hash")
|
||||
gh = _gh_latest(HERMES_AGENT_REPO)
|
||||
if gh:
|
||||
info["latest"] = gh["tag"]
|
||||
ts = _commit_ts(git["path"]) if (git and git.get("path")) else None
|
||||
if gh["published"] and ts:
|
||||
try:
|
||||
pub = datetime.fromisoformat(gh["published"].replace("Z", "+00:00")).timestamp()
|
||||
info["update"] = pub > ts + 86400 # Release > installiertes Commit (+1 Tag Toleranz)
|
||||
except Exception:
|
||||
pass
|
||||
return info
|
||||
|
||||
|
||||
def _anythingllm_update() -> dict:
|
||||
"""AnythingLLM: erreichbar? + neueste verfügbare Version. Installierte Version ist
|
||||
remote nicht unauth. abfragbar → update=None (nur Info: neueste verfügbar)."""
|
||||
reachable = False
|
||||
try:
|
||||
reachable = httpx.get(f"{ANYTHINGLLM_URL}/api/ping", timeout=4).status_code == 200
|
||||
except Exception:
|
||||
reachable = False
|
||||
info = {"key": "anythingllm", "name": "AnythingLLM", "current": None,
|
||||
"latest": None, "update": None, "reachable": reachable}
|
||||
gh = _gh_latest(ANYTHINGLLM_REPO)
|
||||
if gh:
|
||||
info["latest"] = gh["tag"]
|
||||
return info
|
||||
|
||||
|
||||
def _components_cached() -> list[dict]:
|
||||
"""Update-Status von Hermes-Agent + AnythingLLM (1h-Cache → GitHub schonen)."""
|
||||
now = time.time()
|
||||
if now - _comp_cache["ts"] < 3600 and _comp_cache["data"]:
|
||||
return _comp_cache["data"]
|
||||
data = [_hermes_agent_update(), _anythingllm_update()]
|
||||
_comp_cache.update(ts=now, data=data)
|
||||
return data
|
||||
|
||||
|
||||
def model_upgrades() -> list[dict]:
|
||||
"""Dynamisch: je discover-Kategorie das empfohlene Modell, das NOCH NICHT installiert ist,
|
||||
aber NUR wenn für diese Rolle bereits irgendein Modell konfiguriert ist.
|
||||
@@ -135,7 +207,8 @@ def _last_apt_update() -> float | None:
|
||||
def updates() -> dict:
|
||||
ups = model_upgrades()
|
||||
return {"os": _os_upgradable(), "engine": 1 if _engine_update_available() else 0,
|
||||
"models": len(ups), "model_list": ups, "last_check": _last_apt_update()}
|
||||
"models": len(ups), "model_list": ups, "last_check": _last_apt_update(),
|
||||
"components": _components_cached()}
|
||||
|
||||
|
||||
def _run(cmd: list[str], sudo_password: str | None = None) -> dict:
|
||||
@@ -233,8 +306,9 @@ def engine_update_job(sudo_password: str | None = None) -> dict | None:
|
||||
return None
|
||||
if err := check_sudo_needs_password(sudo_password):
|
||||
return err
|
||||
cmd = f"{ENGINE_UPDATE_CMD} && sudo systemctl restart llama-swap"
|
||||
job_id = jobengine.start_job(["bash", "-c", cmd], "Engine-Update (llama.cpp)", sudo_password=sudo_password)
|
||||
# update-engine.sh läuft via sudo als root und startet llama-swap am Ende selbst neu.
|
||||
job_id = jobengine.start_job(["bash", "-c", ENGINE_UPDATE_CMD],
|
||||
"Engine-Update (llama.cpp Vulkan)", sudo_password=sudo_password)
|
||||
return {"ok": True, "job_id": job_id}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user