Warmhalte-Fix: llama-swap-Key heißt persistent, nicht persist

Live gefunden (Zed-Start warf Lucys Hirn raus): llama-swap ignoriert
unbekannte Group-Keys stillschweigend — der Verdrängungsschutz der
brains-Gruppe war seit jeher wirkungslos. set_group() schreibt jetzt
beide Keys (persistent für llama-swap, persist für MC2-API/UI),
budget.py rechnet die echte Ko-Residenz (on-demand reserviert das
Warm-Set statt 0), Warn-Texte beschreiben Überlauf statt Verdrängung.
Box-Config live gefixt + verifiziert: Coder und Qwen3.6 gleichzeitig ready.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-03 08:46:23 +02:00
parent dd3d66156b
commit a2091b4f5f
8 changed files with 38 additions and 24 deletions
+3 -2
View File
@@ -65,7 +65,7 @@ def hermes_brain_info() -> dict:
groups = llamaswap.list_groups()
persist = set()
for g in groups.values():
if isinstance(g, dict) and g.get("persist"):
if isinstance(g, dict) and (g.get("persist") or g.get("persistent")):
persist.update(g.get("members") or [])
cur_name = cur["name"] if cur else None
@@ -197,7 +197,8 @@ def set_agent_brain(model_id: str) -> dict:
if b and not b.get("fits", True):
warning = (f"Speicher-Warnung: Hirn (~{b.get('brain_gb')} GB) + größtes on-demand-"
f"Modell (~{b.get('largest_ondemand_gb')} GB) übersteigen das GTT-Budget "
f"(~{b.get('gtt_gb')} GB) — heavy/coder würden das Hirn verdrängen.")
f"(~{b.get('gtt_gb')} GB) — das Hirn ist persistent, heavy/coder laden "
f"DANEBEN: Überlauf droht (Lade-Crash/Swapping statt Verdrängung).")
except Exception:
log.debug("set_agent_brain: Budget-Check fehlgeschlagen", exc_info=True)
return {"ok": True, "old": old, "new": model_id, "warning": warning}
+19 -15
View File
@@ -1,11 +1,13 @@
"""
Speicher-Budget & SETUP-BEWUSSTE ctx-Vergabe — EINE Quelle der Wahrheit.
Modelliert die auf der Box VERIFIZIERTE Residenz-Realität (llama-swap, Ein-Gruppen-
Residenz, GTT ~124 GB):
• Das Agent-Hirn (Rolle `hermes`) ist IMMER resident.
• Weitere persist-Mitglieder (fast/vision) dürfen verdrängt werden, wenn ein großes
on-demand-Modell lädt.
Modelliert die auf der Box VERIFIZIERTE Residenz-Realität (llama-swap, GTT ~124 GB):
• Die `persistent`-Gruppe (brains = Hirn+embed+vision) bleibt IMMER resident —
seit dem Key-Fix 03.07.2026 greift der Schutz wirklich (vorher stand `persist`
in der Config, das llama-swap stillschweigend ignorierte; on-demand-Last
verdrängte damals die ganze Gruppe).
• Ein on-demand-Modell (heavy/coder/…) lädt NEBEN die brains-Gruppe und muss
deren Footprint mit einplanen.
Daraus folgt, wie viel Speicher NEBEN einem Zielmodell reserviert bleiben muss —
und damit der größte Kontext, der wirklich passt (nicht nur für das Modell allein).
@@ -77,9 +79,10 @@ def params_b_for(name: str) -> float:
def _coresident_members(groups: dict) -> set:
"""Modelle, die GLEICHZEITIG warm sind: Mitglieder aller `swap:false`-Gruppen
(Ko-Residenz, z.B. brains = Hirn+embed+vision). Ein Modell AUSSERHALB dieser Gruppen
ist on-demand und verdrängt beim Laden die GANZE Gruppe — llama-swap swappt Gruppen
(live verifiziert: heavy laden → brains-Gruppe komplett raus, heavy läuft allein)."""
(Ko-Residenz, z.B. brains = Hirn+embed+vision). Seit dem `persistent`-Fix (03.07.2026)
überlebt die Gruppe auch on-demand-Last: Coder/heavy laden DANEBEN, nicht an ihre
Stelle (live verifiziert: Coder + Qwen3.6 gleichzeitig `ready`). Die frühere
Beobachtung „heavy verdrängt die Gruppe" war der ignorierte `persist`-Key."""
out: set = set()
for g in (groups or {}).values():
if isinstance(g, dict) and g.get("swap") is False:
@@ -89,14 +92,14 @@ def _coresident_members(groups: dict) -> set:
def reserved_gb(role: str | None) -> dict:
"""Speicher, der NEBEN einem Zielmodell der gegebenen Rolle resident bleibt — gemäß der
VERIFIZIERTEN llama-swap-Gruppen-Swap-Semantik (NICHT der früheren Annahme „Hirn bleibt
immer"). Nur die ko-residente `swap:false`-Gruppe läuft gemeinsam; ein on-demand-Modell
verdrängt die Gruppe und läuft ALLEIN mit dem vollen GTT.
seit dem `persistent`-Fix (03.07.2026) geltenden Semantik: die ko-residente
`swap:false`-Gruppe (brains) bleibt IMMER geladen, on-demand-Modelle laden daneben.
- Modell IN der Ko-Residenz-Gruppe (Hirn/embed/vision): koexistiert mit den ÜBRIGEN
Gruppen-Mitgliedern → reserviert deren Summe.
- Modell AUSSERHALB (heavy/coder/coder-lite/fast/scout): läuft allein (Gruppe wird beim
Laden rausgeswappt) → reserviert NICHTS, darf den vollen GTT für Kontext nutzen.
- Modell AUSSERHALB (heavy/coder/coder-lite/scout): lädt NEBEN die Gruppe →
reserviert deren GESAMTE Summe (früher 0.0, weil der kaputte `persist`-Key die
Gruppe verdrängen ließ — diese Rechnung erlaubte zu große Kontexte).
"""
from services import llamaswap
models = llamaswap.list_models()
@@ -115,8 +118,9 @@ def reserved_gb(role: str | None) -> dict:
others = sum(footprint_gb(m) for m in models
if m["name"] in cores and m["name"] != holder_name)
return {"reserved_gb": others, "mode": "co-resident", "brain_gb": brain_gb}
# on-demand: verdrängt die Ko-Residenz-Gruppe → läuft allein, voller GTT für Kontext.
return {"reserved_gb": 0.0, "mode": "ondemand-alone", "brain_gb": brain_gb}
# on-demand: lädt neben die (persistente) Ko-Residenz-Gruppe → deren Summe reservieren.
warm = sum(footprint_gb(m) for m in models if m["name"] in cores)
return {"reserved_gb": warm, "mode": "ondemand-beside-warmset", "brain_gb": brain_gb}
def setup_aware_ctx(params_b: float, quant: str, role: str | None = None) -> dict:
+8 -2
View File
@@ -392,10 +392,16 @@ def set_spec_draft(model_id: str, draft_path: str | None) -> dict:
def set_group(group: str, members: list[str], swap: bool = False, persist: bool = False) -> None:
"""llama-swap-`groups`-Eintrag setzen. swap=False → alle Mitglieder dürfen
GLEICHZEITIG laufen (Ko-Residenz, keine Nachlade-Latenz). persist=True →
Mitglieder werden nie automatisch entladen."""
Mitglieder werden nie von anderen Gruppen verdrängt.
llama-swap kennt dafür AUSSCHLIESSLICH den Key `persistent` — `persist` wird von ihm
stillschweigend ignoriert (so verlor das Hirn seinen Verdrängungsschutz; live gefunden
03.07.2026: Coder-Last warf die brains-Gruppe raus). `persist` wird zusätzlich weiter
geschrieben, weil MC2-API/UI (routers/models.py, agent.py, Frontend) diesen Key lesen."""
cfg = read_config()
groups = cfg.setdefault("groups", {})
groups[group] = {"swap": swap, "persist": persist, "members": list(members)}
groups[group] = {"swap": swap, "persist": persist, "persistent": persist,
"members": list(members)}
write_config(cfg)