fix(ux): Idiotensicherheit — 8 Audit-Fixes
Behebung der drei kritischsten UX-Schwachstellen aus dem Produkt-Audit:
1. ConnectPanel: Steps 2-4 (Modellauswahl, Tool-Picker, Config-Snippet,
Vision) werden blockiert wenn noch kein Modell installiert ist.
Stattdessen klarer Hinweis "Erst im Cookbook ein Modell holen" mit
direktem CTA. Verhindert dass Anfänger ein Snippet mit Fake-"coder"
kopieren, bevor ein echtes Modell existiert.
2. OverviewPanel: preload() schluckte Fehler still (catch {}). Zeigt
jetzt einen verständlichen Toast wenn die Engine nicht erreichbar ist.
Schnellstart ist kontextsensitiv: ohne Modelle zeigt er Onboarding-
Schritte (Modell holen → Verbinden → Guide lesen) statt sinnloser
Aktionen wie "Speicher freigeben".
3. CookbookPanel Profi-Suche: doDownload() nutzte zwei sequenzielle
API-Calls (download + register) — wenn der zweite scheiterte, blieb
das System in einem inkonsistenten Halbzustand. Jetzt ein einziger
atomarer Aufruf an /api/cookbook/install-model (wie Recipe/Discover).
Backend-Erweiterung: InstallModelReq akzeptiert jetzt 'file' und
'ctx' um Nutzer-Auswahl direkt zu übergeben.
4. Cookbook Section 1 als "Hier starten" markiert ("1 · Anwendungsfall
wählen & installieren" mit erklärendem Sub-Text).
5. Post-Download-Toasts: Alle drei Install-Pfade sagen nun einheitlich
"Modell erscheint danach automatisch im Modelle-Tab" statt nur
"siehe Aktivität".
6. too_tight-Alternative: Bei Modellen/Setups die den Speicher übersteigen
wird jetzt eine konkrete Alternative (kleinere Datei / passendes Rezept)
verlinkt statt nur "Trotzdem installieren".
7. Duplikat extract_params_b aus cookbook.py entfernt — importiert jetzt
aus hw_math (Single Source of Truth).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+8
-16
@@ -14,7 +14,7 @@ import psutil
|
||||
from ruamel.yaml.scalarstring import LiteralScalarString
|
||||
|
||||
from auth import auth
|
||||
from hw_math import evaluate_fit, max_ctx_for
|
||||
from hw_math import evaluate_fit, max_ctx_for, extract_params_b
|
||||
from config import (MODELS_DIR, CMD_TEMPLATE, DEFAULT_TTL, HF_DOWNLOAD_ENV, USER_RECIPES_PATH,
|
||||
DISCOVER_CACHE_PATH, DISCOVER_TTL, hf_bin)
|
||||
from llamaswap import read_config, write_config, model_id_from_path, set_role_alias
|
||||
@@ -68,6 +68,8 @@ class InstallModelReq(BaseModel):
|
||||
role: str
|
||||
params_b: float
|
||||
quant: str = "Q4_K_M"
|
||||
file: str | None = None # konkrete GGUF-Datei; None → _pick_gguf wählt automatisch
|
||||
ctx: int | None = None # None → max_ctx_for (Hardware-Optimum)
|
||||
hf_token: str | None = None
|
||||
|
||||
|
||||
@@ -85,18 +87,6 @@ class UserRecipeReq(BaseModel):
|
||||
icon: str = "box"
|
||||
models: list[UserRecipeModelReq]
|
||||
|
||||
def extract_params_b(repo_id: str) -> float:
|
||||
"""Extrahiert die Parametergröße (in Milliarden) aus dem Repo-Namen."""
|
||||
# z.B. Qwen2.5-Coder-32B -> 32
|
||||
# 8x7B -> 56 (MoE)
|
||||
moe = re.search(r"(\d+)x(\d+(?:\.\d+)?)[bB]", repo_id)
|
||||
if moe:
|
||||
return float(moe.group(1)) * float(moe.group(2))
|
||||
m = re.search(r"(\d+(?:\.\d+)?)[bB](?![a-zA-Z])", repo_id)
|
||||
if m:
|
||||
return float(m.group(1))
|
||||
return 7.0 # Fallback
|
||||
|
||||
def extract_quant(filename: str) -> str:
|
||||
m = re.search(r"(Q\d_[A-Z0-9_]+|IQ\d_[A-Z0-9_]+|FP16|BF16)", filename, re.IGNORECASE)
|
||||
return m.group(1).upper() if m else "Q4_K_M"
|
||||
@@ -531,8 +521,10 @@ def upgrades():
|
||||
|
||||
@router.post("/install-model")
|
||||
def install_model(req: InstallModelReq):
|
||||
"""Ein einzelnes Modell installieren (Download + Einpflegen unter 'role', optimaler ctx)."""
|
||||
file = _pick_gguf(req.repo, req.quant)
|
||||
"""Ein einzelnes Modell installieren (Download + Einpflegen unter 'role', optimaler ctx).
|
||||
Wenn 'file' übergeben wird, wird genau diese GGUF-Datei geladen (Profi-Suche-Auswahl);
|
||||
andernfalls wählt _pick_gguf automatisch die beste Variante für den gewünschten Quant."""
|
||||
file = req.file or _pick_gguf(req.repo, req.quant)
|
||||
if not file:
|
||||
raise HTTPException(404, "Keine GGUF-Datei im Repo gefunden.")
|
||||
mmproj = _pick_mmproj(req.repo)
|
||||
@@ -550,7 +542,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 = max_ctx_for(req.params_b, req.quant, ram_gb)
|
||||
ctx = req.ctx if req.ctx else max_ctx_for(req.params_b, req.quant, ram_gb)
|
||||
path = str(target / file)
|
||||
cmd = CMD_TEMPLATE.replace("{model}", path).replace("{ctx}", str(ctx))
|
||||
if mmproj:
|
||||
|
||||
Reference in New Issue
Block a user