v5 Phase 2: aktuelle Modelle, GGUF-Klartext, Tools, HF-Token

- recipes.py: Juni-2026-Modelle (Qwen3-Coder-30B-A3B, Qwen3-8B/30B, Qwen2.5-VL-7B, Qwen3-4B);
  nur Repo gespeichert, GGUF-Datei wird beim Installieren dynamisch aufgeloest (_pick_gguf) +
  UPGRADES-Map fuer Phase 3.
- cookbook: 'kein GGUF' neutral statt rot + GGUF-Erklaerung (infoDot); ctx-infoDot.
- connect.js: 'Empfohlene Tools (Juni 2026)' (OpenCode/Cline/Continue) + MCP-Hinweis.
- HF-Token in Einstellungen -> als HF_TOKEN an Downloads/Recipe-Install durchgereicht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-21 14:13:30 +02:00
parent 8e7a5425d4
commit 970e04af30
8 changed files with 113 additions and 55 deletions
+27 -5
View File
@@ -32,6 +32,7 @@ class EvaluateRequest(BaseModel):
class InstallRecipeReq(BaseModel):
recipe_id: str
hf_token: str | None = None
def extract_params_b(repo_id: str) -> float:
"""Extrahiert die Parametergröße (in Milliarden) aus dem Repo-Namen."""
@@ -138,20 +139,41 @@ def install_recipe(req: InstallRecipeReq):
if not recipe:
raise HTTPException(404, "Setup nicht gefunden.")
ram_gb = psutil.virtual_memory().total / (1024 ** 3)
env = {"HF_XET_HIGH_PERFORMANCE": "1"}
if req.hf_token:
env["HF_TOKEN"] = req.hf_token
cfg = read_config()
job_ids = []
for m in recipe["models"]:
file = _pick_gguf(m["repo"], m.get("quant", "Q4_K_M"))
if not file:
continue # kein GGUF im Repo gefunden -> Modell ueberspringen (Rest installiert trotzdem)
target = MODELS_DIR / m["repo"].split("/")[-1]
target.mkdir(parents=True, exist_ok=True)
args = ["hf", "download", m["repo"], m["file"], "--local-dir", str(target)]
jid = start_job(args, f"download {m['name']}", env={"HF_XET_HIGH_PERFORMANCE": "1"})
JOBS[jid]["result_path"] = str(target / m["file"])
args = ["hf", "download", m["repo"], file, "--local-dir", str(target)]
jid = start_job(args, f"download {m['name']}", env=env)
JOBS[jid]["result_path"] = str(target / file)
job_ids.append(jid)
# Eintrag jetzt schon schreiben — optimaler Kontext, aber gedeckelt fuer schnellen Erststart.
ctx = min(max_ctx_for(m["params_b"], m["quant"], ram_gb), 32768)
path = str(target / m["file"])
path = str(target / file)
cmd = CMD_TEMPLATE.replace("{model}", path).replace("{ctx}", str(ctx))
cfg["models"][m["role"]] = {"cmd": LiteralScalarString(cmd + "\n"), "ttl": DEFAULT_TTL}
write_config(cfg)
return {"job_ids": job_ids, "count": len(recipe["models"])}
return {"job_ids": job_ids, "count": len(job_ids)}
def _pick_gguf(repo: str, quant: str = "Q4_K_M") -> str | None:
"""Beste GGUF-Datei eines Repos auflösen: bevorzugt gewünschten Quant, keine Split-Teile."""
try:
with httpx.Client(timeout=10.0) as c:
tree = c.get(f"https://huggingface.co/api/models/{repo}/tree/main").json()
except Exception: # noqa: BLE001
return None
ggufs = [f["path"] for f in tree if isinstance(f, dict) and str(f.get("path", "")).endswith(".gguf")]
if not ggufs:
return None
pref = [g for g in ggufs if quant.lower() in g.lower() and "-of-" not in g]
nosplit = [g for g in ggufs if "-of-" not in g]
return (pref or nosplit or ggufs)[0]
+5 -2
View File
@@ -31,6 +31,7 @@ class DownloadReq(BaseModel):
repo: str
file: str
subdir: str | None = None
hf_token: str | None = None
class RegisterReq(BaseModel):
@@ -139,8 +140,10 @@ def download(req: DownloadReq):
target = MODELS_DIR / sub
target.mkdir(parents=True, exist_ok=True)
args = ["hf", "download", req.repo, req.file, "--local-dir", str(target)]
job_id = start_job(args, f"download {req.repo}/{req.file}",
env={"HF_XET_HIGH_PERFORMANCE": "1"})
env = {"HF_XET_HIGH_PERFORMANCE": "1"}
if req.hf_token:
env["HF_TOKEN"] = req.hf_token
job_id = start_job(args, f"download {req.repo}/{req.file}", env=env)
JOBS[job_id]["result_path"] = str(target / req.file)
return {"job_id": job_id, "expected_path": str(target / req.file)}