sravaniamere commited on
Commit
e965a47
·
1 Parent(s): 830b0e5

use openenv base classes and fix yaml format to match working submissions

Browse files
Files changed (3) hide show
  1. openenv.yaml +20 -7
  2. sql_env/models.py +16 -23
  3. sql_env/server.py +96 -136
openenv.yaml CHANGED
@@ -24,23 +24,26 @@ description: >
24
  author: SyncShift
25
 
26
  tasks:
27
- - name: easy
28
  difficulty: easy
29
  max_steps: 5
30
  description: Fix a single syntax error. Error hint provided.
31
- grader: sql_env.grader.grade
 
32
 
33
- - name: medium
34
  difficulty: medium
35
  max_steps: 5
36
  description: Fix multiple errors across keywords and clauses. No hint.
37
- grader: sql_env.grader.grade
 
38
 
39
- - name: hard
40
  difficulty: hard
41
  max_steps: 4
42
  description: Fix many errors in complex multi-join queries. Schema provided.
43
- grader: sql_env.grader.grade
 
44
 
45
  observation_space:
46
  type: object
@@ -74,4 +77,14 @@ reward:
74
  range: [0.001, 0.999]
75
  description: >
76
  0.999 = exact match, 0.7 = right tokens minor structure diff,
77
- 0.4 = most keywords correct, 0.2 = basic structure present, 0.001 = invalid SQL.
 
 
 
 
 
 
 
 
 
 
 
24
  author: SyncShift
25
 
26
  tasks:
27
+ - id: easy
28
  difficulty: easy
29
  max_steps: 5
30
  description: Fix a single syntax error. Error hint provided.
31
+ steps: 5
32
+ ideal_action: correct_sql
33
 
34
+ - id: medium
35
  difficulty: medium
36
  max_steps: 5
37
  description: Fix multiple errors across keywords and clauses. No hint.
38
+ steps: 5
39
+ ideal_action: correct_sql
40
 
41
+ - id: hard
42
  difficulty: hard
43
  max_steps: 4
44
  description: Fix many errors in complex multi-join queries. Schema provided.
45
+ steps: 4
46
+ ideal_action: correct_sql
47
 
48
  observation_space:
49
  type: object
 
77
  range: [0.001, 0.999]
78
  description: >
79
  0.999 = exact match, 0.7 = right tokens minor structure diff,
80
+ 0.4 = most keywords correct, 0.2 = basic structure present, 0.001 = invalid SQL.
81
+
82
+ scoring:
83
+ reward_range: [0.001, 0.999]
84
+ success_threshold: 0.5
85
+ score_formula: mean(step_rewards)
86
+
87
+ constraints:
88
+ max_runtime_seconds: 1200
89
+ max_memory_gb: 8
90
+ max_vcpu: 2
sql_env/models.py CHANGED
@@ -1,7 +1,13 @@
1
- from pydantic import BaseModel, Field
2
- from typing import Optional, Any, Dict, Callable
 
3
 
4
- class SQLObservation(BaseModel):
 
 
 
 
 
5
  task_id: str
6
  broken_query: str
7
  schema_context: Optional[str] = None
@@ -9,28 +15,15 @@ class SQLObservation(BaseModel):
9
  step_number: int
10
  previous_attempt: Optional[str] = None
11
  feedback: Optional[str] = None
 
 
12
 
13
- class SQLAction(BaseModel):
14
- corrected_query: str
15
-
16
- class SQLReward(BaseModel):
17
- value: float = Field(gt=0.0, lt=1.0) # strictly between, not ge/le
18
- reason: str
19
 
20
- class SQLTask(BaseModel):
21
  task_id: str
22
  difficulty: str
23
- broken_query: str
24
- canonical_answer: str
25
- schema_context: Optional[str] = None
26
- error_hint: Optional[str] = None
27
- max_steps: int = 5
28
- grader: Optional[Any] = Field(default=None, exclude=True)
29
-
30
- model_config = {"arbitrary_types_allowed": True}
31
-
32
- class StepResult(BaseModel):
33
- observation: SQLObservation
34
- reward: float
35
  done: bool
36
- info: Dict[str, Any] = Field(default_factory=dict)
 
 
1
+ from typing import List, Optional, Any, Dict
2
+ from openenv.core.env_server.types import Action, Observation, State
3
+ from pydantic import Field
4
 
5
+
6
+ class SQLAction(Action):
7
+ corrected_query: str
8
+
9
+
10
+ class SQLObservation(Observation):
11
  task_id: str
12
  broken_query: str
13
  schema_context: Optional[str] = None
 
15
  step_number: int
16
  previous_attempt: Optional[str] = None
