import { api } from "../core/api.js"; import { $ } from "../core/ui.js"; const COOKBOOK_MODELS = [ { id: "qwen-coder-32b", name: "Qwen 2.5 Coder 32B", repo: "unsloth/Qwen2.5-Coder-32B-Instruct-GGUF", file: "Qwen2.5-Coder-32B-Instruct-Q4_K_M.gguf", desc: "Top-Tier lokales Coder-Modell. Braucht viel Speicher.", params_b: 32, quant: "Q4_K_M", ctx: 32768, alias: "coder" }, { id: "qwen-coder-7b", name: "Qwen 2.5 Coder 7B", repo: "unsloth/Qwen2.5-Coder-7B-Instruct-GGUF", file: "Qwen2.5-Coder-7B-Instruct-Q4_K_M.gguf", desc: "Schneller Coder. Perfekte Balance aus Speed und Qualität.", params_b: 7, quant: "Q4_K_M", ctx: 32768, alias: "coder-fast" }, { id: "llama3-vision-11b", 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", desc: "Modell für Bilderkennung und multimodale Tasks.", params_b: 11, quant: "Q4_K_M", ctx: 8192, alias: "vision" }, { id: "qwen-general-7b", name: "Qwen 2.5 7B", repo: "unsloth/Qwen2.5-7B-Instruct-GGUF", file: "Qwen2.5-7B-Instruct-Q4_K_M.gguf", desc: "Hervorragendes Generalist/Scout Modell.", params_b: 7, quant: "Q4_K_M", ctx: 8192, alias: "scout" } ]; // Odysseus Hardware Fit Logic function estimateMemoryGB(params_b, quant, ctx) { // Q4_K_M is roughly 0.6 bytes per param const bpp = 0.6; const weights = params_b * bpp; // Context roughly: 1GB per 8K for 7B const context = (ctx / 8192) * (params_b / 7) * 0.8; return weights + context; } function getFit(m, sys) { const req = estimateMemoryGB(m.params_b, m.quant, m.ctx); // Falls keine sys-Daten da sind (Backend mock), nimm Standardwerte an const vram_bytes = sys?.gpu?.vram?.total || 0; const vram = vram_bytes / (1024 ** 3); const ram_bytes = sys?.ram?.total || 0; const ram_used = sys?.ram?.used || 0; const ram = (ram_bytes) / (1024 ** 3); const freeRam = (ram_bytes - ram_used) / (1024 ** 3); // Wenn gar keine echten Metriken kommen (Windows Dummy Backend), immer "Fits" für Demo if (vram === 0 && ram === 0) return { level: "perfect", class: "green", text: "Fits (Mock)", req }; if (vram > 0 && req <= vram) return { level: "perfect", class: "green", text: "Fits VRAM", req }; if (req <= (vram + freeRam)) return { level: "good", class: "yellow", text: "RAM Offload", req }; return { level: "too_tight", class: "red", text: "OOM (Zu groß)", req }; } let lastSys = null; let dlPoll = null; function mount() { render(); } function unmount() { if (dlPoll) clearInterval(dlPoll); } function onSystem(sys) { lastSys = sys; renderGrid(); } export default { mount, unmount, onSystem }; function render() { const c = document.querySelector(".view[data-view='cookbook']"); c.innerHTML = `

Custom Download

`; document.getElementById("cb-btn-dl").onclick = () => { startDownload( document.getElementById("cb-repo").value, document.getElementById("cb-file").value, "", 8192 ); }; document.getElementById("cb-btn-reg").onclick = async () => { const alias = document.getElementById("cb-reg-alias").value; const path = document.getElementById("cb-reg-path").value; const ctx = document.getElementById("cb-reg-ctx").value; if (!alias || !path) return; try { await api("/api/models/register", { method: "POST", body: JSON.stringify({ alias, model_path: path, ctx_len: parseInt(ctx)||4096 }) }); alert("Erfolgreich eingepflegt!"); document.getElementById("cb-register-box").style.display = "none"; } catch(e) { alert("Fehler: " + e); } }; renderGrid(); } function renderGrid() { const grid = document.getElementById("cb-grid"); if (!grid) return; grid.innerHTML = COOKBOOK_MODELS.map(m => { const fit = getFit(m, lastSys); return `

${m.name}

${fit.text}
${m.desc}

Größe: ~${fit.req.toFixed(1)} GB (inkl. Context)
Quant: ${m.quant}
`; }).join(""); document.querySelectorAll(".cb-btn-preset").forEach(b => { b.onclick = () => { const m = COOKBOOK_MODELS.find(x => x.id === b.dataset.id); if(m) { document.getElementById("cb-repo").value = m.repo; document.getElementById("cb-file").value = m.file; startDownload(m.repo, m.file, m.alias, m.ctx); } }; }); } async function startDownload(repo, file, autoAlias, autoCtx) { if(!repo || !file) return; document.getElementById("cb-dl-prog").style.display = "block"; document.getElementById("cb-dl-prog").innerText = "Starte Download..."; document.getElementById("cb-register-box").style.display = "none"; try { const res = await api("/api/models/download", { method: "POST", body: JSON.stringify({ repo_id: repo, filename: file }) }); if (res.job_id) { if(dlPoll) clearInterval(dlPoll); dlPoll = setInterval(() => checkProg(res.job_id, autoAlias, autoCtx), 2000); } } catch(e) { document.getElementById("cb-dl-prog").innerText = "Fehler: " + e; } } async function checkProg(jobId, autoAlias, autoCtx) { try { const res = await api("/api/jobs"); const j = res.find(x => x.id === jobId); if (!j) { clearInterval(dlPoll); document.getElementById("cb-dl-prog").innerText = "Job verschwunden."; return; } document.getElementById("cb-dl-prog").innerText = j.status + (j.error ? (" - " + j.error) : ""); if (j.status === "done") { clearInterval(dlPoll); document.getElementById("cb-register-box").style.display = "block"; document.getElementById("cb-reg-path").value = j.result || ""; if(autoAlias) document.getElementById("cb-reg-alias").value = autoAlias; if(autoCtx) document.getElementById("cb-reg-ctx").value = autoCtx; } else if (j.status === "failed") { clearInterval(dlPoll); } } catch(e) {} }