Download src/autocad_bench/bundle.py from windows2t2/cad-bench: direct link, hf CLI and curl.
- Browser
- Download file 9.87 kB
-
https://huggingface.co/windows2t2/cad-bench/resolve/main/src/autocad_bench/bundle.py
- Command line
-
hf download hf://windows2t2/cad-bench/src/autocad_bench/bundle.py
-
curl -L -o bundle.py https://huggingface.co/windows2t2/cad-bench/resolve/main/src/autocad_bench/bundle.py
9.87 kB
| """Portable, integrity-bound benchmark data bundles.""" | |
| from __future__ import annotations | |
| import hashlib | |
| import os | |
| import re | |
| import shutil | |
| import tempfile | |
| from pathlib import Path, PurePosixPath | |
| from typing import Literal | |
| from pydantic import BaseModel, ConfigDict, Field, field_validator | |
| from autocad_bench.common.io import atomic_write_json | |
| from autocad_bench.common.paths import BENCHMARK_ROOT | |
| from autocad_bench.evaluation.scoring.gold_cache import GoldCacheStore | |
| from autocad_bench.tasks.audit import load_gold_audit | |
| from autocad_bench.tasks.manifest import load_manifest | |
| from autocad_bench.tasks.suite import load_benchmark_task_ids | |
| BUNDLE_FILENAME = "autocad-bench-bundle.json" | |
| BUNDLE_SCHEMA_VERSION = 1 | |
| BUNDLE_VERSION = "autocad-bench-corpus-v1" | |
| class BundleError(RuntimeError): | |
| """A benchmark bundle is incomplete, unsafe, or not reproducible.""" | |
| def _sha256_file(path: Path) -> str: | |
| digest = hashlib.sha256() | |
| with path.open("rb") as source: | |
| for chunk in iter(lambda: source.read(1024 * 1024), b""): | |
| digest.update(chunk) | |
| return digest.hexdigest() | |
| class BundleFile(BaseModel): | |
| model_config = ConfigDict(extra="forbid", frozen=True) | |
| path: str | |
| bytes: int = Field(ge=0) | |
| sha256: str = Field(pattern=r"^[a-f0-9]{64}$") | |
| def validate_path(cls, value: str) -> str: | |
| path = PurePosixPath(value) | |
| if ( | |
| path.is_absolute() | |
| or not path.parts | |
| or ".." in path.parts | |
| or value != path.as_posix() | |
| ): | |
| raise ValueError("bundle file paths must be normalized and relative") | |
| if value == BUNDLE_FILENAME: | |
| raise ValueError("bundle manifest cannot list itself") | |
| return value | |
| class BenchmarkBundle(BaseModel): | |
| model_config = ConfigDict(extra="forbid", frozen=True) | |
| schema_version: Literal[1] = BUNDLE_SCHEMA_VERSION | |
| bundle_version: Literal["autocad-bench-corpus-v1"] = BUNDLE_VERSION | |
| task_count: int = Field(gt=0) | |
| evaluator_versions: tuple[str, ...] = () | |
| files: tuple[BundleFile, ...] = Field(min_length=1) | |
| def validate_unique_files( | |
| cls, | |
| value: tuple[BundleFile, ...], | |
| ) -> tuple[BundleFile, ...]: | |
| paths = [entry.path for entry in value] | |
| if len(paths) != len(set(paths)): | |
| raise ValueError("bundle contains duplicate file paths") | |
| if paths != sorted(paths): | |
| raise ValueError("bundle files must be sorted by path") | |
| return value | |
| def validate_unique_versions(cls, value: tuple[str, ...]) -> tuple[str, ...]: | |
| if tuple(sorted(set(value))) != value: | |
| raise ValueError("evaluator_versions must be sorted and unique") | |
| if any(not re.fullmatch(r"[A-Za-z0-9_.-]+", item) for item in value): | |
| raise ValueError("evaluator_versions contains an unsafe value") | |
| return value | |
| def _source_files( | |
| root: Path, | |
| *, | |
| evaluator_versions: tuple[str, ...], | |
| ) -> tuple[Path, ...]: | |
| entries = load_manifest(root=root) | |
| load_gold_audit(root=root) | |
| load_benchmark_task_ids(root=root) | |
| relative_paths = { | |
| Path("tasks/README.md"), | |
| Path("tasks/manifest.jsonl"), | |
| Path("tasks/gold-audit.jsonl"), | |
| Path("tasks/benchmark-suite-50.txt"), | |
| } | |
| for entry in entries: | |
| relative_paths.add(Path(entry.image_path)) | |
| relative_paths.add(Path(entry.gold_path)) | |
| for evaluator_version in evaluator_versions: | |
| store = GoldCacheStore( | |
| evaluator_version=evaluator_version, | |
| root=root / "artifacts" / "gold-cache", | |
| ) | |
| expected_checksums = { | |
| entry.task_id: _sha256_file(entry.resolve_gold_path(root=root)) | |
| for entry in entries | |
| } | |
| try: | |
| store.verify( | |
| [entry.task_id for entry in entries], | |
| expected_source_sha256=expected_checksums, | |
| ) | |
| except Exception as exc: | |
| raise BundleError( | |
| f"gold cache is not release-ready for {evaluator_version}: {exc}" | |
| ) from exc | |
| for entry in entries: | |
| base = ( | |
| Path("artifacts") | |
| / "gold-cache" | |
| / evaluator_version | |
| / entry.task_id | |
| ) | |
| relative_paths.add(base / "metadata.json") | |
| relative_paths.add(base / "render.png") | |
| paths: list[Path] = [] | |
| for relative in sorted(relative_paths, key=lambda item: item.as_posix()): | |
| source = (root / relative).resolve() | |
| if not source.is_relative_to(root): | |
| raise BundleError(f"bundle source escapes root: {relative.as_posix()}") | |
| if source.is_symlink() or not source.is_file(): | |
| raise BundleError( | |
| f"bundle source must be a regular file: {relative.as_posix()}" | |
| ) | |
| paths.append(source) | |
| return tuple(paths) | |
| def export_bundle( | |
| output_root: Path | str, | |
| *, | |
| source_root: Path | str = BENCHMARK_ROOT, | |
| evaluator_versions: tuple[str, ...] = (), | |
| ) -> BenchmarkBundle: | |
| """Export an allowlisted benchmark directory and bind every byte.""" | |
| source = Path(source_root).expanduser().resolve() | |
| output = Path(output_root).expanduser().resolve() | |
| if output.exists(): | |
| raise BundleError(f"bundle output already exists: {output}") | |
| output.parent.mkdir(parents=True, exist_ok=True) | |
| versions = tuple(sorted(set(evaluator_versions))) | |
| files = _source_files(source, evaluator_versions=versions) | |
| staging = Path( | |
| tempfile.mkdtemp( | |
| prefix=f".{output.name}.staging-", | |
| dir=output.parent, | |
| ) | |
| ) | |
| try: | |
| manifest_files: list[BundleFile] = [] | |
| for source_path in files: | |
| relative = source_path.relative_to(source) | |
| destination = staging / relative | |
| destination.parent.mkdir(parents=True, exist_ok=True) | |
| shutil.copyfile(source_path, destination) | |
| manifest_files.append( | |
| BundleFile( | |
| path=relative.as_posix(), | |
| bytes=destination.stat().st_size, | |
| sha256=_sha256_file(destination), | |
| ) | |
| ) | |
| bundle = BenchmarkBundle( | |
| task_count=len(load_manifest(root=source)), | |
| evaluator_versions=versions, | |
| files=tuple(manifest_files), | |
| ) | |
| atomic_write_json( | |
| staging / BUNDLE_FILENAME, | |
| bundle.model_dump(mode="json"), | |
| ) | |
| os.replace(staging, output) | |
| return bundle | |
| except BaseException: | |
| shutil.rmtree(staging, ignore_errors=True) | |
| raise | |
| def validate_bundle(root: Path | str) -> BenchmarkBundle: | |
| """Verify byte integrity and the semantic task/evaluator contracts.""" | |
| bundle_root = Path(root).expanduser().resolve() | |
| manifest_path = bundle_root / BUNDLE_FILENAME | |
| if not manifest_path.is_file() or manifest_path.is_symlink(): | |
| raise BundleError(f"bundle manifest not found: {manifest_path}") | |
| try: | |
| bundle = BenchmarkBundle.model_validate_json( | |
| manifest_path.read_text(encoding="utf-8") | |
| ) | |
| except Exception as exc: | |
| raise BundleError(f"invalid bundle manifest: {exc}") from exc | |
| for candidate in bundle_root.rglob("*"): | |
| if candidate.is_symlink(): | |
| raise BundleError( | |
| "bundle must not contain symbolic links: " | |
| + candidate.relative_to(bundle_root).as_posix() | |
| ) | |
| expected_paths = {entry.path for entry in bundle.files} | {BUNDLE_FILENAME} | |
| actual_paths = { | |
| path.relative_to(bundle_root).as_posix() | |
| for path in bundle_root.rglob("*") | |
| if path.is_file() | |
| } | |
| if actual_paths != expected_paths: | |
| missing = sorted(expected_paths - actual_paths) | |
| unexpected = sorted(actual_paths - expected_paths) | |
| raise BundleError( | |
| f"bundle file inventory mismatch: missing={missing}, " | |
| f"unexpected={unexpected}" | |
| ) | |
| for entry in bundle.files: | |
| path = (bundle_root / entry.path).resolve() | |
| if not path.is_relative_to(bundle_root) or not path.is_file(): | |
| raise BundleError(f"bundle file is unavailable: {entry.path}") | |
| if path.stat().st_size != entry.bytes: | |
| raise BundleError(f"bundle file size changed: {entry.path}") | |
| if _sha256_file(path) != entry.sha256: | |
| raise BundleError(f"bundle file checksum changed: {entry.path}") | |
| entries = load_manifest(root=bundle_root) | |
| if len(entries) != bundle.task_count: | |
| raise BundleError( | |
| f"bundle task count changed: expected {bundle.task_count}, " | |
| f"found {len(entries)}" | |
| ) | |
| load_gold_audit(root=bundle_root) | |
| load_benchmark_task_ids(root=bundle_root) | |
| expected_checksums = { | |
| entry.task_id: _sha256_file(entry.resolve_gold_path(root=bundle_root)) | |
| for entry in entries | |
| } | |
| for evaluator_version in bundle.evaluator_versions: | |
| store = GoldCacheStore( | |
| evaluator_version=evaluator_version, | |
| root=bundle_root / "artifacts" / "gold-cache", | |
| ) | |
| try: | |
| store.verify( | |
| [entry.task_id for entry in entries], | |
| expected_source_sha256=expected_checksums, | |
| ) | |
| except Exception as exc: | |
| raise BundleError( | |
| f"invalid gold cache for {evaluator_version}: {exc}" | |
| ) from exc | |
| return bundle | |
| __all__ = [ | |
| "BUNDLE_FILENAME", | |
| "BUNDLE_SCHEMA_VERSION", | |
| "BUNDLE_VERSION", | |
| "BenchmarkBundle", | |
| "BundleError", | |
| "BundleFile", | |
| "export_bundle", | |
| "validate_bundle", | |
| ] | |