Feat: fit-aware Brain-Update (Speicher-Budget) + brains-Kontext gesenkt (Always-On-Footprint)
- Brain-Kontexte gesenkt (live config): hermes 128K->64K, fast/vision 128K->32K -> Always-Warm-Footprint 74GB->51GB, ~23GB frei; heavy/coder passen wieder daneben. - /api/agent/brain liefert jetzt `budget`: projiziert den Always-On-Footprint mit dem empfohlenen Brain gegen das GTT-Budget (aus amdgpu.gttsize) und prueft, ob das groesste on-demand-Modell daneben passt (fit.estimate_memory_gb). - Cockpit Agent-Hirn-Karte: Budget-Zeile (gruen/rot) + Warnung im Update-Confirm, wenn ein zu grosses Always-On-Brain das groesste on-demand-Modell verdraengen wuerde. Button wird rot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,19 @@ def _hermes_version(name: str) -> float | None:
|
||||
return float(m.group(1)) if m else None
|
||||
|
||||
|
||||
def _gtt_budget_gb() -> float:
|
||||
"""GPU-adressierbarer Speicher (GTT) in GB — die harte Obergrenze. Liest
|
||||
amdgpu.gttsize aus /proc/cmdline, sonst RAM minus OS-Reserve."""
|
||||
try:
|
||||
with open("/proc/cmdline") as f:
|
||||
m = re.search(r"amdgpu\.gttsize=(\d+)", f.read())
|
||||
if m:
|
||||
return round(int(m.group(1)) / 1024.0, 1)
|
||||
except Exception:
|
||||
pass
|
||||
return round(psutil.virtual_memory().total / (1024 ** 3) - 6.0, 1)
|
||||
|
||||
|
||||
def hermes_brain_info() -> dict:
|
||||
"""Aktuelles Agent-Hirn (hermes-Rolle) + bestes verfügbares NousResearch-Hermes-Modell,
|
||||
das auf diese Hardware passt. Für den Modell-Manager: Brain sichtbar + updatebar,
|
||||
@@ -75,7 +88,45 @@ def hermes_brain_info() -> dict:
|
||||
# gleiche Datei schon installiert? dann kein Update
|
||||
if current and best["repo"].split("/")[-1].lower() in (current["name"] or "").lower():
|
||||
update = False
|
||||
return {"current": current, "recommended": best, "update_available": update}
|
||||
|
||||
# Fit-Check: passt das EMPFOHLENE Brain als Always-On noch ins Budget, sodass das
|
||||
# größte on-demand-Modell daneben lädt? (Brain muss immer resident sein.)
|
||||
budget = None
|
||||
try:
|
||||
from services.fit import estimate_memory_gb
|
||||
groups = llamaswap.list_groups()
|
||||
persist = set()
|
||||
for g in groups.values():
|
||||
if isinstance(g, dict) and g.get("persist"):
|
||||
persist.update(g.get("members") or [])
|
||||
|
||||
def _foot(m: dict) -> float:
|
||||
caps = m.get("capabilities") or {}
|
||||
return estimate_memory_gb(float(caps.get("params_b") or 7.0),
|
||||
m.get("quant") or "Q4_K_M", int(m.get("ctx") or 32768))
|
||||
|
||||
cur_name = cur["name"] if cur else None
|
||||
brain_ctx = int((cur.get("ctx") if cur else None) or 32768)
|
||||
# Always-Warm = persist-Modelle, das Brain durch die Empfehlung ersetzt
|
||||
warm = sum(_foot(m) for m in models if m["name"] in persist and m["name"] != cur_name)
|
||||
if best:
|
||||
warm += estimate_memory_gb(float(best["params_b"]), "Q4_K_M", brain_ctx)
|
||||
elif cur:
|
||||
warm += _foot(cur)
|
||||
largest_od = max((_foot(m) for m in models if m["name"] not in persist), default=0.0)
|
||||
gtt = _gtt_budget_gb()
|
||||
free_after = gtt - warm
|
||||
budget = {
|
||||
"gtt_gb": gtt,
|
||||
"warm_projected_gb": round(warm, 1),
|
||||
"free_after_gb": round(free_after, 1),
|
||||
"largest_ondemand_gb": round(largest_od, 1),
|
||||
"fits": free_after >= largest_od,
|
||||
}
|
||||
except Exception:
|
||||
log.debug("hermes_brain_info: Budget-Berechnung fehlgeschlagen", exc_info=True)
|
||||
|
||||
return {"current": current, "recommended": best, "update_available": update, "budget": budget}
|
||||
|
||||
|
||||
def _reach(url: str, path: str = "") -> bool:
|
||||
|
||||
Reference in New Issue
Block a user