feat: echter Download-Fortschritt via Datei-Polling
`hf` gibt im Nicht-TTY-Modus keinen Fortschritt aus (am Bosgame verifiziert: 0 CR-Frames). Stattdessen pollt jobengine.attach_download_progress die wachsende <local-dir>/.cache/huggingface/download/*.incomplete-Datei gegen die Gesamtgroesse aus der HF-Tree-API (cookbook.hf_file_size) -> exaktes %. - attach_download_progress an /api/download, install-recipe, install-model - Frontend (Aktivitaet + Server-Karte): nutzt job.progress bevorzugt, Log-%-Parsing bleibt Fallback fuer Tools, die selbst Prozente ausgeben Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+17
-1
@@ -14,7 +14,7 @@ from auth import auth
|
||||
from hw_math import evaluate_fit, max_ctx_for
|
||||
from config import MODELS_DIR, CMD_TEMPLATE, DEFAULT_TTL, HF_DOWNLOAD_ENV, hf_bin
|
||||
from llamaswap import read_config, write_config
|
||||
from jobengine import start_job, JOBS
|
||||
from jobengine import start_job, JOBS, attach_download_progress
|
||||
from recipes import RECIPES, UPGRADES
|
||||
|
||||
router = APIRouter(prefix="/api/cookbook", dependencies=[Depends(auth)])
|
||||
@@ -163,6 +163,7 @@ def install_recipe(req: InstallRecipeReq):
|
||||
args = [hf_bin(), "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)
|
||||
attach_download_progress(jid, str(target), hf_file_size(m["repo"], 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)
|
||||
@@ -173,6 +174,20 @@ def install_recipe(req: InstallRecipeReq):
|
||||
return {"job_ids": job_ids, "count": len(job_ids)}
|
||||
|
||||
|
||||
def hf_file_size(repo: str, file: str) -> int:
|
||||
"""Groesse einer Datei im HF-Repo (Bytes) fuer die Fortschrittsanzeige. 0 wenn unbekannt.
|
||||
GGUFs sind LFS -> ggf. unter 'lfs.size'."""
|
||||
try:
|
||||
with httpx.Client(timeout=10.0) as c:
|
||||
tree = c.get(f"https://huggingface.co/api/models/{repo}/tree/main").json()
|
||||
for f in tree:
|
||||
if isinstance(f, dict) and f.get("path") == file:
|
||||
return int(f.get("size") or (f.get("lfs") or {}).get("size") or 0)
|
||||
except Exception: # noqa: BLE001
|
||||
return 0
|
||||
return 0
|
||||
|
||||
|
||||
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:
|
||||
@@ -234,6 +249,7 @@ def install_model(req: InstallModelReq):
|
||||
jid = start_job([hf_bin(), "download", req.repo, file, "--local-dir", str(target)],
|
||||
f"download {req.repo.split('/')[-1]}", env=env)
|
||||
JOBS[jid]["result_path"] = str(target / file)
|
||||
attach_download_progress(jid, str(target), hf_file_size(req.repo, file))
|
||||
cfg = read_config()
|
||||
ctx = min(max_ctx_for(req.params_b, req.quant, ram_gb), 32768)
|
||||
cmd = CMD_TEMPLATE.replace("{model}", str(target / file)).replace("{ctx}", str(ctx))
|
||||
|
||||
Reference in New Issue
Block a user