Download onw/dl.py from ryugyosoft/onwAMD: direct link, hf CLI and curl.
- Browser
- Download file 5.98 kB
-
https://huggingface.co/ryugyosoft/onwAMD/resolve/main/onw/dl.py
- Command line
-
hf download hf://ryugyosoft/onwAMD/onw/dl.py
-
curl -L -o dl.py https://huggingface.co/ryugyosoft/onwAMD/resolve/main/onw/dl.py
5.98 kB
| """Download one model (or just FastFlowLM) as its own process: the manager can cancel it by ending the process, and | |
| a crash inside a download cannot take the tray down. Progress goes to a small JSON file the manager reads. | |
| usage (internal): python -m onw.dl KEY STATUS_FILE a model ("...-FLM"): FastFlowLM first when missing, | |
| `flm pull`, then every file checked against flm's hashes | |
| python -m onw.dl --repair KEY STATUS_FILE the model's damaged files fetched again (after a check) | |
| python -m onw.dl --verify KEY STATUS_FILE just the check; the result (damaged files) in STATUS_FILE | |
| python -m onw.dl --engine STATUS_FILE just FastFlowLM | |
| """ | |
| import json, os, re, subprocess, sys, time | |
| from . import app, lemonade | |
| class Status: | |
| def __init__(self, path): | |
| self.path, self.last = path, 0.0 | |
| def __call__(self, frac, text, force=False, **extra): | |
| if not force and time.time() - self.last < 0.5: | |
| return | |
| self.last = time.time() | |
| with open(self.path + ".tmp", "w", encoding="utf8") as f: | |
| json.dump({"frac": round(frac, 4), "text": text, **extra}, f, ensure_ascii=False) | |
| os.replace(self.path + ".tmp", self.path) | |
| def engine(status): | |
| if not lemonade.up_to_date(): | |
| lemonade.install(status) | |
| status(1.0, "FastFlowLM " + lemonade.VERSION, force=True) | |
| def pull(m, status, cfg, force=False): | |
| """`flm pull <checkpoint>`; progress from its "[FLM] Downloading X/Y: file" lines (lemonade: flm_download).""" | |
| p = subprocess.Popen(lemonade.flm_pull_command(m["checkpoint"], force), stdout=subprocess.PIPE, | |
| stderr=subprocess.STDOUT, env=lemonade.flm_env(cfg), text=True, encoding="utf8", | |
| errors="replace", **lemonade.NO_WINDOW) | |
| # flm prints "Downloading 2/6: model.q4nx" per file and, with \r, "Downloading: 44.6% (85.7MB / 191.9MB)" while a | |
| # file comes down: the bytes give the progress (one big weights file would otherwise sit at 0% for minutes) | |
| file_pat = re.compile(r"Downloading\s+(\d+)/(\d+):\s*(.*)") | |
| byte_pat = re.compile(r"Downloading:\s*([\d.]+)%\s*\(([\d.]+)\s*([KMGT]?B)\s*/\s*([\d.]+)\s*([KMGT]?B)\)") | |
| unit = {"B": 1, "KB": 2**10, "MB": 2**20, "GB": 2**30, "TB": 2**40} | |
| total = lemonade.flm_download_size(m["checkpoint"]) # the files' sizes (the footprint is memory, not download) | |
| done = cur = cur_total = 0.0 # bytes of the finished files, of the current one | |
| i = n = 0 | |
| name = "" | |
| def report(): | |
| whole = max(total, done + cur_total, 1) | |
| frac = min((done + cur) / whole, 0.99) | |
| status(frac, f"{(done + cur) / 2**30:.1f} / {whole / 2**30:.1f} GB ({frac * 100:.0f}%)" | |
| + (f" · {i}/{n} {name[:40]}" if n else "")) | |
| for line in p.stdout: | |
| print(line, end="", flush=True) | |
| line = re.sub(r"\x1b\[[0-9;?]*[A-Za-z]", "", line) | |
| g = file_pat.search(line) | |
| if g: | |
| done, cur, cur_total = done + cur_total, 0.0, 0.0 # the previous file is complete | |
| i, n, name = int(g.group(1)), int(g.group(2)), g.group(3).strip() | |
| report() | |
| continue | |
| g = byte_pat.search(line) | |
| if g: | |
| cur = float(g.group(2)) * unit.get(g.group(3).upper(), 1) | |
| cur_total = float(g.group(4)) * unit.get(g.group(5).upper(), 1) | |
| report() | |
| if p.wait() != 0: | |
| sys.exit(f"flm pull failed (exit code {p.returncode})") | |
| def verify(m, status, cfg): | |
| """The model's files against flm's hashes. -> damaged file names ([] intact, None: no hashes to check).""" | |
| status(0.0, "checking…", force=True) | |
| bad = lemonade.flm_verify(m["checkpoint"], cfg, | |
| lambda frac, text: status(frac, f"checking {text} ({frac * 100:.0f}%)")) | |
| print(f"[onw] verify {m['checkpoint']}: " + ("no hashes" if bad is None else ", ".join(bad) or "intact"), flush=True) | |
| return bad | |
| def repair(m, bad, status, cfg): | |
| """Fetch the damaged files again: remove them so that `flm pull` downloads them; if flm still counts the model | |
| as complete, pull it whole with --force. Exits when the files are still damaged.""" | |
| for name, path, *_ in lemonade.flm_files(m["checkpoint"], cfg): | |
| if name in bad and os.path.exists(path): | |
| os.remove(path) | |
| pull(m, status, cfg) | |
| bad = verify(m, status, cfg) | |
| if bad: | |
| pull(m, status, cfg, force=True) | |
| bad = verify(m, status, cfg) | |
| if bad: | |
| sys.exit("downloaded files are damaged (checked against FastFlowLM's hashes): " + ", ".join(bad)) | |
| def model_entry(key, cfg): | |
| ok, msg = lemonade.flm_validate(cfg) | |
| if not ok: | |
| sys.exit(f"FastFlowLM NPU check failed: {msg}") | |
| m = lemonade.flm_models(cfg).get(key) | |
| if m is None: | |
| sys.exit(f"FastFlowLM has no model {key}") | |
| return m | |
| def main(): | |
| args = sys.argv[1:] | |
| if args[0] == "--engine": | |
| engine(Status(args[1])) | |
| return | |
| if args[0] in ("--verify", "--repair"): | |
| mode, key, status = args[0], args[1], Status(args[2]) | |
| else: | |
| mode, key, status = "--download", args[0], Status(args[1]) | |
| cfg = app.load_config() | |
| status(0.0, "…", force=True) | |
| if mode == "--verify": | |
| m = lemonade.flm_models(cfg).get(key) | |
| if m is None: | |
| sys.exit(f"FastFlowLM has no model {key}") | |
| bad = verify(m, status, cfg) | |
| status(1.0, "done", force=True, bad=bad) | |
| return | |
| engine(lambda frac, text, force=False: status(0.0, text, force)) | |
| m = model_entry(key, cfg) | |
| if mode == "--download": | |
| pull(m, status, cfg) | |
| bad = verify(m, status, cfg) # (--repair: finds the damaged files again, here) | |
| if bad: | |
| repair(m, bad, status, cfg) | |
| status(1.0, "done", force=True, bad=[]) | |
| if __name__ == "__main__": | |
| main() | |