feat(quick-wins): HF-Link-Suche, Download-ETA, Job-Verlauf loeschbar
- Cookbook-Profisuche: ganze HF-URL oder "owner/repo" direkt einfuegbar
(parseHfRepo) -> exakter Treffer statt unscharfer Suche; limit 12->40,
damit auch grosse/seltenere Modelle (z.B. Llama-4-Scout) erscheinen.
- jobengine.attach_download_progress: geglaettete Rate (EMA) + Restzeit
(eta_s) je Download; Aktivitaet zeigt "noch ~X min · Y MB/s".
- Jobs: erledigte/abgebrochene Eintraege einzeln loeschbar (DELETE
/api/jobs/{id}) + "Verlauf leeren" (POST /api/jobs/clear); laufende
bleiben geschuetzt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -43,7 +43,7 @@ function mount() {
|
||||
<summary>Profi-Modus: HuggingFace direkt durchsuchen</summary>
|
||||
<div class="acc-body">
|
||||
<div class="flex gap-3" style="margin-bottom:8px">
|
||||
<input id="cb-search" placeholder="Modell suchen, z.B. „Llama 3"…" style="margin:0;flex:1">
|
||||
<input id="cb-search" placeholder="Suchen oder HuggingFace-Link/Repo einfügen (z.B. unsloth/MiniMax-M3-GGUF)…" style="margin:0;flex:1">
|
||||
<button class="primary" id="cb-btn-search" style="white-space:nowrap">Suchen</button>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center" style="flex-wrap:wrap">
|
||||
@@ -273,18 +273,39 @@ function renderHwChip() {
|
||||
// ---- Profi-Suche (HuggingFace) ----
|
||||
function metricLine(fit) { return `~${fit.req_gb.toFixed(1)} GB · ~${Math.round(fit.tps)} Tok/s`; }
|
||||
|
||||
// Erkennt eine direkte Modell-Angabe: ganze HuggingFace-URL ODER „owner/repo".
|
||||
// So kann man Links wie https://huggingface.co/unsloth/MiniMax-M3-GGUF einfach
|
||||
// reinkopieren und landet exakt auf diesem Modell (statt einer unscharfen Suche).
|
||||
function parseHfRepo(s) {
|
||||
s = s.trim();
|
||||
const u = s.match(/huggingface\.co\/([^\s/]+\/[^\s/?#]+)/i);
|
||||
if (u) return u[1];
|
||||
if (/^[\w.-]+\/[\w.-]+$/.test(s)) return s;
|
||||
return null;
|
||||
}
|
||||
|
||||
async function doSearch() {
|
||||
let q = $("#cb-search").value.trim();
|
||||
if (activeFilter) q = q ? q + " " + activeFilter : activeFilter;
|
||||
if (!q) { $("#cb-grid").innerHTML = ""; return; }
|
||||
const sort = $("#cb-sort")?.value || "downloads";
|
||||
const raw = $("#cb-search").value.trim();
|
||||
const repo = parseHfRepo(raw);
|
||||
let q = raw;
|
||||
if (activeFilter && !repo) q = q ? q + " " + activeFilter : activeFilter;
|
||||
if (!q && !repo) { $("#cb-grid").innerHTML = ""; return; }
|
||||
const btn = $("#cb-btn-search"); btn.disabled = true; btn.textContent = "Lade…";
|
||||
$("#cb-grid").innerHTML = `<div class="empty" style="grid-column:1/-1;text-align:center">Suche auf HuggingFace…</div>`;
|
||||
try {
|
||||
const url = `https://huggingface.co/api/models?search=${encodeURIComponent(q)}&filter=gguf&sort=${encodeURIComponent(sort)}&direction=-1&limit=12`;
|
||||
const r = await fetch(url);
|
||||
currentResults = await r.json();
|
||||
renderResults(currentResults);
|
||||
if (repo) {
|
||||
// Direkter Treffer per Link/Repo — keine Stichwortsuche, sondern genau dieses Modell.
|
||||
currentResults = [{ id: repo, author: repo.split("/")[0], downloads: 0 }];
|
||||
renderResults(currentResults);
|
||||
} else {
|
||||
const sort = $("#cb-sort")?.value || "downloads";
|
||||
// limit=40: auch große/seltenere Modelle (z.B. Llama-4-Scout 109B) erscheinen,
|
||||
// statt von den Top-12-Trends verdrängt zu werden. Fit-Ampel sagt ehrlich, was passt.
|
||||
const url = `https://huggingface.co/api/models?search=${encodeURIComponent(q)}&filter=gguf&sort=${encodeURIComponent(sort)}&direction=-1&limit=40`;
|
||||
const r = await fetch(url);
|
||||
currentResults = await r.json();
|
||||
renderResults(currentResults);
|
||||
}
|
||||
} catch (e) { $("#cb-grid").innerHTML = `<div class="alert err" style="grid-column:1/-1">${esc(e.message)}</div>`; }
|
||||
btn.disabled = false; btn.textContent = "Suchen";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user