Download src/autocad_bench/cli.py from windows2t2/cad-bench: direct link, hf CLI and curl.
- Browser
- Download file 15.1 kB
-
https://huggingface.co/windows2t2/cad-bench/resolve/main/src/autocad_bench/cli.py
- Command line
-
hf download hf://windows2t2/cad-bench/src/autocad_bench/cli.py
-
curl -L -o cli.py https://huggingface.co/windows2t2/cad-bench/resolve/main/src/autocad_bench/cli.py
15.1 kB
| """Local validation commands for the AutoCAD benchmark corpus.""" | |
| from __future__ import annotations | |
| import argparse | |
| import asyncio | |
| import json | |
| import os | |
| from collections import Counter | |
| from collections.abc import Sequence | |
| from pathlib import Path | |
| from typing import Any | |
| from autocad_bench.bundle import export_bundle, validate_bundle | |
| from autocad_bench.common.paths import BENCHMARK_ROOT | |
| from autocad_bench.common.runtime_contract import DEFAULT_EVALUATOR_VERSION | |
| from autocad_bench.tasks.audit import load_gold_audit | |
| from autocad_bench.tasks.manifest import SplitSelector, load_manifest, load_public_tasks | |
| from autocad_bench.evaluation.scoring.evaluator import HttpDWGEvaluator | |
| from autocad_bench.evaluation.scoring.gold_cache import ( | |
| DEFAULT_GOLD_CACHE_ROOT, | |
| GoldCacheStore, | |
| build_gold_cache, | |
| ) | |
| from autocad_bench.evaluation.scoring.models import sha256_bytes | |
| from autocad_bench.evaluation.scoring.vision_judge import ( | |
| DEFAULT_VISION_JUDGE_MODEL, | |
| DEFAULT_VISION_JUDGE_REASONING, | |
| OpenAIVisionJudge, | |
| build_vision_evidence, | |
| load_evaluated_drawing, | |
| write_vision_judge_artifacts, | |
| ) | |
| def _validate() -> int: | |
| entries = load_manifest() | |
| summary = { | |
| "tasks": len(entries), | |
| "enabled": sum(entry.enabled for entry in entries), | |
| "dev": sum(entry.enabled and entry.split == "dev" for entry in entries), | |
| "eval": sum(entry.enabled and entry.split == "eval" for entry in entries), | |
| "levels": dict(Counter(entry.level for entry in entries)), | |
| "drawing_types": dict(Counter(entry.drawing_type for entry in entries)), | |
| "dwg_signature": "AC1032", | |
| "status": "ok", | |
| } | |
| print(json.dumps(summary, indent=2, sort_keys=True)) | |
| return 0 | |
| def _list_tasks(split: SplitSelector) -> int: | |
| tasks = load_public_tasks(split) | |
| print(json.dumps([task.model_dump(mode="json") for task in tasks], indent=2)) | |
| return 0 | |
| def _validate_audit() -> int: | |
| entries = load_gold_audit() | |
| summary = { | |
| "tasks": len(entries), | |
| "machine_checks_passed": sum(entry.machine_checks == "passed" for entry in entries), | |
| "reference_reviews_pending": sum( | |
| entry.reference_review == "pending" for entry in entries | |
| ), | |
| "autocad_reviews_pending": sum(entry.autocad_review == "pending" for entry in entries), | |
| "development_tasks": [ | |
| entry.task_id for entry in entries if entry.selected_for_dev | |
| ], | |
| "status": "ok", | |
| } | |
| print(json.dumps(summary, indent=2, sort_keys=True)) | |
| return 0 | |
| def _export_bundle(args: argparse.Namespace) -> int: | |
| if args.source_only and args.evaluator_version: | |
| raise SystemExit( | |
| "--source-only cannot be combined with --evaluator-version" | |
| ) | |
| evaluator_versions = ( | |
| () | |
| if args.source_only | |
| else tuple(args.evaluator_version or [DEFAULT_EVALUATOR_VERSION]) | |
| ) | |
| bundle = export_bundle( | |
| args.output, | |
| source_root=args.source_root, | |
| evaluator_versions=evaluator_versions, | |
| ) | |
| print( | |
| json.dumps( | |
| { | |
| "bundle_root": str(Path(args.output).expanduser().resolve()), | |
| "bundle_version": bundle.bundle_version, | |
| "tasks": bundle.task_count, | |
| "evaluator_versions": list(bundle.evaluator_versions), | |
| "files": len(bundle.files), | |
| "bytes": sum(entry.bytes for entry in bundle.files), | |
| "status": "ok", | |
| }, | |
| indent=2, | |
| sort_keys=True, | |
| ) | |
| ) | |
| return 0 | |
| def _validate_bundle(args: argparse.Namespace) -> int: | |
| bundle = validate_bundle(args.root) | |
| print( | |
| json.dumps( | |
| { | |
| "bundle_root": str(Path(args.root).expanduser().resolve()), | |
| "bundle_version": bundle.bundle_version, | |
| "tasks": bundle.task_count, | |
| "evaluator_versions": list(bundle.evaluator_versions), | |
| "files": len(bundle.files), | |
| "bytes": sum(entry.bytes for entry in bundle.files), | |
| "status": "ok", | |
| }, | |
| indent=2, | |
| sort_keys=True, | |
| ) | |
| ) | |
| return 0 | |
| def _build_gold_cache(args: argparse.Namespace) -> int: | |
| token = os.environ.get(args.token_env) | |
| if not token: | |
| raise SystemExit(f"required evaluator token is not set: {args.token_env}") | |
| evaluator = HttpDWGEvaluator( | |
| args.evaluator_url, | |
| token=token, | |
| evaluator_version=args.evaluator_version, | |
| timeout_s=args.timeout_s, | |
| ) | |
| store = GoldCacheStore( | |
| evaluator_version=args.evaluator_version, | |
| root=Path(args.root), | |
| ) | |
| async def run() -> tuple[str, ...]: | |
| await evaluator.health() | |
| return await build_gold_cache(evaluator, store, split=args.split) | |
| built = asyncio.run(run()) | |
| print( | |
| json.dumps( | |
| { | |
| "evaluator_version": args.evaluator_version, | |
| "split": args.split, | |
| "built": list(built), | |
| "count": len(built), | |
| "status": "ok", | |
| }, | |
| indent=2, | |
| sort_keys=True, | |
| ) | |
| ) | |
| return 0 | |
| def _verify_gold_cache(args: argparse.Namespace) -> int: | |
| selected = tuple( | |
| entry | |
| for entry in load_manifest() | |
| if entry.enabled and (args.split == "all" or entry.split == args.split) | |
| ) | |
| task_ids = tuple(entry.task_id for entry in selected) | |
| expected_checksums = { | |
| entry.task_id: sha256_bytes(entry.resolve_gold_path().read_bytes()) | |
| for entry in selected | |
| } | |
| store = GoldCacheStore( | |
| evaluator_version=args.evaluator_version, | |
| root=Path(args.root), | |
| ) | |
| verified = store.verify( | |
| task_ids, | |
| expected_source_sha256=expected_checksums, | |
| ) | |
| print( | |
| json.dumps( | |
| { | |
| "evaluator_version": args.evaluator_version, | |
| "split": args.split, | |
| "verified": list(verified), | |
| "count": len(verified), | |
| "status": "ok", | |
| }, | |
| indent=2, | |
| sort_keys=True, | |
| ) | |
| ) | |
| return 0 | |
| def _vision_judge(args: argparse.Namespace) -> int: | |
| api_key = os.environ.get(args.api_key_env) | |
| if not api_key: | |
| raise SystemExit(f"required OpenAI credential is not set: {args.api_key_env}") | |
| rollout_dir = Path(args.rollout_dir).expanduser().resolve() | |
| candidate_dwg_path = rollout_dir / "attempt.dwg" | |
| candidate_metadata_path = rollout_dir / "evaluation" / "metadata.json" | |
| candidate_render_path = rollout_dir / "evaluation" / "render.png" | |
| required = ( | |
| candidate_dwg_path, | |
| candidate_metadata_path, | |
| candidate_render_path, | |
| ) | |
| missing = [str(path) for path in required if not path.is_file()] | |
| if missing: | |
| raise SystemExit("missing rollout evaluation artifacts: " + ", ".join(missing)) | |
| entries = {entry.task_id: entry for entry in load_manifest()} | |
| try: | |
| entry = entries[args.task_id] | |
| except KeyError as exc: | |
| raise SystemExit(f"unknown task_id: {args.task_id}") from exc | |
| candidate = load_evaluated_drawing( | |
| candidate_metadata_path, candidate_render_path | |
| ) | |
| store = GoldCacheStore( | |
| evaluator_version=candidate.metadata.evaluator_version, | |
| root=Path(args.gold_cache_root), | |
| ) | |
| gold = store.load(args.task_id) | |
| candidate_dwg = candidate_dwg_path.read_bytes() | |
| gold_dwg_path = entry.resolve_gold_path() | |
| gold_dwg = gold_dwg_path.read_bytes() | |
| reference_path = entry.resolve_image_path() | |
| reference_png = reference_path.read_bytes() | |
| evidence = build_vision_evidence( | |
| task_id=args.task_id, | |
| units=entry.units, | |
| drawing_type=entry.drawing_type, | |
| candidate_dwg=candidate_dwg, | |
| gold_dwg=gold_dwg, | |
| reference_png=reference_png, | |
| candidate=candidate, | |
| gold=gold, | |
| ) | |
| image_artifacts = ( | |
| { | |
| "role": "reference_image", | |
| "artifact_path": str(reference_path), | |
| "sha256": sha256_bytes(reference_png), | |
| }, | |
| { | |
| "role": "gold_dwg_render", | |
| "artifact_path": str( | |
| Path(args.gold_cache_root) | |
| / candidate.metadata.evaluator_version | |
| / args.task_id | |
| / "render.png" | |
| ), | |
| "sha256": sha256_bytes(gold.render_png), | |
| }, | |
| { | |
| "role": "candidate_dwg_render", | |
| "artifact_path": str(candidate_render_path), | |
| "sha256": sha256_bytes(candidate.render_png), | |
| }, | |
| { | |
| "role": "candidate_dwg_binary_not_sent", | |
| "artifact_path": str(candidate_dwg_path), | |
| "sha256": sha256_bytes(candidate_dwg), | |
| }, | |
| { | |
| "role": "gold_dwg_binary_not_sent", | |
| "artifact_path": str(gold_dwg_path), | |
| "sha256": sha256_bytes(gold_dwg), | |
| }, | |
| ) | |
| judge = OpenAIVisionJudge( | |
| api_key=api_key, | |
| model_id=args.model, | |
| reasoning_effort=args.reasoning_effort, | |
| max_output_tokens=args.max_output_tokens, | |
| timeout_s=args.timeout_s, | |
| max_request_attempts=args.max_request_attempts, | |
| ) | |
| async def run() -> Any: | |
| return await judge.judge( | |
| task_id=args.task_id, | |
| candidate_dwg_sha256=sha256_bytes(candidate_dwg), | |
| gold_dwg_sha256=sha256_bytes(gold_dwg), | |
| evidence=evidence, | |
| reference_png=reference_png, | |
| gold_render_png=gold.render_png, | |
| candidate_render_png=candidate.render_png, | |
| sample_count=args.samples, | |
| image_artifacts=image_artifacts, | |
| ) | |
| judged = asyncio.run(run()) | |
| output_dir = ( | |
| Path(args.output_dir).expanduser().resolve() | |
| if args.output_dir | |
| else rollout_dir / "evaluation" / "vision-judge" | |
| ) | |
| score_path = write_vision_judge_artifacts(output_dir, judged) | |
| print( | |
| json.dumps( | |
| { | |
| "task_id": args.task_id, | |
| "model": args.model, | |
| "samples": args.samples, | |
| "overall_score": judged.result.overall_score, | |
| "verdict": judged.result.verdict, | |
| "target_acquired": judged.result.target_acquired, | |
| "confidence": judged.result.confidence, | |
| "needs_human_review": judged.result.needs_human_review, | |
| "score_path": str(score_path), | |
| }, | |
| indent=2, | |
| sort_keys=True, | |
| ) | |
| ) | |
| return 0 | |
| def build_parser() -> argparse.ArgumentParser: | |
| parser = argparse.ArgumentParser(prog="autocad-bench") | |
| subparsers = parser.add_subparsers(dest="command", required=True) | |
| subparsers.add_parser("validate", help="validate the complete source manifest") | |
| list_parser = subparsers.add_parser( | |
| "list", help="print model-safe public task rows" | |
| ) | |
| list_parser.add_argument("--split", choices=("dev", "eval", "all"), default="all") | |
| subparsers.add_parser( | |
| "validate-audit", help="validate the machine-level gold audit inventory" | |
| ) | |
| export_parser = subparsers.add_parser( | |
| "bundle-export", | |
| help="export a portable, checksum-bound benchmark data directory", | |
| ) | |
| export_parser.add_argument("--output", type=Path, required=True) | |
| export_parser.add_argument( | |
| "--source-root", | |
| type=Path, | |
| default=BENCHMARK_ROOT, | |
| help="checkout or bundle containing tasks/ and artifacts/", | |
| ) | |
| export_parser.add_argument( | |
| "--evaluator-version", | |
| action="append", | |
| help=( | |
| "gold-cache version to include; repeat as needed " | |
| f"(default: {DEFAULT_EVALUATOR_VERSION})" | |
| ), | |
| ) | |
| export_parser.add_argument( | |
| "--source-only", | |
| action="store_true", | |
| help="omit generated gold-cache entries", | |
| ) | |
| validate_bundle_parser = subparsers.add_parser( | |
| "bundle-validate", | |
| help="verify a portable benchmark bundle and all file checksums", | |
| ) | |
| validate_bundle_parser.add_argument("--root", type=Path, required=True) | |
| build_cache_parser = subparsers.add_parser( | |
| "build-gold-cache", help="build trusted gold renders using the Windows evaluator" | |
| ) | |
| build_cache_parser.add_argument("--evaluator-url", required=True) | |
| build_cache_parser.add_argument("--evaluator-version", required=True) | |
| build_cache_parser.add_argument("--root", default=str(DEFAULT_GOLD_CACHE_ROOT)) | |
| build_cache_parser.add_argument("--split", choices=("dev", "eval", "all"), default="dev") | |
| build_cache_parser.add_argument( | |
| "--token-env", default="AUTOCAD_EVALUATOR_TOKEN" | |
| ) | |
| build_cache_parser.add_argument("--timeout-s", type=float, default=120.0) | |
| verify_cache_parser = subparsers.add_parser( | |
| "verify-gold-cache", help="verify gold cache completeness and checksums" | |
| ) | |
| verify_cache_parser.add_argument("--evaluator-version", required=True) | |
| verify_cache_parser.add_argument("--root", default=str(DEFAULT_GOLD_CACHE_ROOT)) | |
| verify_cache_parser.add_argument( | |
| "--split", choices=("dev", "eval", "all"), default="dev" | |
| ) | |
| vision_parser = subparsers.add_parser( | |
| "vision-judge", | |
| help="judge one evaluated rollout with GPT-5.6 Sol vision", | |
| ) | |
| vision_parser.add_argument("--task-id", required=True) | |
| vision_parser.add_argument("--rollout-dir", required=True) | |
| vision_parser.add_argument("--gold-cache-root", default=str(DEFAULT_GOLD_CACHE_ROOT)) | |
| vision_parser.add_argument("--output-dir") | |
| vision_parser.add_argument("--api-key-env", default="OPENAI_API_KEY") | |
| vision_parser.add_argument("--model", default=DEFAULT_VISION_JUDGE_MODEL) | |
| vision_parser.add_argument( | |
| "--reasoning-effort", | |
| choices=("none", "low", "medium", "high", "xhigh", "max"), | |
| default=DEFAULT_VISION_JUDGE_REASONING, | |
| ) | |
| vision_parser.add_argument("--samples", type=int, choices=range(1, 6), default=1) | |
| vision_parser.add_argument("--max-output-tokens", type=int, default=32768) | |
| vision_parser.add_argument("--timeout-s", type=float, default=600.0) | |
| vision_parser.add_argument("--max-request-attempts", type=int, default=3) | |
| return parser | |
| def main(argv: Sequence[str] | None = None) -> int: | |
| args = build_parser().parse_args(argv) | |
| if args.command == "validate": | |
| return _validate() | |
| if args.command == "list": | |
| return _list_tasks(args.split) | |
| if args.command == "validate-audit": | |
| return _validate_audit() | |
| if args.command == "bundle-export": | |
| return _export_bundle(args) | |
| if args.command == "bundle-validate": | |
| return _validate_bundle(args) | |
| if args.command == "build-gold-cache": | |
| return _build_gold_cache(args) | |
| if args.command == "verify-gold-cache": | |
| return _verify_gold_cache(args) | |
| if args.command == "vision-judge": | |
| return _vision_judge(args) | |
| raise AssertionError(f"unhandled command: {args.command}") | |