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:
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -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>
|
||||||
|
|||||||
@@ -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?",
|
||||||
|
|||||||
Reference in New Issue
Block a user