17
  feedback: Optional[str] = None
18
+ reward: float = 0.0
19
+ done: bool = False
20
 
 
 
 
 
 
 
21
 
22
+ class SQLState(State):
23
  task_id: str
24
  difficulty: str
25
+ step_count: int
26
+ max_steps: int
 
 
 
 
 
 
 
 
 
 
27
  done: bool
28
+ last_reward: float
29
+ rewards_history: List[float]
sql_env/server.py CHANGED
@@ -1,149 +1,109 @@
1
  """
2
- FastAPI HTTP wrapper for SQLCorrectionEnv.
3
-
4
- Exposes the OpenEnv-required endpoints: /reset, /step, /state + /tasks for validator.
5
  """
6
-
7
- from contextlib import asynccontextmanager
8
  from typing import Optional
 
 
 
9
 
10
- from fastapi import FastAPI, HTTPException
11
- from fastapi.middleware.cors import CORSMiddleware
12
- from pydantic import BaseModel
13
-
14
- from sql_env import SQLAction, SQLCorrectionEnv
15
- from sql_env.tasks import ALL_TASKS
16
-
17
-
18
- class ResetRequest(BaseModel):
19
- difficulty: Optional[str] = "easy"
20
- task_name: Optional[str] = None
21
- task_index: Optional[int] = None
22
-
23
-
24
- class StepRequest(BaseModel):
25
- corrected_query: str
26
-
27
-
28
- env: Optional[SQLCorrectionEnv] = None
29
-
30
-
31
- @asynccontextmanager
32
- async def lifespan(_: FastAPI):
33
- global env
34
- env = SQLCorrectionEnv(difficulty="easy")
35
- yield
36
- if env is not None:
37
- await env.close()
38
-
39
-
40
- app = FastAPI(
41
- title="SQL Correction RL Environment",
42
- description="OpenEnv-compliant environment for SQL query correction tasks.",
43
- version="1.0.0",
44
- lifespan=lifespan,
45
- )
46
-
47
- app.add_middleware(
48
- CORSMiddleware,
49
- allow_origins=["*"],
50
- allow_methods=["*"],
51
- allow_headers=["*"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  )
53
 
54
 
55
- @app.post("/reset")
56
- async def reset(request: ResetRequest = ResetRequest()):
57
- """Reset the environment and return the initial observation."""
58
- global env
59
- difficulty = request.task_name or request.difficulty or "easy"
60
- if difficulty not in {"easy", "medium", "hard"}:
61
- raise HTTPException(status_code=400, detail="difficulty must be easy, medium, or hard")
62
-
63
- env = SQLCorrectionEnv(
64
- difficulty=difficulty,
65
- task_index=request.task_index,
66
- )
67
- obs = await env.reset()
68
- return obs.model_dump()
69
-
70
-
71
- @app.post("/step")
72
- async def step(request: StepRequest):
73
- """Take one step and return the new observation, reward, done flag, and info."""
74
- global env
75
- if env is None:
76
- raise HTTPException(status_code=400, detail="Call /reset first.")
77
-
78
- try:
79
- action = SQLAction(corrected_query=request.corrected_query)
80
- result = await env.step(action)
81
- return result.model_dump()
82
- except RuntimeError as exc:
83
- raise HTTPException(status_code=400, detail=str(exc)) from exc
84
-
85
-
86
- @app.post("/state")
87
- async def state():
88
- """Return current environment state."""
89
- global env
90
- if env is None:
91
- return {"status": "not_initialized"}
92
- return await env.state()
93
-
94
-
95
- @app.get("/health")
96
- async def health():
97
- return {"status": "ok", "service": "sql-correction-env"}
98
-
99
-
100
- @app.get("/tasks")
101
- async def list_tasks():
102
- """Return graded tasks by difficulty (RL validator format)."""
103
- graded_tasks = {
104
- diff: [task.__dict__ for task in tasks if task.grader is not None]
105
- for diff, tasks in ALL_TASKS.items()
106
- }
107
- return graded_tasks # {"easy": [tasks], "medium": [tasks], "hard": [tasks]}
108
-
109
-
110
- @app.get("/")
111
- async def root():
112
- return {
113
- "name": "SQL Correction RL Environment",
114
- "version": "1.0.0",
115
- "endpoints": ["/reset", "/step", "/state", "/health", "/tasks"],
116
- "tasks": ["easy", "medium", "hard"],
117
- }
118
-
119
-
120
  def main():
121
  import uvicorn
122
  uvicorn.run(app, host="0.0.0.0", port=7860)
123
 
124
 
125
  if __name__ == "__main__":
126
- main()
127
- @app.post("/grader")
128
- async def grader_endpoint(request: dict):
129
- """Grader endpoint called by validator to score a task directly."""
130
- from sql_env.grader import grade
131
- from sql_env.tasks import TASK_SETS
132
- import random
133
-
134
- task_name = request.get("task_name", "easy")
135
- action_data = request.get("action", {})
136
- corrected_query = action_data.get("corrected_query", "")
137
-
138
- tasks = TASK_SETS.get(task_name, TASK_SETS["easy"])
139
- task = random.choice(tasks)
140
-
141
- action = SQLAction(corrected_query=corrected_query)
142
- reward_obj = grade(action, task)
143
-
144
- return {
145
- "task_name": task_name,
146
- "score": reward_obj.value,
147
- "reason": reward_obj.reason,
148
- "success": reward_obj.value >= 0.95,
149
- }
 
1
  """
2
+ FastAPI server using openenv.core base classes — required for validator.
 
 
3
  """
4
+ import random
 
5
  from typing import Optional
6
+ from openenv.core.env_server import create_fastapi_app
7
+ from openenv.core.env_server.interfaces import Environment
8
+ from openenv.core.env_server.types import State
9
 
10
+ try:
11
+ from sql_env.models import SQLAction, SQLObservation, SQLState
12
+ from sql_env.tasks import TASK_SETS
13
+ from sql_env.grader import grade, generate_feedback
14
+ except ImportError:
15
+ from models import SQLAction, SQLObservation, SQLState
16
+ from tasks import TASK_SETS
17
+ from grader import grade, generate_feedback
18
+
19
+
20
+ class SQLCorrectionEnvironment(Environment):
21
+
22
+ def __init__(self):
23
+ super().__init__()
24
+ self._difficulty = "easy"
25
+ self._current_task = None
26
+ self._step_count = 0
27
+ self._done = False
28
+ self._last_reward = 0.0
29
+ self._rewards_history = []
30
+
31
+ def reset(self, difficulty: str = "easy") -> SQLObservation:
32
+ self._difficulty = difficulty
33
+ tasks = TASK_SETS.get(difficulty, TASK_SETS["easy"])
34
+ self._current_task = random.choice(tasks)
35
+ self._step_count = 0
36
+ self._done = False
37
+ self._last_reward = 0.0
38
+ self._rewards_history = []
39
+ return SQLObservation(
40
+ task_id=self._current_task.task_id,
41
+ broken_query=self._current_task.broken_query,
42
+ schema_context=self._current_task.schema_context,
43
+ error_hint=self._current_task.error_hint,
44
+ step_number=0,
45
+ previous_attempt=None,
46
+ feedback=None,
47
+ reward=0.0,
48
+ done=False,
49
+ )
50
+
51
+ def step(self, action: SQLAction) -> SQLObservation:
52
+ self._step_count += 1
53
+ reward_obj = grade(action, self._current_task)
54
+ reward = reward_obj.value
55
+ self._last_reward = reward
56
+ self._rewards_history.append(reward)
57
+ done = (reward >= 0.95) or (self._step_count >= self._current_task.max_steps)
58
+ self._done = done
59
+ feedback = generate_feedback(action, self._current_task, reward_obj)
60
+ return SQLObservation(
61
+ task_id=self._current_task.task_id,
62
+ broken_query=self._current_task.broken_query,
63
+ schema_context=self._current_task.schema_context,
64
+ error_hint=self._current_task.error_hint,
65
+ step_number=self._step_count,
66
+ previous_attempt=action.corrected_query,
67
+ feedback=feedback,
68
+ reward=reward,
69
+ done=done,
70
+ )
71
+
72
+ @property
73
+ def state(self) -> SQLState:
74
+ if self._current_task is None:
75
+ return SQLState(
76
+ task_id="none",
77
+ difficulty="none",
78
+ step_count=0,
79
+ max_steps=0,
80
+ done=False,
81
+ last_reward=0.0,
82
+ rewards_history=[],
83
+ )
84
+ return SQLState(
85
+ task_id=self._current_task.task_id,
86
+ difficulty=self._difficulty,
87
+ step_count=self._step_count,
88
+ max_steps=self._current_task.max_steps,
89
+ done=self._done,
90
+ last_reward=self._last_reward,
91
+ rewards_history=self._rewards_history,
92
+ )
93
+
94
+
95
+ app = create_fastapi_app(
96
+ SQLCorrectionEnvironment,
97
+ SQLAction,
98
+ SQLObservation,
99
+ env_name="sql-correction-env",
100
  )
101
 
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  def main():
104
  import uvicorn
105
  uvicorn.run(app, host="0.0.0.0", port=7860)
106
 
107
 
108
  if __name__ == "__main__":
109
+ main()