feat(diagnostic): Katalog zeigt, ob ein Modell Werkzeuge kann (🔧) + Filter
Erkennung: Chat-Vorlage des GGUF (HF expand[]=gguf) kennt ein Werkzeug- Format, oder Schlagworte wie function-calling/tool-use. Zeigt, OB das Format da ist — nicht, wie gut das Modell damit umgeht. - Neue und hinzugefuegte Eintraege bekommen tools: true/false/null, bestehende beim naechsten "Von HuggingFace aktualisieren". - Liste: 🔧-Abzeichen; Haken "nur Modelle mit Werkzeugen" (Standard an) blendet Modelle ohne Werkzeug-Format aus (unbekannt bleibt sichtbar). - ℹ: Zeile "Werkzeuge: ja / nein / unbekannt". Getestet gegen HuggingFace (7 Modelle, u.a. Hermes 3 ueber das Schlagwort, weil dessen offizielle Vorlage minimal ist). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+23
-1
@@ -573,6 +573,11 @@ async function refreshLlmCatalogFromHF() {
|
||||
e.quant = q.quant; e.sizeGB = q.sizeGB; e.quants = q.quants; delete e.unusable;
|
||||
}));
|
||||
}
|
||||
// Werkzeug-Faehigkeit nachtragen (alle Eintraege, die noch keine Angabe haben)
|
||||
const noTools = [...byId.values()].filter(e => e.hfRepo && e.tools === undefined);
|
||||
for (let i = 0; i < noTools.length; i += 6) {
|
||||
await Promise.all(noTools.slice(i, i + 6).map(async e => { e.tools = await hfToolSupport(e.hfRepo); }));
|
||||
}
|
||||
let added = 0;
|
||||
for (const e of byId.values()) { if (e._new) { added++; delete e._new; } }
|
||||
// Aufraeumen: von HF uebernommen, vor > LLM_CATALOG_KEEP_DAYS Tagen, auf
|
||||
@@ -821,12 +826,27 @@ async function llmModelInfo(repo) {
|
||||
created: meta.createdAt || "", updated: meta.lastModified || "",
|
||||
tags: tags.slice(0, 20), families,
|
||||
uncensored: !!unc,
|
||||
tools: await hfToolSupport(repo),
|
||||
uncensoredWhy: unc ? `erkannt an „${unc[0]}“ in Name/Schlagworten` : "",
|
||||
intro: await hfReadmeIntro(base || repo) || await hfReadmeIntro(repo),
|
||||
introFrom: base || repo,
|
||||
};
|
||||
}
|
||||
|
||||
// Kann das Modell Werkzeuge? Kennzeichen: die Chat-Vorlage im GGUF (HF liefert
|
||||
// sie unter expand[]=gguf) kennt ein Werkzeug-Format, oder Schlagworte wie
|
||||
// function-calling/tool-use. Zeigt nur, OB das Format da ist, nicht wie gut.
|
||||
const TOOLS_TEMPLATE_RE = /\btools\b|tool_call|<tool|function_call/;
|
||||
const TOOLS_TAG_RE = /function.?call|tool.?use|tool.?calling|agent/i;
|
||||
async function hfToolSupport(repo) {
|
||||
const d = await hfJson(`https://huggingface.co/api/models/${repo}?expand[]=gguf&expand[]=tags`);
|
||||
if (!d) return null;
|
||||
const tpl = String(((d.gguf || {}).chat_template) || "");
|
||||
if (TOOLS_TEMPLATE_RE.test(tpl)) return true;
|
||||
if ((d.tags || []).some(t => TOOLS_TAG_RE.test(t))) return true;
|
||||
return tpl ? false : null; // keine Vorlage = unbekannt
|
||||
}
|
||||
|
||||
async function addHfRepoToCatalog(repo, description = "") {
|
||||
repo = hfRepoFromInput(repo);
|
||||
if (!/^[\w.-]+\/[\w.-]+$/.test(String(repo || ""))) throw new Error("ungueltiges Repo — Link oder org/name");
|
||||
@@ -842,7 +862,9 @@ async function addHfRepoToCatalog(repo, description = "") {
|
||||
if (same) return { models: cat, id: same.id, existed: true };
|
||||
while (cat.some(m => m.id === id)) id += "-2";
|
||||
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" });
|
||||
description: description || "Aus den HuggingFace-Trends übernommen", source: "hf",
|
||||
tools: await hfToolSupport(repo), uncensored: UNCENSORED_RE.test(repo),
|
||||
added_at: new Date().toISOString().slice(0, 10) });
|
||||
saveLlmCatalog(cat);
|
||||
return { models: cat, id, existed: false };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user