diff --git a/recipes.py b/recipes.py index 6364426..8e9cb3f 100644 --- a/recipes.py +++ b/recipes.py @@ -7,6 +7,22 @@ aufgeloest (robust). Fit + optimaler Kontext kommen zur Laufzeit aus hw_math. """ RECIPES = [ + { + "id": "elite", "title": "Elite-Lineup (für deine APU)", "icon": "bolt", + "desc": "Das Stärkste, das auf deinem Strix Halo wirklich flott läuft: bevorzugt MoE-Modelle (viel Wissen, wenige aktive Parameter). Ein Spitzenmodell je Rolle — sie wechseln sich automatisch ab, es läuft immer nur eins.", + "models": [ + {"role": "coder", "name": "Qwen3-Coder 30B-A3B", "repo": "unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF", + "params_b": 30, "quant": "Q4_K_M", "why": "Bestes lokales Coder-Modell — MoE (nur ~3B aktiv) → 30B-Wissen bei hohem Tempo."}, + {"role": "manager", "name": "Qwen3 30B-A3B", "repo": "unsloth/Qwen3-30B-A3B-Instruct-2507-GGUF", + "params_b": 30, "quant": "Q4_K_M", "why": "Starker Allrounder & Orchestrator (MoE) — plant, delegiert, fasst zusammen."}, + {"role": "reviewer", "name": "Gemma 3 27B", "repo": "unsloth/gemma-3-27b-it-GGUF", + "params_b": 27, "quant": "Q4_K_M", "why": "Googles Modell mit feinem Sprachgefühl — ideal zum Prüfen & Gegenlesen von Code/Texten."}, + {"role": "vision", "name": "Qwen3-VL 8B", "repo": "Qwen/Qwen3-VL-8B-Instruct-GGUF", + "params_b": 8, "quant": "Q4_K_M", "why": "Liest Screenshots, Diagramme & Fehlerbilder — der Blick fürs Visuelle."}, + {"role": "scout", "name": "Qwen3 8B", "repo": "unsloth/Qwen3-8B-GGUF", + "params_b": 8, "quant": "Q4_K_M", "why": "Flinker Kundschafter für schnelle Fragen & einfache Aufgaben."}, + ], + }, { "id": "coding", "title": "Coden & Programmieren", "icon": "code", "desc": "Aktueller Top-Coder (MoE, nur 3B aktiv → schnell), ein flinker Helfer und Vision für Screenshots.", diff --git a/routers/cookbook.py b/routers/cookbook.py index 219c3e8..72f6662 100644 --- a/routers/cookbook.py +++ b/routers/cookbook.py @@ -433,7 +433,11 @@ def discover(force: bool = False): ms = [{**m, "installed": _model_installed(m["repo"], idx), "requirements": _model_requirements(m["role"], m.get("quant", "Q4_K_M"), m["fit"])} for m in c["models"]] - out.append({**c, "models": ms}) + # „Beste Wahl" je Kategorie: das lauffähige Modell mit dem besten Fit (perfect vor + # marginal), bei Gleichstand das meistgeladene. Zu große Modelle kommen nie in Frage. + ranked = sorted([m for m in ms if m["fit"]["level"] != "too_tight"], + key=lambda m: (_FIT_ORDER[m["fit"]["level"]], -int(m.get("downloads") or 0))) + out.append({**c, "models": ms, "recommended": ranked[0]["repo"] if ranked else None}) return {"updated": data["updated"], "categories": out, "sys_ram_gb": round(ram_gb, 1), "stale": not fresh} diff --git a/static/js/panels/cookbook.js b/static/js/panels/cookbook.js index ee5bc16..e72bd96 100644 --- a/static/js/panels/cookbook.js +++ b/static/js/panels/cookbook.js @@ -6,6 +6,38 @@ import { $, esc, icon, toast, infoDot, confirmModal } from "../core/ui.js"; const CTX_HELP = "Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr, braucht aber mehr Speicher und wird etwas langsamer."; const GGUF_HELP = "GGUF ist das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF-Datei kann sie nicht nutzen."; +const MOE_HELP = "MoE (Mixture of Experts): viele Parameter fürs Wissen, aber pro Wort ist nur ein kleiner Teil aktiv → fast große-Modell-Qualität bei hohem Tempo. Ideal für deine APU."; + +// Was kann das Modell? (Fähigkeit aus dem Namen / der Backend-Rolle ableiten.) +const ROLE_LABEL = { vision: "Bilder", coder: "Coden", reasoning: "Logik", agent: "Agenten & Tools", + scout: "Allrounder", reviewer: "Review", manager: "Manager" }; +const CAP_KW = [ + { label: "Bilder", kw: ["-vl-", "-vl", "vision", "llava", "multimodal", "-mm-", "pixtral"] }, + { label: "Coden", kw: ["coder", "-code-", "code-", "codestral", "starcoder"] }, + { label: "Logik", kw: ["-r1", "deepseek-r1", "reasoning", "qwq", "magistral", "-think", "thinking", "-o1"] }, + { label: "Agenten & Tools", kw: ["hermes", "-tool", "command-r", "watt", "-fc-", "function"] }, +]; +function capLabel(name) { + const l = (name || "").toLowerCase(); + for (const c of CAP_KW) if (c.kw.some(k => l.includes(k))) return c.label; + return "Allrounder"; +} +// MoE-Erkennung über strukturelle Marker im Namen (zuverlässig, keine Falschaussagen): +// 8x7B-Stil, „…-A3B" (aktive Parameter), oder explizit „mixtral"/„moe". +function isMoe(s = "") { + const l = s.toLowerCase(); + return /\d+x\d+(\.\d+)?b/.test(l) || /(?MoE${a ? ` · ~${a}B aktiv` : ""}`; +} +function capChip(label) { return label ? `${esc(label)}` : ""; } const FILTERS = [ { id: "", label: "Alle" }, { id: "coder", label: "Coder" }, { id: "scout", label: "Scout" }, @@ -182,23 +214,28 @@ function renderDiscover(d) {