fix(phase-a): Kontext-Cap, Download-State, Recipe-Edit, OS-Badge, Swap-Flash

A1: 32768-Kontext-Cap entfernt (install-recipe, install-model, register) →
    max_ctx_for() liefert nun bis zu 128k auf Strix Halo; behebt "context
    size exceeded" bei externen Tools.
A2: Download-State jetzt im Status-Endpoint sichtbar: Modelle zeigen
    "↓ Download X%" statt "bereit" während Job läuft (Backend + Frontend).
A3: PUT /api/cookbook/user-recipe/{id} + Edit-Button (✎) für eigene Setups.
    Download-Modal setzt Kontext-Input automatisch auf optimal.
A4: /api/updates liefert apt_cache_age_h; Badge zeigt Tooltip + ⚠ wenn >24h.
A5: Swap-Flash: Topbar-Text pulst kurz teal wenn Modell den State wechselt.
A6: LLM-Engine-Update fragt jetzt per confirmModal nach (Konsistenz).
A7: Event-Delegation statt per-render addEventListener in models.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-22 21:33:03 +02:00
parent 022c42dccb
commit 82d15d82db
10 changed files with 214 additions and 45 deletions
+32 -3
View File
@@ -216,6 +216,35 @@ def create_user_recipe(req: UserRecipeReq):
return {"ok": True, "id": rid}
@router.put("/user-recipe/{recipe_id}")
def update_user_recipe(recipe_id: str, req: UserRecipeReq):
"""Bestehendes eigenes Setup aktualisieren (Titel, Beschreibung, Modelle)."""
items = load_user_recipes()
idx = next((i for i, r in enumerate(items) if r.get("id") == recipe_id), None)
if idx is None:
raise HTTPException(404, "Eigenes Setup nicht gefunden.")
if not req.title.strip():
raise HTTPException(400, "Bitte einen Titel angeben.")
models = []
for m in req.models:
if not m.repo.strip():
continue
models.append({
"role": (m.role or "modell").strip(),
"name": (m.name or m.repo.split("/")[-1]).strip(),
"repo": m.repo.strip(),
"params_b": extract_params_b(m.repo),
"quant": (m.quant or "Q4_K_M").strip(),
"why": (m.why or "").strip(),
})
if not models:
raise HTTPException(400, "Mindestens ein Modell (Repo) angeben.")
items[idx] = {**items[idx], "title": req.title.strip(), "icon": (req.icon or "box").strip(),
"desc": req.desc.strip(), "models": models}
save_user_recipes(items)
return {"ok": True, "id": recipe_id}
@router.delete("/user-recipe/{recipe_id}")
def delete_user_recipe(recipe_id: str):
items = load_user_recipes()
@@ -251,8 +280,8 @@ def install_recipe(req: InstallRecipeReq):
JOBS[jid]["result_path"] = str(target / file)
attach_download_progress(jid, str(target), hf_file_size(m["repo"], file))
job_ids.append(jid)
# Eintrag jetzt schon schreiben — optimaler Kontext, aber gedeckelt fuer schnellen Erststart.
ctx = min(max_ctx_for(m["params_b"], m["quant"], ram_gb), 32768)
# Eintrag jetzt schon schreiben — optimaler Kontext für die Hardware.
ctx = max_ctx_for(m["params_b"], m["quant"], ram_gb)
path = str(target / file)
cmd = CMD_TEMPLATE.replace("{model}", path).replace("{ctx}", str(ctx))
# Schluessel = sprechender Modellname; Rolle (aus dem Rezept) als Alias.
@@ -490,7 +519,7 @@ def install_model(req: InstallModelReq):
JOBS[jid]["result_path"] = str(target / file)
attach_download_progress(jid, str(target), hf_file_size(req.repo, file))
cfg = read_config()
ctx = min(max_ctx_for(req.params_b, req.quant, ram_gb), 32768)
ctx = max_ctx_for(req.params_b, req.quant, ram_gb)
path = str(target / file)
cmd = CMD_TEMPLATE.replace("{model}", path).replace("{ctx}", str(ctx))
mid = model_id_from_path(path)