v5 Phase 2: aktuelle Modelle, GGUF-Klartext, Tools, HF-Token
- recipes.py: Juni-2026-Modelle (Qwen3-Coder-30B-A3B, Qwen3-8B/30B, Qwen2.5-VL-7B, Qwen3-4B); nur Repo gespeichert, GGUF-Datei wird beim Installieren dynamisch aufgeloest (_pick_gguf) + UPGRADES-Map fuer Phase 3. - cookbook: 'kein GGUF' neutral statt rot + GGUF-Erklaerung (infoDot); ctx-infoDot. - connect.js: 'Empfohlene Tools (Juni 2026)' (OpenCode/Cline/Continue) + MCP-Hinweis. - HF-Token in Einstellungen -> als HF_TOKEN an Downloads/Recipe-Install durchgereicht. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+43
-42
@@ -1,81 +1,82 @@
|
||||
"""
|
||||
Kuratierte Use-Case-"Setups" (Stacks) fuers Cookbook 2.0.
|
||||
Kuratierte Use-Case-„Setups" (Stacks) fuers Cookbook 2.0. Stand: Juni 2026.
|
||||
|
||||
Idee: Der Nutzer waehlt *wofuer* er es braucht — nicht *welches Modell*. Jedes Setup ist eine
|
||||
Kombination aus Rollen (z.B. Haupt-Coder + schneller Coder + Vision). Hardware-Fit + optimaler
|
||||
Kontext werden zur Laufzeit aus der echten RAM-Groesse berechnet (hw_math), nicht hier hartkodiert.
|
||||
|
||||
Daten, kein Code — bewusst getrennt (KISS/SoC). Repo/Datei sind unsloth-GGUFs auf HuggingFace.
|
||||
Daten, kein Code (KISS/SoC). Pro Modell nur Repo + Groesse/Quant — die konkrete GGUF-Datei wird
|
||||
beim Installieren dynamisch aus dem Repo aufgeloest (robust gegen Datei-Umbenennungen). Hardware-Fit
|
||||
+ optimaler Kontext kommen zur Laufzeit aus hw_math.
|
||||
"""
|
||||
|
||||
# Aktuelle, lokal gut laufende GGUF-Repos (Juni 2026). Bevorzugt unsloth (zuverlaessige Quants).
|
||||
RECIPES = [
|
||||
{
|
||||
"id": "coding", "title": "Coden & Programmieren", "icon": "code",
|
||||
"desc": "Ein starker Haupt-Coder, ein flinker für Autovervollständigung und Vision für Screenshots.",
|
||||
"desc": "Aktueller Top-Coder (MoE, nur 3B aktiv → schnell), ein flinker Helfer und Vision für Screenshots.",
|
||||
"models": [
|
||||
{"role": "coder", "name": "Qwen2.5 Coder 32B", "repo": "unsloth/Qwen2.5-Coder-32B-Instruct-GGUF",
|
||||
"file": "Qwen2.5-Coder-32B-Instruct-Q4_K_M.gguf", "params_b": 32, "quant": "Q4_K_M",
|
||||
"why": "Hauptmodell — versteht große Codebasen und komplexe Aufgaben."},
|
||||
{"role": "coder-fast", "name": "Qwen2.5 Coder 7B", "repo": "unsloth/Qwen2.5-Coder-7B-Instruct-GGUF",
|
||||
"file": "Qwen2.5-Coder-7B-Instruct-Q4_K_M.gguf", "params_b": 7, "quant": "Q4_K_M",
|
||||
"why": "Schnelle Autovervollständigung & einfache Edits."},
|
||||
{"role": "vision", "name": "Llama 3.2 Vision 11B", "repo": "unsloth/Llama-3.2-11B-Vision-Instruct-GGUF",
|
||||
"file": "Llama-3.2-11B-Vision-Instruct-Q4_K_M.gguf", "params_b": 11, "quant": "Q4_K_M",
|
||||
"why": "Liest Screenshots & Fehlerbilder für die Analyse."},
|
||||
{"role": "coder", "name": "Qwen3-Coder 30B-A3B", "repo": "unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF",
|
||||
"params_b": 30, "quant": "Q4_K_M", "why": "Aktuelles Top-Coder-Modell — versteht große Codebasen, läuft dank MoE flott."},
|
||||
{"role": "coder-fast", "name": "Qwen3 8B", "repo": "unsloth/Qwen3-8B-GGUF",
|
||||
"params_b": 8, "quant": "Q4_K_M", "why": "Schneller Helfer für einfache Edits & Autovervollständigung."},
|
||||
{"role": "vision", "name": "Qwen2.5-VL 7B", "repo": "unsloth/Qwen2.5-VL-7B-Instruct-GGUF",
|
||||
"params_b": 7, "quant": "Q4_K_M", "why": "Liest Screenshots & Fehlerbilder für die Analyse."},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "vision", "title": "Bilder verstehen", "icon": "eye",
|
||||
"desc": "Ein multimodales Modell, das Bilder und Text gemeinsam versteht.",
|
||||
"models": [
|
||||
{"role": "vision", "name": "Llama 3.2 Vision 11B", "repo": "unsloth/Llama-3.2-11B-Vision-Instruct-GGUF",
|
||||
"file": "Llama-3.2-11B-Vision-Instruct-Q4_K_M.gguf", "params_b": 11, "quant": "Q4_K_M",
|
||||
"why": "Beschreibt Bilder, liest Diagramme, analysiert Screenshots."},
|
||||
{"role": "vision", "name": "Qwen2.5-VL 7B", "repo": "unsloth/Qwen2.5-VL-7B-Instruct-GGUF",
|
||||
"params_b": 7, "quant": "Q4_K_M", "why": "Beschreibt Bilder, liest Diagramme, analysiert Screenshots."},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "chat", "title": "Allrounder & Chat", "icon": "compass",
|
||||
"desc": "Ein vielseitiges Modell für alltägliche Fragen, Texte und Brainstorming.",
|
||||
"desc": "Vielseitige Modelle für Alltag, Texte und Brainstorming.",
|
||||
"models": [
|
||||
{"role": "scout", "name": "Qwen2.5 7B", "repo": "unsloth/Qwen2.5-7B-Instruct-GGUF",
|
||||
"file": "Qwen2.5-7B-Instruct-Q4_K_M.gguf", "params_b": 7, "quant": "Q4_K_M",
|
||||
"why": "Schneller, kluger Allrounder — guter Standard."},
|
||||
{"role": "scout-pro", "name": "Qwen2.5 14B", "repo": "unsloth/Qwen2.5-14B-Instruct-GGUF",
|
||||
"file": "Qwen2.5-14B-Instruct-Q4_K_M.gguf", "params_b": 14, "quant": "Q4_K_M",
|
||||
"why": "Mehr Tiefe, wenn die Antworten besser sein sollen."},
|
||||
{"role": "scout", "name": "Qwen3 8B", "repo": "unsloth/Qwen3-8B-GGUF",
|
||||
"params_b": 8, "quant": "Q4_K_M", "why": "Schneller, kluger Allrounder — guter Standard."},
|
||||
{"role": "scout-pro", "name": "Qwen3 30B-A3B", "repo": "unsloth/Qwen3-30B-A3B-Instruct-2507-GGUF",
|
||||
"params_b": 30, "quant": "Q4_K_M", "why": "Mehr Tiefe (MoE), wenn die Antworten besser sein sollen."},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "longdoc", "title": "Lange Dokumente", "icon": "file",
|
||||
"desc": "Großes Modell mit großem Kontext — für lange Texte, Verträge, Bücher.",
|
||||
"models": [
|
||||
{"role": "reader", "name": "Qwen2.5 14B", "repo": "unsloth/Qwen2.5-14B-Instruct-GGUF",
|
||||
"file": "Qwen2.5-14B-Instruct-Q4_K_M.gguf", "params_b": 14, "quant": "Q4_K_M",
|
||||
"why": "Solide Qualität bei großem Kontextfenster (auto-optimiert)."},
|
||||
{"role": "reader", "name": "Qwen3 30B-A3B", "repo": "unsloth/Qwen3-30B-A3B-Instruct-2507-GGUF",
|
||||
"params_b": 30, "quant": "Q4_K_M", "why": "Starke Qualität bei großem Kontextfenster (auto-optimiert)."},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "agents", "title": "Agenten & Tool-Use", "icon": "layers",
|
||||
"desc": "Modelle, die gut mit Werkzeugen/Funktionen umgehen — für autonome Agenten.",
|
||||
"desc": "Modelle, die gut mit Werkzeugen/Funktionen umgehen — für autonome Agenten (MCP).",
|
||||
"models": [
|
||||
{"role": "agent", "name": "Qwen2.5 32B", "repo": "unsloth/Qwen2.5-32B-Instruct-GGUF",
|
||||
"file": "Qwen2.5-32B-Instruct-Q4_K_M.gguf", "params_b": 32, "quant": "Q4_K_M",
|
||||
"why": "Stark im Function-Calling und mehrstufigem Denken."},
|
||||
{"role": "agent-fast", "name": "Qwen2.5 7B", "repo": "unsloth/Qwen2.5-7B-Instruct-GGUF",
|
||||
"file": "Qwen2.5-7B-Instruct-Q4_K_M.gguf", "params_b": 7, "quant": "Q4_K_M",
|
||||
"why": "Schneller Helfer für einfache Tool-Schritte."},
|
||||
{"role": "agent", "name": "Qwen3 30B-A3B", "repo": "unsloth/Qwen3-30B-A3B-Instruct-2507-GGUF",
|
||||
"params_b": 30, "quant": "Q4_K_M", "why": "Stark im Function-Calling und mehrstufigem Denken."},
|
||||
{"role": "agent-fast", "name": "Qwen3 8B", "repo": "unsloth/Qwen3-8B-GGUF",
|
||||
"params_b": 8, "quant": "Q4_K_M", "why": "Schneller Helfer für einfache Tool-Schritte."},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "fast", "title": "Schnell & sparsam", "icon": "bolt",
|
||||
"desc": "Kleine, flinke Modelle — ideal bei wenig Speicher oder für schnelle Antworten.",
|
||||
"models": [
|
||||
{"role": "mini", "name": "Qwen2.5 3B", "repo": "unsloth/Qwen2.5-3B-Instruct-GGUF",
|
||||
"file": "Qwen2.5-3B-Instruct-Q4_K_M.gguf", "params_b": 3, "quant": "Q4_K_M",
|
||||
"why": "Winzig & sehr schnell für einfache Aufgaben."},
|
||||
{"role": "coder-fast", "name": "Qwen2.5 Coder 7B", "repo": "unsloth/Qwen2.5-Coder-7B-Instruct-GGUF",
|
||||
"file": "Qwen2.5-Coder-7B-Instruct-Q4_K_M.gguf", "params_b": 7, "quant": "Q4_K_M",
|
||||
"why": "Flinker Coder, falls es doch mal Code sein soll."},
|
||||
{"role": "mini", "name": "Qwen3 4B", "repo": "Qwen/Qwen3-4B-GGUF",
|
||||
"params_b": 4, "quant": "Q4_K_M", "why": "Winzig & sehr schnell für einfache Aufgaben."},
|
||||
{"role": "scout", "name": "Qwen3 8B", "repo": "unsloth/Qwen3-8B-GGUF",
|
||||
"params_b": 8, "quant": "Q4_K_M", "why": "Etwas mehr Können, immer noch flott."},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
# Upgrade-Map (Phase 3.2): wenn der Nutzer ein „altes" Modell hat, schlagen wir das neue vor.
|
||||
UPGRADES = [
|
||||
{"match": ["qwen2.5-coder", "qwen2_5-coder"], "name": "Qwen3-Coder 30B-A3B",
|
||||
"repo": "unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF", "params_b": 30, "quant": "Q4_K_M",
|
||||
"why": "Nachfolger deines Coders: MoE mit nur 3B aktiven Parametern → schneller und stärker auf SWE-bench."},
|
||||
{"match": ["qwen2.5-7b", "qwen2_5-7b", "qwen2.5-instruct"], "name": "Qwen3 8B",
|
||||
"repo": "unsloth/Qwen3-8B-GGUF", "params_b": 8, "quant": "Q4_K_M",
|
||||
"why": "Neuere Generation deines Allrounders — besser im Reasoning und bei Tool-Use."},
|
||||
{"match": ["llama-3.2-11b-vision", "llama3.2", "llama-3.2-vision"], "name": "Qwen2.5-VL 7B",
|
||||
"repo": "unsloth/Qwen2.5-VL-7B-Instruct-GGUF", "params_b": 7, "quant": "Q4_K_M",
|
||||
"why": "Modernes Vision-Modell — kleiner und meist treffsicherer beim Bildverständnis."},
|
||||
]
|
||||
|
||||
+27
-5
@@ -32,6 +32,7 @@ class EvaluateRequest(BaseModel):
|
||||
|
||||
class InstallRecipeReq(BaseModel):
|
||||
recipe_id: str
|
||||
hf_token: str | None = None
|
||||
|
||||
def extract_params_b(repo_id: str) -> float:
|
||||
"""Extrahiert die Parametergröße (in Milliarden) aus dem Repo-Namen."""
|
||||
@@ -138,20 +139,41 @@ def install_recipe(req: InstallRecipeReq):
|
||||
if not recipe:
|
||||
raise HTTPException(404, "Setup nicht gefunden.")
|
||||
ram_gb = psutil.virtual_memory().total / (1024 ** 3)
|
||||
env = {"HF_XET_HIGH_PERFORMANCE": "1"}
|
||||
if req.hf_token:
|
||||
env["HF_TOKEN"] = req.hf_token
|
||||
cfg = read_config()
|
||||
job_ids = []
|
||||
for m in recipe["models"]:
|
||||
file = _pick_gguf(m["repo"], m.get("quant", "Q4_K_M"))
|
||||
if not file:
|
||||
continue # kein GGUF im Repo gefunden -> Modell ueberspringen (Rest installiert trotzdem)
|
||||
target = MODELS_DIR / m["repo"].split("/")[-1]
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
args = ["hf", "download", m["repo"], m["file"], "--local-dir", str(target)]
|
||||
jid = start_job(args, f"download {m['name']}", env={"HF_XET_HIGH_PERFORMANCE": "1"})
|
||||
JOBS[jid]["result_path"] = str(target / m["file"])
|
||||
args = ["hf", "download", m["repo"], file, "--local-dir", str(target)]
|
||||
jid = start_job(args, f"download {m['name']}", env=env)
|
||||
JOBS[jid]["result_path"] = str(target / 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)
|
||||
path = str(target / m["file"])
|
||||
path = str(target / file)
|
||||
cmd = CMD_TEMPLATE.replace("{model}", path).replace("{ctx}", str(ctx))
|
||||
cfg["models"][m["role"]] = {"cmd": LiteralScalarString(cmd + "\n"), "ttl": DEFAULT_TTL}
|
||||
write_config(cfg)
|
||||
return {"job_ids": job_ids, "count": len(recipe["models"])}
|
||||
return {"job_ids": job_ids, "count": len(job_ids)}
|
||||
|
||||
|
||||
def _pick_gguf(repo: str, quant: str = "Q4_K_M") -> str | None:
|
||||
"""Beste GGUF-Datei eines Repos auflösen: bevorzugt gewünschten Quant, keine Split-Teile."""
|
||||
try:
|
||||
with httpx.Client(timeout=10.0) as c:
|
||||
tree = c.get(f"https://huggingface.co/api/models/{repo}/tree/main").json()
|
||||
except Exception: # noqa: BLE001
|
||||
return None
|
||||
ggufs = [f["path"] for f in tree if isinstance(f, dict) and str(f.get("path", "")).endswith(".gguf")]
|
||||
if not ggufs:
|
||||
return None
|
||||
pref = [g for g in ggufs if quant.lower() in g.lower() and "-of-" not in g]
|
||||
nosplit = [g for g in ggufs if "-of-" not in g]
|
||||
return (pref or nosplit or ggufs)[0]
|
||||
|
||||
|
||||
+5
-2
@@ -31,6 +31,7 @@ class DownloadReq(BaseModel):
|
||||
repo: str
|
||||
file: str
|
||||
subdir: str | None = None
|
||||
hf_token: str | None = None
|
||||
|
||||
|
||||
class RegisterReq(BaseModel):
|
||||
@@ -139,8 +140,10 @@ def download(req: DownloadReq):
|
||||
target = MODELS_DIR / sub
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
args = ["hf", "download", req.repo, req.file, "--local-dir", str(target)]
|
||||
job_id = start_job(args, f"download {req.repo}/{req.file}",
|
||||
env={"HF_XET_HIGH_PERFORMANCE": "1"})
|
||||
env = {"HF_XET_HIGH_PERFORMANCE": "1"}
|
||||
if req.hf_token:
|
||||
env["HF_TOKEN"] = req.hf_token
|
||||
job_id = start_job(args, f"download {req.repo}/{req.file}", env=env)
|
||||
JOBS[job_id]["result_path"] = str(target / req.file)
|
||||
return {"job_id": job_id, "expected_path": str(target / req.file)}
|
||||
|
||||
|
||||
@@ -100,6 +100,11 @@
|
||||
<input id="token" class="tokin" placeholder="Nur nötig, wenn der Server geschützt ist…" autocomplete="off">
|
||||
<div class="hint mt-2">Schützt den Zugriff. Wird für API-Aufrufe und Live-Verbindungen mitgeschickt.</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<label>HuggingFace-Token (optional)</label>
|
||||
<input id="hf-token" class="tokin" placeholder="hf_…" autocomplete="off">
|
||||
<div class="hint mt-2">Nicht nötig für öffentliche Modelle — vermeidet aber Rate-Limits und erlaubt zugangsbeschränkte Modelle. Erstellen: huggingface.co/settings/tokens</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ export function hdr() {
|
||||
return h;
|
||||
}
|
||||
|
||||
// HuggingFace-Token (optional) — wird bei Downloads als HF_TOKEN mitgesendet.
|
||||
export function getHfToken() { return localStorage.getItem("mc_hf_token") || ""; }
|
||||
export function setHfToken(t) { localStorage.setItem("mc_hf_token", (t || "").trim()); }
|
||||
|
||||
export async function api(path, opts = {}) {
|
||||
const r = await fetch(path, { headers: hdr(), ...opts });
|
||||
const data = await r.json().catch(() => ({}));
|
||||
|
||||
+4
-1
@@ -1,7 +1,7 @@
|
||||
// main.js — App-Boot: Panels mounten, Nav starten, Topbar/Alert pflegen, Polling fahren.
|
||||
// Panel-Vertrag: { id, mount?(), onStatus?(s), onJobs?(jobs) }.
|
||||
|
||||
import { api, getToken, setToken } from "./core/api.js";
|
||||
import { api, getToken, setToken, getHfToken, setHfToken } from "./core/api.js";
|
||||
import { $ } from "./core/ui.js";
|
||||
import { initNav } from "./core/nav.js";
|
||||
|
||||
@@ -112,6 +112,9 @@ function bootToken() {
|
||||
i.addEventListener("change", e => { setToken(e.target.value); pollStatus(); });
|
||||
}
|
||||
|
||||
const hf = $("#hf-token");
|
||||
if (hf) { hf.value = getHfToken(); hf.addEventListener("change", e => setHfToken(e.target.value)); }
|
||||
|
||||
const sbtn = $("#nav-settings");
|
||||
const smod = $("#settings-modal");
|
||||
const scls = $("#sm-close");
|
||||
|
||||
@@ -30,6 +30,15 @@ function field(label, value) {
|
||||
}
|
||||
const okChip = txt => `<span class="chip" style="background:rgba(63,185,80,.12);border-color:rgba(63,185,80,.25);color:#7ee29a">${icon("check")}${esc(txt)}</span>`;
|
||||
|
||||
function toolRow(name, desc, url, badge) {
|
||||
return `<div class="li" style="align-items:flex-start">
|
||||
<div class="li-main">
|
||||
<div class="flex items-center gap-2"><span class="li-id" style="font-family:var(--sans);font-weight:500">${esc(name)}</span>${badge ? `<span class="fit-badge ok">${esc(badge)}</span>` : ""}</div>
|
||||
<div class="li-sub">${esc(desc)}</div></div>
|
||||
<a href="${esc(url)}" target="_blank" rel="noopener"><button class="ghost">Öffnen</button></a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function render() {
|
||||
const c = $(".view[data-view='connect']"); if (!c) return;
|
||||
const url = baseUrl();
|
||||
@@ -56,9 +65,18 @@ function render() {
|
||||
<div id="cn-vision"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-h"><h3>Empfohlene Tools (Juni 2026)</h3></div>
|
||||
<div class="card-sub">Diese verbinden sich nahtlos mit deinem Stack. Einrichtungs-Configs findest du darunter.</div>
|
||||
${toolRow("OpenCode", "Beliebtester Open-Source-Coding-Agent (MIT) — Terminal, Desktop & IDE.", "https://opencode.ai", "Empfohlen")}
|
||||
${toolRow("Cline", "Bestes Erlebnis in VS Code, Bring-Your-Own-Key, Mensch-im-Loop.", "https://cline.bot")}
|
||||
${toolRow("Continue.dev", "Feinkörnige Kontrolle + bestes MCP-Ökosystem für Werkzeuge.", "https://continue.dev")}
|
||||
<div class="hint" style="margin-top:12px">MCP gibt Agenten zusätzliche Werkzeuge (Web durchsuchen, Dateien, Tools). Continue & Cline unterstützen MCP — mehr dazu im Guide.</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="padding:0;overflow:hidden">
|
||||
<details class="guide-acc" open>
|
||||
<summary>OpenCode (Windows)</summary>
|
||||
<summary>OpenCode (Windows) — Config</summary>
|
||||
<div class="acc-body">
|
||||
<p>Lege in OpenCode einen OpenAI-kompatiblen Provider an:</p>
|
||||
${field("Basis-URL", url)}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// cookbook.js — Cookbook 2.0 (v4): Use-Case-Setups („Wofür?") + Profi-Suche.
|
||||
// Backend: /api/cookbook/{recipes,install-recipe,analyze,evaluate} (hw_math).
|
||||
|
||||
import { api } from "../core/api.js";
|
||||
import { api, getHfToken } from "../core/api.js";
|
||||
import { $, esc, icon, toast, infoDot } 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 FILTERS = [
|
||||
{ id: "", label: "Alle" }, { id: "coder", label: "Coder" }, { id: "scout", label: "Scout" },
|
||||
@@ -55,6 +56,7 @@ function mount() {
|
||||
<span><i style="background:var(--warn)"></i>läuft, aber knapp</span>
|
||||
<span><i style="background:var(--err)"></i>zu groß für deinen Speicher</span>
|
||||
</div>
|
||||
<div class="hint" style="margin:-6px 0 14px">Es werden nur Modelle im GGUF-Format ${infoDot(GGUF_HELP)} gezeigt — nur die laufen lokal.</div>
|
||||
<div class="grid grid-3" id="cb-grid"></div>
|
||||
</div>
|
||||
</details>
|
||||
@@ -163,7 +165,7 @@ function openRecipe(id) {
|
||||
async function installRecipe(id) {
|
||||
const btn = $("#cb-r-install"); btn.disabled = true; btn.textContent = "Starte…";
|
||||
try {
|
||||
const r = await api("/api/cookbook/install-recipe", { method: "POST", body: JSON.stringify({ recipe_id: id }) });
|
||||
const r = await api("/api/cookbook/install-recipe", { method: "POST", body: JSON.stringify({ recipe_id: id, hf_token: getHfToken() }) });
|
||||
toast(`Setup wird installiert (${r.count} Modelle) — siehe Aktivität.`);
|
||||
$("#cb-recipe-modal").style.display = "none";
|
||||
document.querySelector(".nav-item[data-view='activity']")?.click();
|
||||
@@ -226,7 +228,7 @@ async function fetchFitForCard(i, repo_id) {
|
||||
const res = await api("/api/cookbook/analyze", { method: "POST", body: JSON.stringify({ repo_id, ctx: 8192 }) });
|
||||
const b = $("#cb-b-" + i), mt = $("#cb-m-" + i);
|
||||
if (!b || !mt) return;
|
||||
if (!res.files?.length) { b.className = "fit-badge bad"; b.textContent = "keine GGUFs"; mt.textContent = "—"; return; }
|
||||
if (!res.files?.length) { b.className = "fit-badge"; b.style.cssText = "background:rgba(139,151,165,.14);color:var(--mut)"; b.title = GGUF_HELP; b.textContent = "kein GGUF"; mt.textContent = "—"; return; }
|
||||
let best = res.files.find(f => f.quant?.includes("Q4_K_M")) || res.files[0];
|
||||
b.className = "fit-badge " + fitCls(best.fit.level); b.textContent = best.fit.text;
|
||||
mt.textContent = metricLine(best.fit) + " · " + (best.quant || "GGUF");
|
||||
@@ -289,7 +291,7 @@ async function doDownload() {
|
||||
if (!repo || !file || !alias) return toast("Bitte alle Felder ausfüllen.", true);
|
||||
const btn = $("#cb-m-download"); btn.disabled = true; btn.textContent = "Starte…";
|
||||
try {
|
||||
const res = await api("/api/download", { method: "POST", body: JSON.stringify({ repo, file }) });
|
||||
const res = await api("/api/download", { method: "POST", body: JSON.stringify({ repo, file, hf_token: getHfToken() }) });
|
||||
await api("/api/register", { method: "POST", body: JSON.stringify({ alias, model_path: res.expected_path, ctx }) });
|
||||
toast("Download gestartet — siehe Aktivität.");
|
||||
$("#cb-modal").style.display = "none";
|
||||
|
||||
Reference in New Issue
Block a user