Spaces:
Sleeping
Sleeping
| r""" | |
| CivicAI OpenEnv Compliance Validation Script | |
| Run: venv/Scripts/python.exe validate_openenv.py | |
| """ | |
| import sys | |
| print("=== Import Check ===") | |
| from civicai.models import Action, Observation, Reward, SocietyState, SubsidyPolicy | |
| print("[OK] civicai.models: Action, Observation, Reward, SocietyState") | |
| from civicai.environment import CivicAIEnv | |
| print("[OK] civicai.environment: CivicAIEnv") | |
| from openenv.env import Env | |
| assert issubclass(CivicAIEnv, Env), "CivicAIEnv must inherit from openenv.env.Env" | |
| print("[OK] CivicAIEnv inherits from openenv.env.Env") | |
| from pydantic import BaseModel | |
| assert issubclass(Action, BaseModel), "Action must be Pydantic" | |
| assert issubclass(Observation, BaseModel), "Observation must be Pydantic" | |
| assert issubclass(Reward, BaseModel), "Reward must be Pydantic" | |
| print("[OK] Action, Observation, Reward are Pydantic BaseModels") | |
| print() | |
| print("=== OpenEnv API Compliance ===") | |
| env = CivicAIEnv() | |
| # reset() -> Observation | |
| obs = env.reset() | |
| assert isinstance(obs, Observation), f"reset() must return Observation, got {type(obs)}" | |
| print(f"[OK] reset() -> Observation (turn={obs.turn}, task={obs.task_id})") | |
| # state() -> SocietyState (must be callable method, NOT a property) | |
| assert callable(getattr(env, "state")), "state must be a callable method, not a property" | |
| st = env.state() | |
| assert isinstance(st, SocietyState), f"state() must return SocietyState, got {type(st)}" | |
| print(f"[OK] state() -> SocietyState (callable method, turn={st.turn})") | |
| # step(action) -> (Observation, float, bool, dict) | |
| action = Action() | |
| result = env.step(action) | |
| assert len(result) == 4, f"step() must return 4-tuple, got {len(result)}" | |
| obs2, reward, done, info = result | |
| assert isinstance(obs2, Observation), f"step()[0] must be Observation, got {type(obs2)}" | |
| assert isinstance(reward, float), f"step()[1] must be float, got {type(reward)}" | |
| assert isinstance(done, bool), f"step()[2] must be bool, got {type(done)}" | |
| assert isinstance(info, dict), f"step()[3] must be dict, got {type(info)}" | |
| assert 0.0 <= reward <= 1.0, f"reward must be in [0,1], got {reward}" | |
| print(f"[OK] step(action) -> (Observation, float, bool, dict) reward={reward:.4f}") | |
| print() | |
| print("=== Task Tests ===") | |
| for task_id in ["stabilize_economy", "manage_pandemic", "control_crisis"]: | |
| obs = env.reset(task_id=task_id) | |
| assert isinstance(obs, Observation) | |
| obs2, r, done, info_ = env.step(Action()) | |
| assert 0.0 <= r <= 1.0 | |
| print(f"[OK] task={task_id} initial_reward={r:.4f}") | |
| print() | |
| print("=== Reward Model ===") | |
| from civicai.reward import compute_reward | |
| obs = env.reset() | |
| env.step(Action()) | |
| st = env.state() | |
| reward_obj = compute_reward(st, Action()) | |
| rd = reward_obj.model_dump() | |
| assert "score" in rd and "rubrics" in rd and "penalties" in rd | |
| rubric_keys = set(rd["rubrics"].keys()) | |
| assert rubric_keys == {"economic", "health", "social", "sustainability", "crime"}, \ | |
| f"Unexpected rubric keys: {rubric_keys}" | |
| print(f"[OK] Reward.score={reward_obj.score:.4f} rubrics={sorted(rubric_keys)}") | |
| print() | |
| print("=== openenv.yaml Validation ===") | |
| import yaml, os | |
| yaml_path = "openenv.yaml" | |
| assert os.path.exists(yaml_path), "openenv.yaml not found" | |
| with open(yaml_path) as f: | |
| cfg = yaml.safe_load(f) | |
| required_top_keys = ["name", "description", "observation_space", "action_space", "reward_range", "tasks"] | |
| for k in required_top_keys: | |
| assert k in cfg, f"openenv.yaml missing required key: {k}" | |
| print(f"[OK] openenv.yaml has '{k}'") | |
| assert len(cfg["tasks"]) >= 3, f"Need >= 3 tasks, found {len(cfg['tasks'])}" | |
| print(f"[OK] openenv.yaml has {len(cfg['tasks'])} tasks (>= 3 required)") | |
| for task in cfg["tasks"]: | |
| for field in ["id", "name", "description", "success_criteria", "max_steps"]: | |
| assert field in task, f"Task '{task.get('id', '?')}' missing field: {field}" | |
| print(f"[OK] All task entries have required fields") | |
| assert isinstance(cfg["reward_range"], list) and len(cfg["reward_range"]) == 2 | |
| print(f"[OK] reward_range={cfg['reward_range']}") | |
| print() | |
| print("=" * 55) | |
| print(" ALL CHECKS PASSED") | |
| print(" CivicAI is fully OpenEnv compliant.") | |
| print("=" * 55) | |