Download scripts/deploy_space.py from FineEnvs/DesignGym: direct link, hf CLI and curl.
- Browser
- Download file 6.23 kB
-
https://huggingface.co/spaces/FineEnvs/DesignGym/resolve/main/scripts/deploy_space.py
- Command line
-
hf download hf://spaces/FineEnvs/DesignGym/scripts/deploy_space.py
-
curl -L -o deploy_space.py https://huggingface.co/spaces/FineEnvs/DesignGym/resolve/main/scripts/deploy_space.py
6.23 kB
| """Publish the environment as a Hugging Face Space that serves the full corpus. | |
| The Space image holds code and the corpus manifest only. The catalog is copied from the bucket at startup, and | |
| each design's pixels are read from it the first time an episode uses it: over HTTP by default, or with | |
| --transport mount from the bucket attached read-only at /corpus. | |
| python scripts/deploy_space.py --space FineEnvs/DesignGym \\ | |
| --manifest corpus-manifest.json --private | |
| Refuses to deploy unless the manifest's catalog is published in the bucket | |
| with the exact size recorded in the manifest. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import shutil | |
| import sys | |
| import tempfile | |
| from pathlib import Path | |
| HERE = Path(__file__).resolve().parents[1] | |
| IGNORE = shutil.ignore_patterns(".venv", "__pycache__", ".pytest_cache", ".ruff_cache", "*.egg-info", | |
| "tests", "artifacts") | |
| def main(argv=None) -> int: | |
| from huggingface_hub import HfApi, Volume | |
| ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| ap.add_argument("--space", required=True, help="e.g. FineEnvs/DesignGym") | |
| ap.add_argument("--manifest", default=str(HERE / "corpus-manifest.json")) | |
| ap.add_argument("--hardware", default="cpu-upgrade", help="Space hardware (GUI episodes run Chromium)") | |
| ap.add_argument("--private", action="store_true") | |
| ap.add_argument("--max-envs", type=int, default=32, help="concurrent environment sessions per worker") | |
| ap.add_argument("--workers", type=int, default=4, help="server processes behind the Space's one URL") | |
| ap.add_argument("--reward", default="3", help="DESIGN_CANVAS_REWARD (3 = reward v3, as the evals use)") | |
| ap.add_argument("--daytona-desktops", type=int, default=0, | |
| help="run the real apps on Daytona with this many desktops (0 = no real apps); the key and URL " | |
| "are copied from DAYTONA_API_KEY / DAYTONA_API_URL into the Space's secrets") | |
| ap.add_argument("--transport", choices=["http", "mount"], default="http", | |
| help="read designs over HTTP (default) or from the bucket mounted at /corpus; the mount returned " | |
| "I/O errors under the workers' random reads (2026-10-05)") | |
| args = ap.parse_args(argv) | |
| manifest = json.loads(Path(args.manifest).read_text()) | |
| bucket = manifest["bucket_id"] | |
| api = HfApi() | |
| catalog = manifest["catalog"] | |
| found = {r.path: r.size for r in api.get_bucket_paths_info(bucket, [catalog["path"]])} | |
| if found.get(catalog["path"]) != catalog["size"]: | |
| sys.exit(f"publish the index first: {catalog['path']} is not in {bucket} at the recorded size") | |
| with tempfile.TemporaryDirectory(prefix="design-canvas-space-") as tmp: | |
| staging = Path(tmp) | |
| shutil.copytree(HERE, staging, dirs_exist_ok=True, ignore=IGNORE) | |
| shutil.copyfile(args.manifest, staging / "corpus-manifest.json") | |
| with (staging / "README.md").open("a") as card: | |
| n = sum(manifest["splits"].values()) | |
| card.write("\n## Served corpus\n\n") | |
| card.write(f"{n:,} Crello designs ({', '.join(f'{k} {v:,}' for k, v in manifest['splits'].items())}), " | |
| f"read on demand from the [{bucket}](https://huggingface.co/buckets/{bucket}) bucket " | |
| f"mounted at `/corpus`. Snapshot `{manifest['snapshot_id'][:16]}`.\n") | |
| api.create_repo(args.space, repo_type="space", space_sdk="docker", private=args.private, | |
| exist_ok=True, space_hardware=args.hardware) | |
| commit = api.upload_folder(repo_id=args.space, repo_type="space", folder_path=staging, | |
| commit_message=f"Serve Crello corpus {manifest['snapshot_id'][:12]} " | |
| "from the mounted bucket") | |
| runtime = api.space_info(args.space).runtime | |
| volumes = [v for v in (runtime.volumes or []) if v.mount_path != "/corpus"] if runtime else [] | |
| if args.transport == "mount": | |
| volumes.append(Volume(type="bucket", source=bucket, mount_path="/corpus", read_only=True)) | |
| if volumes: | |
| api.set_space_volumes(args.space, volumes=volumes) | |
| elif runtime and runtime.volumes: | |
| api.delete_space_volumes(args.space) # the Hub refuses an empty list | |
| current = api.get_space_variables(args.space) | |
| if args.transport == "http" and "DESIGN_CANVAS_SOURCE_ROOT" in current: | |
| api.delete_space_variable(args.space, "DESIGN_CANVAS_SOURCE_ROOT") | |
| for key, value in {"MAX_CONCURRENT_ENVS": str(args.max_envs), | |
| "DESIGN_CANVAS_WORKERS": str(args.workers), | |
| "DESIGN_CANVAS_REWARD": args.reward, | |
| **({"DESIGN_CANVAS_SOURCE_ROOT": "/corpus"} if args.transport == "mount" else {}), | |
| "DESIGN_CANVAS_PUBLIC_URL": f"https://{args.space.replace('/', '-').lower()}.hf.space"}.items(): | |
| if key not in current or current[key].value != value: | |
| api.add_space_variable(args.space, key, value) | |
| if args.daytona_desktops > 0: # the Space's own pool, so it never takes a local server's desktops | |
| import os | |
| for key, value in {"DESIGN_CANVAS_APPS": "daytona", "DESIGN_CANVAS_DAYTONA_POOL": "space", | |
| "DESIGN_CANVAS_DAYTONA_DESKTOPS": str(args.daytona_desktops)}.items(): | |
| if key not in current or current[key].value != value: | |
| api.add_space_variable(args.space, key, value) | |
| for key in ("DAYTONA_API_KEY", "DAYTONA_API_URL"): | |
| if os.environ.get(key): | |
| api.add_space_secret(args.space, key, os.environ[key]) | |
| elif "DESIGN_CANVAS_APPS" in current: | |
| api.delete_space_variable(args.space, "DESIGN_CANVAS_APPS") | |
| print(json.dumps({"space": args.space, "commit": commit.oid, "snapshot_id": manifest["snapshot_id"], | |
| "bucket": bucket, "transport": args.transport, "daytona_desktops": args.daytona_desktops, | |
| "url": f"https://huggingface.co/spaces/{args.space}"}, indent=2)) | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |