Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| Local OpenEnv validation helper. | |
| Checks: | |
| 1. openenv.yaml exists and contains required sections | |
| 2. environment/model import paths are importable | |
| 3. optional: `openenv validate` when CLI is installed | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import importlib | |
| import subprocess | |
| import sys | |
| from pathlib import Path | |
| from typing import Any | |
| import yaml | |
| REQUIRED_TOP_LEVEL = ("name", "entrypoint", "environment", "tasks", "api") | |
| def _import_path(path: str) -> Any: | |
| module_name, _, obj_name = path.rpartition(".") | |
| if not module_name or not obj_name: | |
| raise ValueError(f"Invalid import path: {path!r}") | |
| module = importlib.import_module(module_name) | |
| return getattr(module, obj_name) | |
| def main() -> int: | |
| parser = argparse.ArgumentParser(description="Validate OpenEnv environment shape") | |
| parser.add_argument("--repo", default=".") | |
| parser.add_argument( | |
| "--skip-openenv-cli", | |
| action="store_true", | |
| help="Skip invoking `openenv validate`", | |
| ) | |
| args = parser.parse_args() | |
| repo = Path(args.repo).resolve() | |
| if str(repo) not in sys.path: | |
| sys.path.insert(0, str(repo)) | |
| cfg_path = repo / "openenv.yaml" | |
| if not cfg_path.exists(): | |
| print(f"[FAIL] Missing {cfg_path}") | |
| return 1 | |
| config = yaml.safe_load(cfg_path.read_text(encoding="utf-8")) | |
| if config.get("spec_version") != 1: | |
| print("[FAIL] openenv.yaml must declare spec_version: 1") | |
| return 1 | |
| for key in REQUIRED_TOP_LEVEL: | |
| if key not in config: | |
| print(f"[FAIL] openenv.yaml missing required top-level key: {key}") | |
| return 1 | |
| env_cfg = config["environment"] | |
| entrypoint = config["entrypoint"] | |
| for field in ("module", "object"): | |
| if field not in entrypoint: | |
| print(f"[FAIL] entrypoint missing field: {field}") | |
| return 1 | |
| _import_path(f"{entrypoint['module']}.{entrypoint['object']}") | |
| _import_path(env_cfg["class"]) | |
| _import_path(env_cfg["observation_model"]) | |
| _import_path(env_cfg["action_model"]) | |
| _import_path(env_cfg["reward_model"]) | |
| _import_path(env_cfg["state_model"]) | |
| _import_path(env_cfg["step_info_model"]) | |
| print("[OK] openenv.yaml imports are valid") | |
| tasks = config.get("tasks", []) | |
| if len(tasks) < 3: | |
| print("[FAIL] Need at least 3 tasks in openenv.yaml") | |
| return 1 | |
| print(f"[OK] task count={len(tasks)}") | |
| if not args.skip_openenv_cli: | |
| try: | |
| proc = subprocess.run( | |
| ["openenv", "validate"], | |
| cwd=str(repo), | |
| check=False, | |
| capture_output=True, | |
| text=True, | |
| ) | |
| except FileNotFoundError: | |
| print("[WARN] `openenv` CLI not found; skipping `openenv validate`") | |
| else: | |
| if proc.returncode != 0: | |
| print("[FAIL] `openenv validate` failed") | |
| if proc.stdout: | |
| print(proc.stdout.rstrip()) | |
| if proc.stderr: | |
| print(proc.stderr.rstrip()) | |
| return proc.returncode | |
| print("[OK] `openenv validate` passed") | |
| print("[DONE] validation complete") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |