Download cli.py from FineEnvs/DesignGym: direct link, hf CLI and curl.
- Browser
- Download file 13.4 kB
-
https://huggingface.co/spaces/FineEnvs/DesignGym/resolve/main/cli.py
- Command line
-
hf download hf://spaces/FineEnvs/DesignGym/cli.py
-
curl -L -o cli.py https://huggingface.co/spaces/FineEnvs/DesignGym/resolve/main/cli.py
13.4 kB
| """`design-canvas` command line. | |
| design-canvas prepare [--train 200 --validation 60 --test 100] build the task pack | |
| design-canvas serve [--port 8000] [--workers N] OpenEnv server + editor (N processes, one URL) | |
| design-canvas mcp stdio MCP server for Claude & co. | |
| design-canvas export-sft --split train --out sft.jsonl expert trajectories | |
| design-canvas export-hf --cua artifacts/cua/cua.jsonl --out artifacts/hf/cua cua-v1 dataset (Hub-ready) | |
| design-canvas smoke offline end-to-end check | |
| design-canvas corpus build --out DIR index all of FineEnvs/crello-bucket | |
| design-canvas corpus publish --out DIR upload the index next to the data | |
| design-canvas corpus stats what the served corpus holds | |
| design-canvas evalset build --out FILE freeze the stratified evaluation set | |
| design-canvas evalset verify FILE check a frozen set against the corpus | |
| design-canvas evalset export FILE --out DIR write it as an offline task pack | |
| design-canvas evalset subset FILE --name N --size K [--within lite] nest a smaller subset in the set | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import sys | |
| import time | |
| from pathlib import Path | |
| def _pack_arg(p: argparse.ArgumentParser): | |
| p.add_argument("--pack", default=os.environ.get("DESIGN_CANVAS_PACK"), | |
| help="task pack directory (default ~/.cache/design_canvas/pack)") | |
| def cmd_prepare(args) -> int: | |
| from huggingface_hub import hf_hub_download | |
| from .core.pack import CRELLO_REPO, CRELLO_REVISION, DEFAULT_PACK, build_pack | |
| out = Path(args.pack or DEFAULT_PACK).expanduser() | |
| def dl(name): | |
| return hf_hub_download(CRELLO_REPO, name, repo_type="dataset", revision=CRELLO_REVISION) | |
| shards = {"train": "data/train-{:05d}-of-00031.parquet", | |
| "validation": "data/validation-{:05d}-of-00003.parquet", | |
| "test": "data/test-{:05d}-of-00004.parquet"} | |
| per = {"train": args.train, "validation": args.validation, "test": args.test} | |
| sources = {} | |
| for split, pattern in shards.items(): | |
| if per[split] <= 0: | |
| continue | |
| # Each shard holds ~600 templates; about 60% pass the filters. | |
| need = max(1, min(args.max_shards, 1 + per[split] // 350)) | |
| sources[split] = [dl(pattern.format(i)) for i in range(need)] | |
| print(f"{split}: {len(sources[split])} shard(s)") | |
| t = time.time() | |
| manifest = build_pack(out, sources, per, dl("resources/fonts.pickle"), | |
| max_elements=args.max_elements) | |
| print(json.dumps(manifest, indent=2)) | |
| print(f"pack ready at {out} in {time.time() - t:.0f}s") | |
| return 0 | |
| def cmd_serve(args) -> int: | |
| if args.pack: | |
| os.environ["DESIGN_CANVAS_PACK"] = str(Path(args.pack).expanduser()) | |
| os.environ["PORT"] = str(args.port) | |
| from .server.app import main | |
| print(f"DesignGym on http://{args.host}:{args.port} " | |
| f"(editor /editor/, playground /web/, MCP /mcp)") | |
| main(host=args.host, port=args.port, workers=args.workers) | |
| return 0 | |
| def cmd_mcp(args) -> int: | |
| if args.pack: | |
| os.environ["DESIGN_CANVAS_PACK"] = str(Path(args.pack).expanduser()) | |
| from .server.stdio_mcp import run | |
| run(editor_port=args.editor_port) | |
| return 0 | |
| def cmd_export_sft(args) -> int: | |
| from .core.pack import load_pack | |
| from .train_data import export | |
| pack = load_pack(args.pack) | |
| n = export(pack, args.split, Path(args.out), surfaces=args.surfaces.split(","), | |
| modes=args.modes.split(","), limit=args.limit) | |
| print(f"wrote {n} trajectories to {args.out}") | |
| return 0 | |
| def cmd_export_cua(args) -> int: | |
| from .core.pack import load_pack | |
| from .cua import export | |
| summary = export(load_pack(args.pack), args.split, Path(args.out), limit=args.limit, start=args.start, | |
| modes=tuple(args.modes.split(",")), seed=args.seed, workers=args.workers, | |
| server=args.server, max_elements=args.max_elements, image_format=args.image_format) | |
| print(json.dumps(summary, indent=2)) | |
| return 0 | |
| def cmd_export_hf(args) -> int: | |
| from .cua_format import Window, export_hf | |
| summary = export_hf(Path(args.cua), Path(args.out), image_format=args.image_format, min_reward=args.min_reward, | |
| window=Window(args.max_images, args.keep_images), shard_mb=args.shard_mb, | |
| with_windows=args.windows) | |
| print(json.dumps(summary, indent=2)) | |
| return 0 | |
| def cmd_smoke(args) -> int: | |
| from .smoke import run | |
| return run(args.pack) | |
| def _corpus(args): | |
| from .core.corpus import CorpusPack | |
| manifest = args.corpus or os.environ.get("DESIGN_CANVAS_CORPUS") | |
| if not manifest: | |
| raise SystemExit("set --corpus or DESIGN_CANVAS_CORPUS to a corpus manifest") | |
| return CorpusPack(manifest) | |
| def cmd_corpus(args) -> int: | |
| from .core import corpus | |
| if args.action == "build": | |
| source = corpus.BucketSource(args.bucket, args.source_root) | |
| m = corpus.build_index(args.out, source, max_elements=args.max_elements, workers=args.workers) | |
| print(json.dumps({k: m[k] for k in ("snapshot_id", "splits", "rejected", "difficulty")}, indent=2)) | |
| elif args.action == "publish": | |
| print(corpus.publish_index(args.out, args.bucket)) | |
| elif args.action == "warm": | |
| pack = _corpus(args) | |
| out = pack.warm(args.splits.split(","), workers=min(args.workers, 8), | |
| progress=lambda g, n, d: print(f" {g}/{n} row groups, {d} designs", flush=True)) | |
| out["failed"] = out["failed"][:20] | |
| print(json.dumps(out, indent=2)) | |
| return 1 if out["failed"] else 0 | |
| else: | |
| pack = _corpus(args) | |
| print(json.dumps({**pack.stats(), "splits": pack.manifest["splits"], | |
| "rejected": pack.manifest["rejected"]}, indent=2)) | |
| return 0 | |
| def cmd_evalset(args) -> int: | |
| from .core import evalset as ev | |
| pack = _corpus(args) | |
| if args.action == "build": | |
| record = ev.build(pack, split=args.split, size=args.size, lite=args.lite, seed=args.seed, | |
| validate=not args.no_validate) | |
| ev.save(record, args.out) | |
| print(json.dumps({"evalset_id": record["evalset_id"], **record["summary"], | |
| "lite": ev.summarize(record, "lite"), "failures": record["failures"]}, indent=2)) | |
| return 1 if record["failures"] else 0 | |
| record = ev.load(args.file, pack.snapshot_id) | |
| ev.check_against(record, pack) | |
| if args.action == "calibrate": | |
| from .core.difficulty import calibrate | |
| rewards: dict[str, list[float]] = {} | |
| for trace in args.trace: | |
| for line in Path(trace).read_text().splitlines(): | |
| r = json.loads(line) | |
| if r.get("evalset_task") and not r.get("error"): | |
| rewards.setdefault(r["evalset_task"], []).append(r["reward"] or 0.0) | |
| ids = {e["task_id"] for e in record["tasks"]} | |
| rows = [r for r in pack.rows(record["split"]) if r["task_id"] in ids] | |
| print(json.dumps(calibrate(rows, rewards, pack.manifest["difficulty"]["weights"]), indent=2)) | |
| return 0 | |
| if args.action == "export": | |
| print(json.dumps(ev.export_pack(pack, record, args.out), indent=2)) | |
| elif args.action == "subset": | |
| ev.add_subset(record, args.name, args.size, args.within) | |
| ev.save(record, args.file) | |
| print(json.dumps({"evalset_id": record["evalset_id"], "subset": args.name, | |
| **ev.summarize(record, args.name), "task_ids": record["subsets"][args.name]}, indent=2)) | |
| else: | |
| print(json.dumps({"evalset_id": record["evalset_id"], "ok": True, **record["summary"]}, indent=2)) | |
| return 0 | |
| def main(argv=None) -> int: | |
| parser = argparse.ArgumentParser(prog="design-canvas", description=__doc__, | |
| formatter_class=argparse.RawDescriptionHelpFormatter) | |
| sub = parser.add_subparsers(dest="cmd", required=True) | |
| p = sub.add_parser("prepare", help="build the task pack from Crello") | |
| _pack_arg(p) | |
| p.add_argument("--train", type=int, default=200) | |
| p.add_argument("--validation", type=int, default=60) | |
| p.add_argument("--test", type=int, default=100) | |
| p.add_argument("--max-elements", type=int, default=20) | |
| p.add_argument("--max-shards", type=int, default=4) | |
| p.set_defaults(fn=cmd_prepare) | |
| p = sub.add_parser("serve", help="run the OpenEnv server with the editor") | |
| _pack_arg(p) | |
| p.add_argument("--host", default="127.0.0.1") | |
| p.add_argument("--port", type=int, default=int(os.environ.get("PORT", "8007"))) | |
| p.add_argument("--workers", type=int, default=int(os.environ.get("DESIGN_CANVAS_WORKERS", "1")), | |
| help="server processes behind one URL (a session-aware router); 1 = a single process") | |
| p.set_defaults(fn=cmd_serve) | |
| p = sub.add_parser("mcp", help="stdio MCP server (e.g. claude mcp add design-canvas -- ...)") | |
| _pack_arg(p) | |
| p.add_argument("--editor-port", type=int, default=8007, | |
| help="also serve the live editor on this port (0 to disable)") | |
| p.set_defaults(fn=cmd_mcp) | |
| p = sub.add_parser("export-sft", help="write expert trajectories as chat JSONL") | |
| _pack_arg(p) | |
| p.add_argument("--split", default="train") | |
| p.add_argument("--out", default="sft.jsonl") | |
| p.add_argument("--surfaces", default="tools,html") | |
| p.add_argument("--modes", default="reference,description") | |
| p.add_argument("--limit", type=int, default=None) | |
| p.set_defaults(fn=cmd_export_sft) | |
| p = sub.add_parser("export-cua", help="record verified computer-use trajectories (screenshots + actions)") | |
| _pack_arg(p) | |
| p.add_argument("--split", default="train") | |
| p.add_argument("--out", default="artifacts/cua") | |
| p.add_argument("--limit", type=int, default=20) | |
| p.add_argument("--start", type=int, default=0) | |
| p.add_argument("--modes", default="reference,description") | |
| p.add_argument("--seed", type=int, default=0) | |
| p.add_argument("--workers", type=int, default=2) | |
| p.add_argument("--max-elements", type=int, default=12) | |
| p.add_argument("--image-format", default="webp", choices=["webp", "png", "jpeg"]) | |
| p.add_argument("--server", default=None, help="a running server with DESIGN_CANVAS_DEBUG=1; default in-process") | |
| p.set_defaults(fn=cmd_export_cua) | |
| p = sub.add_parser("export-hf", help="write recorded trajectories as a cua-v1 Hugging Face dataset folder") | |
| p.add_argument("--cua", required=True, help="the recorder's cua.jsonl (images next to it)") | |
| p.add_argument("--out", required=True) | |
| p.add_argument("--image-format", default="keep", choices=["keep", "png", "jpeg"], | |
| help="keep = the recorder's format (WebP), smallest") | |
| p.add_argument("--min-reward", type=float, default=0.98) | |
| p.add_argument("--max-images", type=int, default=8) | |
| p.add_argument("--keep-images", type=int, default=3) | |
| p.add_argument("--shard-mb", type=int, default=500) | |
| p.add_argument("--windows", action="store_true", help="also store the SFT windows view (duplicates screenshots)") | |
| p.set_defaults(fn=cmd_export_hf) | |
| p = sub.add_parser("smoke", help="offline end-to-end check") | |
| _pack_arg(p) | |
| p.set_defaults(fn=cmd_smoke) | |
| p = sub.add_parser("corpus", help="index, publish or inspect the bucket-backed corpus") | |
| p.add_argument("action", choices=["build", "publish", "stats", "warm"]) | |
| p.add_argument("--out", default="artifacts/corpus-index", help="index directory (build, publish)") | |
| p.add_argument("--bucket", default="FineEnvs/crello-bucket") | |
| p.add_argument("--source-root", default=None, help="local copy or mount of the bucket") | |
| p.add_argument("--max-elements", type=int, default=30) | |
| p.add_argument("--workers", type=int, default=8) | |
| p.add_argument("--corpus", default=None, help="corpus manifest (stats, warm)") | |
| p.add_argument("--splits", default="train,validation,test", help="splits to download ahead of time (warm)") | |
| p.set_defaults(fn=cmd_corpus) | |
| p = sub.add_parser("evalset", help="build, verify or export the frozen evaluation set") | |
| p.add_argument("action", choices=["build", "verify", "export", "calibrate", "subset"]) | |
| p.add_argument("file", nargs="?", help="frozen set (verify, export, subset)") | |
| p.add_argument("--name", help="subset name (subset)") | |
| p.add_argument("--within", default="lite", help="subset to nest inside (subset)") | |
| p.add_argument("--out", help="output file (build) or pack directory (export)") | |
| p.add_argument("--corpus", default=None, help="corpus manifest (default DESIGN_CANVAS_CORPUS)") | |
| p.add_argument("--split", default="test") | |
| p.add_argument("--size", type=int, default=300, help="set size (build) or subset size (subset)") | |
| p.add_argument("--lite", type=int, default=60) | |
| p.add_argument("--seed", type=int, default=42) | |
| p.add_argument("--no-validate", action="store_true") | |
| p.add_argument("--trace", action="append", default=[], help="rollout trace.jsonl (calibrate)") | |
| p.set_defaults(fn=cmd_evalset) | |
| args = parser.parse_args(argv) | |
| return args.fn(args) | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |