Spaces:
Sleeping
Sleeping
| from typing import Dict, Any | |
| from env.models import Observation, Action, EpisodeState | |
| from env.tasks import TASKS | |
| from env.grader import grade_identify, grade_fix | |
| def _clamp(reward: float) -> float: | |
| """Clamp reward strictly to [0.0, 1.0] per OpenEnv spec.""" | |
| return round(max(0.0, min(reward, 1.0)), 4) | |
| class CodeReviewEnvironment: | |
| """ | |
| OpenEnv-compliant code review environment. | |
| Agent reads buggy code, identifies issues, and suggests fixes. | |
| 3 tasks: easy (syntax) β medium (logic) β hard (performance) | |
| All rewards are clamped to [0.0, 1.0] before being returned. | |
| """ | |
| def __init__(self): | |
| self._tasks = TASKS | |
| self._current_task_index = 0 | |
| self._current_step = 0 | |
| self._history = [] | |
| self._phase = "identify" # "identify" β "fix" | |
| self._identify_reward = 0.0 | |
| self._total_reward = 0.0 | |
| self._done = False | |
| # ------------------------------------------------------------------ | |
| # OpenEnv required: reset() | |
| # ------------------------------------------------------------------ | |
| def reset(self) -> Observation: | |
| self._current_task_index = 0 | |
| self._current_step = 0 | |
| self._history = [] | |
| self._phase = "identify" | |
| self._identify_reward = 0.0 | |
| self._total_reward = 0.0 | |
| self._done = False | |
| return self._make_observation() | |
| # ------------------------------------------------------------------ | |
| # OpenEnv required: step(action) | |
| # ------------------------------------------------------------------ | |
| def step(self, action: Action) -> Dict[str, Any]: | |
| if self._done: | |
| return { | |
| "observation": self._make_observation(), | |
| "reward": 0.0, | |
| "done": True, | |
| "info": {"error": "Episode already done. Call reset()."}, | |
| } | |
| task = self._current_task() | |
| reward = 0.0 | |
| info = {} | |
| self._current_step += 1 | |
| # ββ IDENTIFY phase ββββββββββββββββββββββββββββββββββββββββββββββ | |
| if action.action_type == "identify": | |
| if self._phase == "fix": | |
| # Repeated identify after fix phase β penalise (clamped to 0.0) | |
| reward = 0.0 | |
| info["warning"] = "Already in fix phase. Skipping repeated identify." | |
| else: | |
| reward = grade_identify(task["identify_keywords"], action.content) | |
| self._identify_reward = reward | |
| self._phase = "fix" | |
| info["phase_transition"] = "identify β fix" | |
| info["identify_score"] = reward | |
| # ββ FIX phase βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| elif action.action_type == "fix": | |
| if self._phase == "identify": | |
| # Jumped straight to fix without identifying β partial credit only | |
| fix_score = grade_fix(task["fixed_code"], action.content) | |
| reward = fix_score * 0.5 # halved because no identify step | |
| info["warning"] = "Skipped identify phase. Partial fix credit." | |
| else: | |
| fix_score = grade_fix(task["fixed_code"], action.content) | |
| # Identify bonus for continuous signal β final reward clamped to 1.0 | |
| bonus = 0.1 if self._identify_reward >= 0.4 else 0.0 | |
| reward = fix_score + bonus | |
| info["fix_score"] = fix_score | |
| info["identify_bonus"] = bonus | |
| # Clamp before recording and returning | |
| reward = _clamp(reward) | |
| self._total_reward += reward | |
| # Move to next task or end episode | |
| done = self._advance_task() | |
| self._phase = "identify" | |
| self._identify_reward = 0.0 | |
| obs = self._make_observation() | |
| self._history.append( | |
| f"[task={task['id']}] action={action.action_type} reward={reward:.2f}" | |
| ) | |
| info["task_completed"] = task["id"] | |
| info["task_difficulty"] = task["difficulty"] | |
| return { | |
| "observation": obs, | |
| "reward": reward, | |
| "done": done, | |
| "info": info, | |
| } | |
| else: | |
| # Unknown action type β penalise, clamped to 0.0 | |
| reward = 0.0 | |
| info["error"] = f"Unknown action_type '{action.action_type}'. Use 'identify' or 'fix'." | |
| reward = _clamp(reward) | |
| self._total_reward += reward | |
| self._history.append( | |
| f"[task={task['id']}] action={action.action_type} reward={reward:.2f}" | |
| ) | |
| return { | |
| "observation": self._make_observation(), | |
| "reward": reward, | |
| "done": self._done, | |
| "info": info, | |
| } | |
| # ------------------------------------------------------------------ | |
| # OpenEnv required: state (property) | |
| # ------------------------------------------------------------------ | |
| def state(self) -> EpisodeState: | |
| task = self._current_task() | |
| return EpisodeState( | |
| current_task_id=task["id"], | |
| current_task_difficulty=task["difficulty"], | |
| current_task_category=task["category"], | |
| phase=self._phase, | |
| step=self._current_step, | |
| total_reward=round(self._total_reward, 4), | |
| done=self._done, | |
| history=list(self._history), | |
| ) | |
| # ------------------------------------------------------------------ | |
| # Internal helpers | |
| # ------------------------------------------------------------------ | |
| def _current_task(self) -> dict: | |
| # Clamp to last task when episode is done to avoid index error | |
| idx = min(self._current_task_index, len(self._tasks) - 1) | |
| return self._tasks[idx] | |
| def _make_observation(self) -> Observation: | |
| task = self._current_task() | |
| task_prompt = ( | |
| f"[{task['difficulty'].upper()} | {task['category']}] {task['title']}\n" | |
| f"{task['description']}\n\n" | |
| f"Phase: {self._phase.upper()}\n" | |
| f"{'Identify the bug.' if self._phase == 'identify' else 'Fix the code.'}" | |
| ) | |
| return Observation( | |
| code=task["code"], | |
| task=task_prompt, | |
| history=list(self._history), | |
| task_id=task["id"], | |
| language=task["language"], | |
| difficulty=task["difficulty"], | |
| category=task["category"], | |
| ) | |
| def _advance_task(self) -> bool: | |
| """Move to next task. Returns True if episode is done.""" | |
| self._current_task_index += 1 | |
| if self._current_task_index >= len(self._tasks): | |
| self._done = True | |
| return True | |
| return False |