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:
@@ -6,6 +6,7 @@ zeilenweisem Log-Capture. Keine Persistenz, kein Broker. Genutzt von allen
|
||||
Routern, die laenger laufende Shell-Befehle anstossen (Download, Update, ...).
|
||||
"""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
@@ -102,6 +103,38 @@ def _run_job(job_id: str, args: list[str], env: dict | None = None, stdin_data:
|
||||
job["finished_at"] = time.time()
|
||||
|
||||
|
||||
def attach_download_progress(job_id: str, local_dir: str, total_bytes: int) -> None:
|
||||
"""Echten Download-Fortschritt (in %) auf den Job legen. `hf` gibt im Nicht-TTY-
|
||||
Modus keinen Fortschritt aus, schreibt aber in <local_dir>/.cache/huggingface/
|
||||
download/*.incomplete (waechst). Wir vergleichen dessen Groesse mit total_bytes
|
||||
(aus der HF-Tree-API). Ein Daemon-Thread aktualisiert job["progress"]."""
|
||||
if not total_bytes or total_bytes <= 0:
|
||||
return
|
||||
job = JOBS.get(job_id)
|
||||
if job is not None:
|
||||
job["progress"] = 0
|
||||
|
||||
def _watch():
|
||||
pat = os.path.join(local_dir, ".cache", "huggingface", "download", "*.incomplete")
|
||||
while True:
|
||||
j = JOBS.get(job_id)
|
||||
if not j or j["state"] in ("done", "failed", "canceled"):
|
||||
break
|
||||
try:
|
||||
inc = glob.glob(pat)
|
||||
cur = sum(os.path.getsize(f) for f in inc) if inc else 0
|
||||
if cur:
|
||||
j["progress"] = min(99, int(cur * 100 / total_bytes))
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
time.sleep(1.0)
|
||||
j = JOBS.get(job_id)
|
||||
if j and j["state"] == "done":
|
||||
j["progress"] = 100
|
||||
|
||||
threading.Thread(target=_watch, daemon=True).start()
|
||||
|
||||
|
||||
def cancel_job(job_id: str) -> bool:
|
||||
"""Laufenden Job abbrechen: Prozess terminieren. Liefert False, wenn der Job
|
||||
nicht (mehr) laeuft oder unbekannt ist."""
|
||||
|
||||
Reference in New Issue
Block a user