feat(diagnostic): Modell per HuggingFace-Link in den Katalog — mit GGUF-Vorschlaegen

Bisher kamen Modelle nur ueber "Von HuggingFace aktualisieren" und die
Trends in den Katalog. Jetzt: Feld "HuggingFace-Link oder org/name".
Hat das Repo kein GGUF (z.B. nur safetensors wie Virtue-AI-HUB/VulnLLM-R-7B
oder fdtn-ai/Foundation-Sec-8B), sucht der Server GGUF-Fassungen desselben
Modells (HF-Suche, filter=gguf, nach Downloads) und bietet sie mit
"Diese nehmen" an. Hinzugefuegte stehen danach gefiltert in der Liste.

Getestet gegen HuggingFace: beide Beispiele ohne GGUF erkannt, je 5
ladbare GGUF-Fassungen gefunden (Q4_K_M ~4,7–4,9 GB).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-31 19:14:24 +01:00
co-authored by Claude Opus 5.5
parent 2cd8f1a0de
commit 848eee5d1c
2 changed files with 68 additions and 8 deletions
+35
View File
@@ -960,6 +960,12 @@
den Fortschritt. Danach ist es im Modell-Dropdown oben waehlbar.
<span style="color:#FFD60A;">Achte auf den VRAM der Box (Spalte VRAM).</span>
</p>
<div style="display:flex;gap:6px;flex-wrap:wrap;margin:0 0 8px;">
<input id="llm-catalog-add" type="text" placeholder="HuggingFace-Link oder org/name hinzufügen …" onkeydown="if(event.key==='Enter')addCatalogByLink()"
style="flex:1;min-width:240px;background:#1E1E2E;border:1px solid #333;border-radius:4px;padding:6px 8px;color:#E0E0F0;font-size:12px;">
<button class="btn secondary" onclick="addCatalogByLink()" style="padding:4px 10px;font-size:11px;">Hinzufügen</button>
</div>
<div id="llm-catalog-add-result" style="font-size:12px;margin:0 0 8px;"></div>
<input id="llm-catalog-search" type="search" placeholder="🔍 Modell suchen … (z.B. coder, qwen3, 30b)" oninput="filterLlmCatalog()"
style="display:none;width:100%;max-width:360px;box-sizing:border-box;margin:0 0 8px;background:#1E1E2E;border:1px solid #333;border-radius:4px;padding:6px 8px;color:#E0E0F0;font-size:12px;">
<div id="llm-catalog-list" style="font-size:12px;color:#8888AA;overflow-x:auto;">(lade Katalog…)</div>
@@ -10206,6 +10212,35 @@
'<td style="' + td + 'text-align:right;white-space:nowrap;">' + act + '</td></tr>';
}).join('') + '</table></div>';
}
// Modell per Link/Repo in den Katalog. Ohne GGUF: GGUF-Fassungen vorschlagen.
async function addCatalogByLink(repo) {
const inp = document.getElementById('llm-catalog-add');
const out = document.getElementById('llm-catalog-add-result');
const val = repo || (inp.value || '').trim();
if (!val) return;
out.innerHTML = '<span style="color:#8888AA;">Prüfe ' + escapeHtml(val) + ' auf HuggingFace …</span>';
try {
const j = await (await fetch('/api/llm-catalog/add', { method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hfRepo: val, manual: true }) })).json();
if (j.ok) {
_llmCatalog = j.models || _llmCatalog;
renderLlmCatalog();
inp.value = '';
out.innerHTML = '<span style="color:#34C759;">✓ ' + escapeHtml(j.id) + (j.existed ? ' war schon im Katalog' : ' ist jetzt im Katalog') + ' — unten Ziel-Karte wählen und laden.</span>';
const s = document.getElementById('llm-catalog-search');
if (s) { s.value = j.id; filterLlmCatalog(); }
return;
}
const alts = j.alternatives || [];
out.innerHTML = '<span style="color:#FF9F0A;">⚠ ' + escapeHtml(j.error || 'Fehler') + '</span>'
+ (alts.length ? '<div style="color:#9a9ab5;margin-top:4px;">GGUF-Fassungen desselben Modells:</div>' + alts.map(a =>
`<div style="display:flex;gap:8px;align-items:center;margin-top:3px;">
<a href="https://huggingface.co/${escapeHtml(a.repo)}" target="_blank" rel="noopener noreferrer" style="color:#E0E0F0;">${escapeHtml(a.repo)}</a>
<span style="color:#555570;">↓ ${Number(a.downloads).toLocaleString('de')}</span>
<button class="btn secondary" onclick="addCatalogByLink('${escapeHtmlAttr(a.repo)}')" style="padding:1px 8px;font-size:11px;">Diese nehmen</button></div>`).join('')
: '');
} catch (e) { out.innerHTML = '<span style="color:#FF6B6B;">✗ ' + escapeHtml(e.message) + '</span>'; }
}
async function addTrendingToCatalog(btn, repo) {
btn.disabled = true; btn.textContent = '…';
try {