Spaces:
Sleeping
Sleeping
Download server/sql_environment.py from aakashdandekar2006/sql-debug-env: direct link, hf CLI and curl.
- Browser
- Download file 5.64 kB
-
https://huggingface.co/spaces/aakashdandekar2006/sql-debug-env/resolve/main/server/sql_environment.py
- Command line
-
hf download hf://spaces/aakashdandekar2006/sql-debug-env/server/sql_environment.py
-
curl -L -o sql_environment.py https://huggingface.co/spaces/aakashdandekar2006/sql-debug-env/resolve/main/server/sql_environment.py
5.64 kB
| """ | |
| SQL Debug Environment — server-side environment logic. | |
| Implements the OpenEnv Environment interface: reset(), step(), state property. | |
| """ | |
| import sys | |
| import os | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| import random | |
| from uuid import uuid4 | |
| from typing import Optional | |
| from models import Action, Observation, State | |
| from tasks import TASKS, TASK_GROUPS, get_task | |
| from graders import grade_submission | |
| MAX_STEPS = 5 | |
| class SQLDebugEnvironment: | |
| """ | |
| Real-world OpenEnv environment for SQL query debugging. | |
| An agent is given a broken SQL query and must submit corrected versions | |
| to earn rewards. The environment tracks attempts and computes shaped rewards. | |
| """ | |
| def __init__(self): | |
| self._state: Optional[State] = None | |
| self._current_task_id: Optional[str] = None | |
| self._current_task: Optional[dict] = None | |
| self._submissions: list = [] | |
| self._done: bool = False | |
| self._last_reward: float = 0.0 | |
| self._last_result: str = "" | |
| self._task_group: Optional[str] = None | |
| def reset( | |
| self, | |
| task_group: Optional[str] = None, | |
| task_id: Optional[str] = None, | |
| seed: Optional[int] = None, | |
| ) -> Observation: | |
| """ | |
| Initialize a new episode. | |
| Args: | |
| task_group: One of 'easy_syntax_fix', 'medium_logic_fix', 'hard_optimization_fix' | |
| task_id: Specific task id (overrides task_group) | |
| seed: Random seed for reproducibility | |
| """ | |
| if seed is not None: | |
| random.seed(seed) | |
| if task_id and task_id in TASKS: | |
| self._current_task_id = task_id | |
| self._task_group = None | |
| elif task_id and task_id in TASK_GROUPS: | |
| self._current_task_id = random.choice(TASK_GROUPS[task_id]) | |
| self._task_group = task_id | |
| elif task_group and task_group in TASK_GROUPS: | |
| self._current_task_id = random.choice(TASK_GROUPS[task_group]) | |
| self._task_group = task_group | |
| else: | |
| self._current_task_id = random.choice(list(TASKS.keys())) | |
| self._task_group = None | |
| self._current_task = get_task(self._current_task_id) | |
| self._submissions = [] | |
| self._done = False | |
| self._last_reward = 0.0 | |
| self._last_result = "" | |
| episode_id = str(uuid4()) | |
| self._state = State( | |
| episode_id=episode_id, | |
| step_count=0, | |
| task_id=self._task_group if self._task_group else self._current_task_id, | |
| max_steps=MAX_STEPS, | |
| cumulative_reward=0.0, | |
| ) | |
| return self._build_observation() | |
| def step(self, action: Action) -> Observation: | |
| """ | |
| Execute one fix attempt. | |
| Args: | |
| action: Agent's submitted SQL query + optional explanation | |
| Returns: | |
| Observation with reward, done flag, and feedback | |
| """ | |
| if self._done: | |
| return self._build_observation( | |
| message="Episode already done. Call reset()." | |
| ) | |
| if self._state is None: | |
| raise RuntimeError("Environment not initialized. Call reset() first.") | |
| submitted_query = action.query.strip() | |
| self._submissions.append(submitted_query) | |
| self._state.step_count += 1 | |
| score, feedback, details = grade_submission( | |
| self._current_task_id, submitted_query | |
| ) | |
| reward = self._compute_reward(score, self._state.step_count) | |
| self._last_reward = reward | |
| self._state.cumulative_reward += reward | |
| self._last_result = feedback | |
| if score == 1.0: | |
| self._done = True | |
| elif self._state.step_count >= MAX_STEPS: | |
| self._done = True | |
| return self._build_observation() | |
| def state(self) -> State: | |
| """Return current environment state metadata.""" | |
| if self._state is None: | |
| return State(episode_id="uninitialized", step_count=0) | |
| return self._state | |
| def _compute_reward(self, score: float, step_num: int) -> float: | |
| """ | |
| Shaped reward function with partial progress signals. | |
| - score=1.0: full reward, bonus for solving early | |
| - score=0.7: meaningful partial credit | |
| - score=0.5: small partial credit | |
| - score=0.3: tiny credit for at least running | |
| - score=0.0: penalty for wasted attempt (after first) | |
| """ | |
| if score == 1.0: | |
| early_bonus = max(0.0, (MAX_STEPS - step_num) * 0.05) | |
| return round(1.0 + early_bonus, 4) | |
| elif score >= 0.7: | |
| return 0.5 | |
| elif score >= 0.5: | |
| return 0.2 | |
| elif score >= 0.3: | |
| return 0.1 | |
| else: | |
| return -0.05 if step_num > 1 else 0.0 | |
| def _build_observation(self, message: str = "") -> Observation: | |
| task = self._current_task | |
| attempts_remaining = MAX_STEPS - (self._state.step_count if self._state else 0) | |
| return Observation( | |
| schema_sql=task["schema_sql"].strip(), | |
| sample_data=task["sample_data"].strip(), | |
| buggy_query=task["buggy_query"].strip(), | |
| error_message=task["error_message"], | |
| task_description=task["task_description"], | |
| expected_columns=task["expected_columns"], | |
| attempts_remaining=max(0, attempts_remaining), | |
| last_attempt_result=self._last_result + (" " + message if message else ""), | |
| reward=self._last_reward, | |
| done=self._done, | |
| success=self._done and self._last_reward >= 1.0, | |
| ) | |