feat(2.0): W2+W3 — HF-Link/Suche + Modell-Verwaltungs-UX
W2: install akzeptiert HF-URL ODER org/repo (normalize_repo); GET /api/hf/
search + /api/hf/quants; Frontend AddModel-Panel (URL+Quant-Dropdown+freie
Suche) im Discover-Tab. W3: POST /api/models/{id}/role + /ctx; Installiert-
Tab mit Rollen-Select (fast/heavy/coder/...), ctx-Edit, Loeschen → LLM
tauschen per Klick.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+44
-1
@@ -1,11 +1,54 @@
|
||||
"""HuggingFace-Helfer: GGUF-Dateien eines Repos auflösen (inkl. Split-Teile) + Größen."""
|
||||
"""HuggingFace-Helfer: GGUF-Dateien eines Repos auflösen (inkl. Split-Teile) + Größen,
|
||||
freie Suche, Repo-URL→ID, verfügbare Quants."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
def normalize_repo(s: str) -> str:
|
||||
"""Akzeptiert volle HF-URL oder `org/repo` → liefert immer `org/repo`."""
|
||||
s = (s or "").strip()
|
||||
m = re.search(r"huggingface\.co/([^/\s]+/[^/\s?#]+)", s)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return s.strip("/")
|
||||
|
||||
|
||||
def list_quants(repo: str) -> list[str]:
|
||||
"""Verfügbare Quant-Stufen eines Repos (aus den GGUF-Dateinamen, ohne mmproj)."""
|
||||
quants: set[str] = set()
|
||||
for e in _tree(repo):
|
||||
p = str(e.get("path", ""))
|
||||
if p.lower().endswith(".gguf") and "mmproj" not in p.lower():
|
||||
m = re.search(r"(I?Q\d[\w]*|F16|BF16|FP16|F32)", p, re.IGNORECASE)
|
||||
if m:
|
||||
quants.add(m.group(1).upper())
|
||||
# gängige Reihenfolge zuerst
|
||||
order = {"Q4_K_M": 0, "Q4_K_S": 1, "Q5_K_M": 2, "Q6_K": 3, "Q8_0": 4, "Q3_K_M": 5, "Q2_K": 6}
|
||||
return sorted(quants, key=lambda q: (order.get(q, 99), q))
|
||||
|
||||
|
||||
def search(q: str, limit: int = 20) -> list[dict]:
|
||||
"""Freie HF-Suche nach GGUF-Repos."""
|
||||
url = (f"https://huggingface.co/api/models?search={q}"
|
||||
f"&filter=gguf&sort=downloads&direction=-1&limit={limit}")
|
||||
try:
|
||||
with httpx.Client(timeout=12.0) as c:
|
||||
data = c.get(url).json()
|
||||
except Exception:
|
||||
return []
|
||||
out = []
|
||||
for m in (data if isinstance(data, list) else []):
|
||||
rid = m.get("id")
|
||||
if rid:
|
||||
out.append({"repo": rid, "downloads": int(m.get("downloads") or 0),
|
||||
"likes": int(m.get("likes") or 0)})
|
||||
return out
|
||||
|
||||
|
||||
def hf_bin() -> str:
|
||||
"""Pfad zur `hf`-CLI (bevorzugt neben dem laufenden Python im venv)."""
|
||||
cand = os.path.join(os.path.dirname(sys.executable), "hf")
|
||||
|
||||
Reference in New Issue
Block a user