feat(diagnostic): Beliebtheit im Modell-Katalog — Downloads und Likes von HuggingFace

HF hat keine Sternebewertung; es gibt Downloads (30 Tage, gesamt) und
Likes. Beide sagen 'viel benutzt', nicht 'gut' (viele Downloads sind
Automatik, Likes oft beim Entdecken). Angezeigt unter dem Modellnamen mit
Hinweis im Tooltip; beim Hinzufuegen sofort, beim taeglichen Abgleich
aufgefrischt. ℹ: Zeile Beliebtheit mit Einordnung.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-31 21:19:24 +01:00
co-authored by Claude Opus 5.5
parent 237a216945
commit 3fbe2dd090
2 changed files with 22 additions and 6 deletions
+17 -5
View File
@@ -573,12 +573,16 @@ async function refreshLlmCatalogFromHF() {
e.quant = q.quant; e.sizeGB = q.sizeGB; e.quants = q.quants; delete e.unusable;
}));
}
// Werkzeug-Faehigkeit + Sicherheits-Scan nachtragen (wo noch keine Angabe)
const noTools = [...byId.values()].filter(e => e.hfRepo && (e.tools === undefined || e.safety === undefined));
for (let i = 0; i < noTools.length; i += 6) {
await Promise.all(noTools.slice(i, i + 6).map(async e => {
// Werkzeug-Faehigkeit + Sicherheits-Scan nachtragen (wo noch keine Angabe),
// Beliebtheit bei jedem Abgleich frisch (aendert sich taeglich)
const today = new Date().toISOString().slice(0, 10);
const toEnrich = [...byId.values()].filter(e => e.hfRepo && (e.tools === undefined || e.safety === undefined
|| !e.popularity || e.popularity.at !== today));
for (let i = 0; i < toEnrich.length; i += 6) {
await Promise.all(toEnrich.slice(i, i + 6).map(async e => {
if (e.tools === undefined) e.tools = await hfToolSupport(e.hfRepo);
if (e.safety === undefined) e.safety = await hfRepoSafety(e.hfRepo);
if (!e.popularity || e.popularity.at !== today) e.popularity = await hfPopularity(e.hfRepo) || e.popularity;
}));
}
let added = 0;
@@ -862,6 +866,14 @@ function knownGgufAuthor(a) { // Funktion: TRUSTED_GGUF_AUTHORS steht we
return TRUSTED_GGUF_AUTHORS.has(a) || EXTRA_KNOWN_GGUF_AUTHORS.has(a);
}
const RISKY_FILE_RE = /\.(py|sh|bash|bat|cmd|ps1|exe|dll|so|dylib|pkl|pickle|pt|pth|ckpt|joblib|npy|zip|tar|gz|7z|jar|js|vbs)$|(^|\/)pytorch_model[^/]*\.bin$/i;
// Beliebtheit: HF hat keine Sterne — nur Downloads (30 Tage / gesamt) und Likes.
async function hfPopularity(repo) {
const d = await hfJson(`https://huggingface.co/api/models/${repo}?expand[]=downloads&expand[]=likes&expand[]=downloadsAllTime`);
if (!d) return null;
return { downloads: d.downloads || 0, downloadsAll: d.downloadsAllTime || 0, likes: d.likes || 0,
at: new Date().toISOString().slice(0, 10) };
}
async function hfRepoSafety(repo) {
const author = String(repo).split("/")[0].toLowerCase();
const out = { known: knownGgufAuthor(author), flagged: [], risky: [], scanned: 0 };
@@ -896,7 +908,7 @@ async function addHfRepoToCatalog(repo, description = "") {
cat.push({ id, hfRepo: repo, quant: q.quant, ctx: 8192, sizeGB: q.sizeGB, quants: q.quants,
description: description || "Aus den HuggingFace-Trends übernommen", source: "hf",
tools: await hfToolSupport(repo), uncensored: UNCENSORED_RE.test(repo),
safety: await hfRepoSafety(repo),
safety: await hfRepoSafety(repo), popularity: await hfPopularity(repo),
added_at: new Date().toISOString().slice(0, 10) });
saveLlmCatalog(cat);
return { models: cat, id, existed: false };