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() groups = llamaswap.list_groups()
persist = set() persist = set()
for g in groups.values(): 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 []) persist.update(g.get("members") or [])
cur_name = cur["name"] if cur else None 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): if b and not b.get("fits", True):
warning = (f"Speicher-Warnung: Hirn (~{b.get('brain_gb')} GB) + größtes on-demand-" 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"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: except Exception:
log.debug("set_agent_brain: Budget-Check fehlgeschlagen", exc_info=True) log.debug("set_agent_brain: Budget-Check fehlgeschlagen", exc_info=True)
return {"ok": True, "old": old, "new": model_id, "warning": warning} 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. Speicher-Budget & SETUP-BEWUSSTE ctx-Vergabe — EINE Quelle der Wahrheit.
Modelliert die auf der Box VERIFIZIERTE Residenz-Realität (llama-swap, Ein-Gruppen- Modelliert die auf der Box VERIFIZIERTE Residenz-Realität (llama-swap, GTT ~124 GB):
Residenz, GTT ~124 GB): • Die `persistent`-Gruppe (brains = Hirn+embed+vision) bleibt IMMER resident —
• Das Agent-Hirn (Rolle `hermes`) ist IMMER resident. seit dem Key-Fix 03.07.2026 greift der Schutz wirklich (vorher stand `persist`
• Weitere persist-Mitglieder (fast/vision) dürfen verdrängt werden, wenn ein großes in der Config, das llama-swap stillschweigend ignorierte; on-demand-Last
on-demand-Modell lädt. 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 — 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). 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: def _coresident_members(groups: dict) -> set:
"""Modelle, die GLEICHZEITIG warm sind: Mitglieder aller `swap:false`-Gruppen """Modelle, die GLEICHZEITIG warm sind: Mitglieder aller `swap:false`-Gruppen
(Ko-Residenz, z.B. brains = Hirn+embed+vision). Ein Modell AUSSERHALB dieser Gruppen (Ko-Residenz, z.B. brains = Hirn+embed+vision). Seit dem `persistent`-Fix (03.07.2026)
ist on-demand und verdrängt beim Laden die GANZE Gruppe — llama-swap swappt Gruppen überlebt die Gruppe auch on-demand-Last: Coder/heavy laden DANEBEN, nicht an ihre
(live verifiziert: heavy laden → brains-Gruppe komplett raus, heavy läuft allein).""" 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() out: set = set()
for g in (groups or {}).values(): for g in (groups or {}).values():
if isinstance(g, dict) and g.get("swap") is False: 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: def reserved_gb(role: str | None) -> dict:
"""Speicher, der NEBEN einem Zielmodell der gegebenen Rolle resident bleibt — gemäß der """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 seit dem `persistent`-Fix (03.07.2026) geltenden Semantik: die ko-residente
immer"). Nur die ko-residente `swap:false`-Gruppe läuft gemeinsam; ein on-demand-Modell `swap:false`-Gruppe (brains) bleibt IMMER geladen, on-demand-Modelle laden daneben.
verdrängt die Gruppe und läuft ALLEIN mit dem vollen GTT.
- Modell IN der Ko-Residenz-Gruppe (Hirn/embed/vision): koexistiert mit den ÜBRIGEN - Modell IN der Ko-Residenz-Gruppe (Hirn/embed/vision): koexistiert mit den ÜBRIGEN
Gruppen-Mitgliedern → reserviert deren Summe. Gruppen-Mitgliedern → reserviert deren Summe.
- Modell AUSSERHALB (heavy/coder/coder-lite/fast/scout): läuft allein (Gruppe wird beim - Modell AUSSERHALB (heavy/coder/coder-lite/scout): lädt NEBEN die Gruppe →
Laden rausgeswappt) → reserviert NICHTS, darf den vollen GTT für Kontext nutzen. 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 from services import llamaswap
models = llamaswap.list_models() models = llamaswap.list_models()
@@ -115,8 +118,9 @@ def reserved_gb(role: str | None) -> dict:
others = sum(footprint_gb(m) for m in models others = sum(footprint_gb(m) for m in models
if m["name"] in cores and m["name"] != holder_name) if m["name"] in cores and m["name"] != holder_name)
return {"reserved_gb": others, "mode": "co-resident", "brain_gb": brain_gb} 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. # on-demand: lädt neben die (persistente) Ko-Residenz-Gruppe → deren Summe reservieren.
return {"reserved_gb": 0.0, "mode": "ondemand-alone", "brain_gb": brain_gb} 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: 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: 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 """llama-swap-`groups`-Eintrag setzen. swap=False → alle Mitglieder dürfen
GLEICHZEITIG laufen (Ko-Residenz, keine Nachlade-Latenz). persist=True → 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() cfg = read_config()
groups = cfg.setdefault("groups", {}) 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) write_config(cfg)
+3
View File
@@ -103,7 +103,10 @@ models:
groups: groups:
brains: brains:
swap: false swap: false
# llama-swap versteht NUR `persistent` (Verdrängungsschutz); `persist` ist der
# MC2-interne Lese-Key (API/UI) und wird von llama-swap ignoriert. Beide pflegen!
persist: true persist: true
persistent: true
members: members:
- Qwen3-Embedding-0.6B - Qwen3-Embedding-0.6B
- Qwen3-VL-8B-Instruct - Qwen3-VL-8B-Instruct
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -7,7 +7,7 @@
<link rel="manifest" href="/manifest.webmanifest" /> <link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Mission Control 2.0</title> <title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-CxXfDlAe.js"></script> <script type="module" crossorigin src="/assets/index-BwrPCJGM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-_Rap01H_.css"> <link rel="stylesheet" crossorigin href="/assets/index-_Rap01H_.css">
</head> </head>
<body> <body>
+1 -1
View File
@@ -210,7 +210,7 @@ export function Cockpit() {
async function handleBrainUpdate(repo: string) { async function handleBrainUpdate(repo: string) {
const b = brain?.budget const b = brain?.budget
const warn = b && !b.fits const warn = b && !b.fits
? `\n\n⚠ Speicher-Warnung: Dieses Brain (~${b.brain_gb} GB) muss immer resident sein. Zusammen mit dem größten on-demand-Modell (~${b.largest_ondemand_gb} GB) sprengt das das Budget (${b.gtt_gb} GB) → heavy/coder würden das Brain verdrängen. Erwäge ein kleineres Brain oder weniger Kontext.` ? `\n\n⚠ Speicher-Warnung: Dieses Brain (~${b.brain_gb} GB) bleibt immer resident (persistent). Zusammen mit dem größten on-demand-Modell (~${b.largest_ondemand_gb} GB) sprengt das das Budget (${b.gtt_gb} GB) → beim Laden von heavy/coder droht ein Überlauf. Erwäge ein kleineres Brain oder weniger Kontext.`
: "" : ""
showConfirm( showConfirm(
"Agent-Hirn aktualisieren?", "Agent-Hirn aktualisieren?",