| |
| from __future__ import annotations |
|
|
| import argparse |
| import json |
| import os |
| import sys |
| from pathlib import Path |
|
|
| ROOT = Path(__file__).resolve().parents[1] |
| if str(ROOT) not in sys.path: |
| sys.path.insert(0, str(ROOT)) |
|
|
| from dovla_cil.eval.external_vla_baseline import ( |
| ExternalVLABaselineSpec, |
| assess_external_vla_baseline, |
| build_external_vla_plan, |
| run_external_vla_entrypoint, |
| write_external_vla_plan, |
| ) |
|
|
|
|
| def parse_args() -> argparse.Namespace: |
| parser = argparse.ArgumentParser( |
| description=( |
| "Prepare or run an isolated public VLA baseline adapter. This command never downloads " |
| "weights or imports heavy VLA packages unless a user-provided adapter entrypoint is " |
| "run." |
| ) |
| ) |
| parser.add_argument("--model-family", default="smolvla", choices=["smolvla", "openvla"]) |
| parser.add_argument("--checkpoint", default=None, help="Local checkpoint/model directory.") |
| parser.add_argument("--dataset", default=None, help="DoVLA-CIL dataset directory to evaluate.") |
| parser.add_argument("--out", required=True, help="Output directory for plan and metrics.") |
| parser.add_argument("--revision", default=None, help="Pinned public checkpoint revision.") |
| parser.add_argument("--repo-id", default=None, help="Public Hugging Face repo id.") |
| parser.add_argument("--package-name", default=None, help="External package to check/import.") |
| parser.add_argument( |
| "--python", default="python", help="Python executable to use in the generated env plan." |
| ) |
| parser.add_argument( |
| "--adapter-entrypoint", |
| default=None, |
| help=( |
| "External adapter formatted as module:function. The function receives " |
| "(spec_dict, plan_dict) and returns JSON-serializable metrics." |
| ), |
| ) |
| parser.add_argument( |
| "--adapter-config", |
| type=Path, |
| default=None, |
| help="Secret-free JSON object passed to the adapter as spec metadata.", |
| ) |
| parser.add_argument( |
| "--dry-run", |
| action="store_true", |
| help="Write a plan and exit without requiring the external baseline to be ready.", |
| ) |
| parser.add_argument( |
| "--require-ready", |
| action="store_true", |
| help="Exit nonzero unless package, checkpoint, dataset, and adapter entrypoint are ready.", |
| ) |
| return parser.parse_args() |
|
|
|
|
| def main() -> int: |
| args = parse_args() |
| adapter_metadata = _load_adapter_config(args.adapter_config) |
| spec = ExternalVLABaselineSpec( |
| model_family=args.model_family, |
| checkpoint_path=args.checkpoint, |
| dataset_dir=args.dataset, |
| out_dir=args.out, |
| revision=args.revision, |
| repo_id=args.repo_id, |
| package_name=args.package_name, |
| python=args.python, |
| adapter_entrypoint=args.adapter_entrypoint, |
| metadata=adapter_metadata, |
| ) |
| out_dir = Path(args.out) |
| out_dir.mkdir(parents=True, exist_ok=True) |
| plan_path = write_external_vla_plan(spec, out_dir) |
| plan = build_external_vla_plan(spec) |
| status = assess_external_vla_baseline(spec) |
|
|
| print(f"external VLA plan: {plan_path}") |
| print(json.dumps(status.to_dict(), indent=2, sort_keys=True)) |
| if args.dry_run: |
| return 0 |
| if args.require_ready and not status.ready: |
| print( |
| "External VLA baseline is not ready. Use the generated plan to create an isolated " |
| "environment, download the public checkpoint, and provide an adapter entrypoint.", |
| file=sys.stderr, |
| ) |
| return 2 |
| if not args.adapter_entrypoint: |
| print( |
| "No adapter entrypoint was provided; wrote a reproducible plan but did not run " |
| "metrics.", |
| file=sys.stderr, |
| ) |
| return 2 |
|
|
| metrics = run_external_vla_entrypoint(args.adapter_entrypoint, spec, plan) |
| metrics_path = out_dir / "external_vla_metrics.json" |
| metrics_path.write_text(json.dumps(metrics, indent=2, sort_keys=True), encoding="utf-8") |
| print(f"external VLA metrics: {metrics_path}") |
| return 0 |
|
|
|
|
| def _load_adapter_config(path: Path | None) -> dict[str, object]: |
| if path is None: |
| return {} |
| payload = json.loads(os.path.expandvars(path.read_text(encoding="utf-8"))) |
| if not isinstance(payload, dict): |
| raise ValueError("adapter config must be a JSON object") |
| forbidden = {"api_key", "apikey", "token", "secret", "password"} |
| unsafe = [key for key in payload if key.lower() in forbidden] |
| if unsafe: |
| raise ValueError(f"adapter config must not contain secrets: {', '.join(sorted(unsafe))}") |
| return payload |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|