Spaces:
Sleeping
Sleeping
| """ | |
| Core environment logic: task loading, reset, step, state. | |
| """ | |
| import json | |
| import random | |
| import uuid | |
| from pathlib import Path | |
| from .executor import run_code_safely | |
| class CodeDebugEnvironment: | |
| def __init__(self): | |
| self.tasks: dict[str, dict] = {} | |
| self.episodes: dict[str, dict] = {} | |
| self._load_tasks() | |
| def _load_tasks(self): | |
| tasks_dir = Path(__file__).parent.parent / "tasks" | |
| for json_file in sorted(tasks_dir.rglob("*.json")): | |
| with open(json_file, encoding="utf-8") as f: | |
| task = json.load(f) | |
| self.tasks[task["task_id"]] = task | |
| # ------------------------------------------------------------------ | |
| # Public API | |
| # ------------------------------------------------------------------ | |
| def reset(self, task_id: str | None = None, seed: int | None = None) -> dict: | |
| if seed is not None: | |
| random.seed(seed) | |
| if task_id is None: | |
| task_id = random.choice(list(self.tasks.keys())) | |
| if task_id not in self.tasks: | |
| raise KeyError(f"Unknown task_id: {task_id!r}") | |
| task = self.tasks[task_id] | |
| episode_id = str(uuid.uuid4()) | |
| self.episodes[episode_id] = { | |
| "episode_id": episode_id, | |
| "task": task, | |
| "step_count": 0, | |
| "done": False, | |
| "rewards": [], | |
| "last_test_results": [], | |
| } | |
| observation = self._initial_observation(task) | |
| return {"episode_id": episode_id, "observation": observation} | |
| def step(self, episode_id: str, action: dict) -> dict: | |
| if episode_id not in self.episodes: | |
| raise KeyError(f"Unknown episode_id: {episode_id!r}") | |
| ep = self.episodes[episode_id] | |
| if ep["done"]: | |
| raise ValueError("Episode is already finished. Call reset() to start a new episode.") | |
| task = ep["task"] | |
| submitted_code = action.get("code", "") | |
| ep["step_count"] += 1 | |
| test_results_raw, stdout, stderr = run_code_safely( | |
| submitted_code, | |
| task["test_code"], | |
| timeout=10, | |
| ) | |
| tests_passed = sum(1 for t in test_results_raw if t.get("passed", False)) | |
| total_tests = len(test_results_raw) | |
| reward = round(tests_passed / total_tests, 4) if total_tests > 0 else 0.0 | |
| max_steps = task.get("max_steps", 5) | |
| done = reward == 1.0 or ep["step_count"] >= max_steps | |
| ep["done"] = done | |
| ep["rewards"].append(reward) | |
| ep["last_test_results"] = test_results_raw | |
| observation = { | |
| "task_id": task["task_id"], | |
| "difficulty": task["difficulty"], | |
| "description": task["description"], | |
| "buggy_code": task["buggy_code"], | |
| "test_descriptions": task["test_descriptions"], | |
| "test_results": test_results_raw, | |
| "stdout": stdout, | |
| "stderr": stderr, | |
| "step_count": ep["step_count"], | |
| "max_steps": max_steps, | |
| "reward": reward, | |
| "done": done, | |
| "total_tests": total_tests, | |
| "tests_passed": tests_passed, | |
| } | |
| return {"observation": observation, "reward": reward, "done": done, "info": {}} | |
| def state(self, episode_id: str) -> dict: | |
| if episode_id not in self.episodes: | |
| raise KeyError(f"Unknown episode_id: {episode_id!r}") | |
| ep = self.episodes[episode_id] | |
| task = ep["task"] | |
| last_results = ep.get("last_test_results", []) | |
| return { | |
| "episode_id": episode_id, | |
| "task_id": task["task_id"], | |
| "difficulty": task["difficulty"], | |
| "step_count": ep["step_count"], | |
| "max_steps": task.get("max_steps", 5), | |
| "last_reward": ep["rewards"][-1] if ep["rewards"] else 0.0, | |
| "cumulative_reward": round(sum(ep["rewards"]), 4), | |
| "tests_passed": sum(1 for t in last_results if t.get("passed", False)), | |
| "total_tests": len(last_results), | |
| "done": ep["done"], | |
| } | |
| def list_tasks(self) -> list[dict]: | |
| return [ | |
| { | |
| "task_id": t["task_id"], | |
| "difficulty": t["difficulty"], | |
| "description": t["description"], | |
| "max_steps": t.get("max_steps", 5), | |
| "total_tests": len(t["test_descriptions"]), | |
| } | |
| for t in self.tasks.values() | |
| ] | |
| # ------------------------------------------------------------------ | |
| # Internal helpers | |
| # ------------------------------------------------------------------ | |
| def _initial_observation(self, task: dict) -> dict: | |
| return { | |
| "task_id": task["task_id"], | |
| "difficulty": task["difficulty"], | |
| "description": task["description"], | |
| "buggy_code": task["buggy_code"], | |
| "test_descriptions": task["test_descriptions"], | |
| "test_results": [], | |
| "stdout": "", | |
| "stderr": "", | |
| "step_count": 0, | |
| "max_steps": task.get("max_steps", 5), | |
| "reward": 0.0, | |
| "done": False, | |
| "total_tests": len(task["test_descriptions"]), | |
| "tests_passed": 0, | |
| } | |