Spaces:
Running
Running
Download datainventor/cli.py from codelion/data-inventor: direct link, hf CLI and curl.
- Browser
- Download file 4.95 kB
-
https://huggingface.co/spaces/codelion/data-inventor/resolve/main/datainventor/cli.py
- Command line
-
hf download hf://spaces/codelion/data-inventor/datainventor/cli.py
-
curl -L -o cli.py https://huggingface.co/spaces/codelion/data-inventor/resolve/main/datainventor/cli.py
4.95 kB
| """datainventor: curate a pretraining corpus on your own machines. | |
| datainventor init recipe.yaml write a starter recipe to edit | |
| datainventor preview recipe.yaml run every stage on a small sample and print what each did | |
| datainventor run recipe.yaml --out ./curated run it in full (resumable) | |
| The model endpoint is any OpenAI-compatible server: vLLM, SGLang, Ollama, a hosted API. Set its key in the | |
| environment variable named by endpoint.api_key_env (DI_API_KEY by default). | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import sys | |
| from .compose import fmt | |
| from .recipe import DEFAULT, dump, endpoint_of, load | |
| def main(argv=None): | |
| ap = argparse.ArgumentParser(prog="datainventor", description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| sub = ap.add_subparsers(dest="cmd", required=True) | |
| a = sub.add_parser("init", help="write a starter recipe") | |
| a.add_argument("path") | |
| a = sub.add_parser("preview", help="run every stage on a sample") | |
| a.add_argument("recipe") | |
| a.add_argument("--per-source", type=int, default=300) | |
| a.add_argument("--grade", type=int, default=64) | |
| a.add_argument("--rephrase", type=int, default=6) | |
| a.add_argument("--workers", type=int, default=None) | |
| a.add_argument("--json", help="also write the full report here") | |
| a = sub.add_parser("run", help="run the recipe in full") | |
| a.add_argument("recipe") | |
| a.add_argument("--out", default=None, help="output folder (default: output.dir in the recipe)") | |
| a.add_argument("--procs", type=int, default=0, help="CPU processes for cleaning and scoring (default: all but one)") | |
| a.add_argument("--workers", type=int, default=None, help="parallel model calls (default: endpoint.workers)") | |
| a.add_argument("--stages", default="grade,curate,create,compose") | |
| a.add_argument("--limit", type=int, default=0, help="read at most this many documents per source (for a trial run)") | |
| args = ap.parse_args(argv) | |
| if args.cmd == "init": | |
| if os.path.exists(args.path): | |
| sys.exit(f"{args.path} exists; not overwriting it.") | |
| r = json.loads(json.dumps(DEFAULT)) | |
| r["sources"] = [ | |
| {"name": "finepdfs", "dataset": "codelion/finepdfs-1B", "kind": "textbook"}, | |
| {"name": "dclm", "dataset": "codelion/dclm-baseline-1B", "kind": "web"}, | |
| {"name": "fineweb-edu", "dataset": "codelion/fineweb-edu-1B", "kind": "educational"}, | |
| ] | |
| r["targets"]["description"] = "grade-school to university math and science reasoning" | |
| r["compose"]["mixes"][0]["budget_tokens"] = 2_000_000_000 | |
| with open(args.path, "w") as f: | |
| f.write(dump(r)) | |
| print(f"Wrote {args.path}. Edit the sources, targets and budget, then: datainventor preview {args.path}") | |
| return | |
| r = load(args.recipe) | |
| if args.cmd == "preview": | |
| from .preview import Preview, run_preview | |
| from .sources import inspect | |
| token = os.environ.get("HF_TOKEN", "") | |
| for s in r["sources"]: # row counts make the token estimates possible | |
| if s.get("dataset") and not s.get("num_rows"): | |
| try: | |
| s["num_rows"] = inspect(s["dataset"], token)["num_rows"] if (s.get("config", "default") == "default") else 0 | |
| except Exception: | |
| pass | |
| p = Preview(r) | |
| ep = endpoint_of(r, args.workers) | |
| run_preview(p, ep, token, per_source=args.per_source, grade_n=args.grade, rephrase_n=args.rephrase, invent_n=2) | |
| if p.state != "done": | |
| sys.exit(f"Preview {p.state}: {p.message}") | |
| rep = p.report | |
| for f in rep["sources"]: | |
| drops = ", ".join(f"{k} {v}" for k, v in sorted(f["dropped"].items(), key=lambda x: -x[1])) | |
| print(f"{f['name']}: read {f['read']}, selected {f['selected']}; dropped: {drops}; decontaminated: {f['decontaminated'] or 'none'}") | |
| if f.get("available_tokens"): | |
| print(f" about {fmt(f['available_tokens'])} tokens available after curation") | |
| c = rep["stages"]["calibrate"] | |
| print(f"graded {c['graded']}; scorer agreement {c['agreement']}") | |
| for plan in rep["stages"]["compose"]["plans"]: | |
| print(f"mix {plan['name']} ({plan['budget']}): " + "; ".join(f"{x['name']} {int(x['share'] * 100)}% x{x['epochs']}" for x in plan["rows"])) | |
| for w in plan["warnings"]: | |
| print(" - " + w) | |
| if args.json: | |
| with open(args.json, "w") as fh: | |
| json.dump({"report": rep, "docs": p.docs}, fh, indent=1, ensure_ascii=False) | |
| return | |
| from .runner import Run | |
| out = args.out or r["output"]["dir"] | |
| Run(r, out, procs=args.procs, workers=args.workers, limit=args.limit).go(tuple(args.stages.split(","))) | |
| print(f"Done. See {os.path.join(out, 'REPORT.md')}") | |
| if __name__ == "__main__": | |
| main() | |