thorodin103 commited on
Commit
98c059c
Β·
verified Β·
1 Parent(s): d76a1ac

Upload main.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. main.py +222 -0
main.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import sys
3
+ import os
4
+ sys.path.insert(0, os.path.dirname(__file__))
5
+
6
+ from fastapi import FastAPI, HTTPException
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from typing import Dict, Any
9
+ from models import Action, StepResult, Reward
10
+ from environment import DataCleaningEnv
11
+
12
+ # ─────────────────────────────────────────
13
+ # App Setup
14
+ # ─────────────────────────────────────────
15
+
16
+ app = FastAPI(
17
+ title="Data Cleaning OpenEnv",
18
+ description=(
19
+ "An OpenEnv-compliant environment where AI agents "
20
+ "learn to clean messy real-world datasets step by step."
21
+ ),
22
+ version="1.0.0"
23
+ )
24
+
25
+ app.add_middleware(
26
+ CORSMiddleware,
27
+ allow_origins=["*"],
28
+ allow_methods=["*"],
29
+ allow_headers=["*"],
30
+ )
31
+
32
+ # ─────────────────────────────────────────
33
+ # One environment instance per task
34
+ # ─────────────────────────────────────────
35
+
36
+ VALID_TASKS = [
37
+ "easy_dedup_rename",
38
+ "medium_missing_dtype",
39
+ "hard_full_pipeline"
40
+ ]
41
+
42
+ envs: Dict[str, DataCleaningEnv] = {
43
+ task_id: DataCleaningEnv(task_id=task_id)
44
+ for task_id in VALID_TASKS
45
+ }
46
+
47
+
48
+ def get_env(task_id: str) -> DataCleaningEnv:
49
+ if task_id not in envs:
50
+ raise HTTPException(
51
+ status_code=404,
52
+ detail=(
53
+ f"Task '{task_id}' not found. "
54
+ f"Valid tasks: {VALID_TASKS}"
55
+ )
56
+ )
57
+ return envs[task_id]
58
+
59
+
60
+ # ─────────────────────────────────────────
61
+ # ROUTES
62
+ # ─────────────────────────────────────────
63
+
64
+ @app.get("/")
65
+ def root():
66
+ return {
67
+ "name": "Data Cleaning OpenEnv",
68
+ "version": "1.0.0",
69
+ "status": "running",
70
+ "tasks": VALID_TASKS,
71
+ "endpoints": {
72
+ "reset": "POST /reset/{task_id}",
73
+ "step": "POST /step/{task_id}",
74
+ "state": "GET /state/{task_id}",
75
+ "tasks": "GET /tasks",
76
+ "health": "GET /health",
77
+ "docs": "GET /docs"
78
+ }
79
+ }
80
+
81
+
82
+ @app.get("/health")
83
+ def health():
84
+ return {
85
+ "status": "ok",
86
+ "tasks_loaded": len(envs)
87
+ }
88
+
89
+
90
+ @app.get("/tasks")
91
+ def list_tasks():
92
+ return {
93
+ "tasks": [
94
+ {
95
+ "task_id": "easy_dedup_rename",
96
+ "difficulty": "easy",
97
+ "description": (
98
+ "Remove duplicate rows and rename columns "
99
+ "to snake_case in an employee dataset."
100
+ ),
101
+ "max_steps": 10,
102
+ "operations": ["remove_duplicates", "rename_columns", "finish"]
103
+ },
104
+ {
105
+ "task_id": "medium_missing_dtype",
106
+ "difficulty": "medium",
107
+ "description": (
108
+ "Fill missing values using correct strategies "
109
+ "and fix wrong data types in a customer dataset."
110
+ ),
111
+ "max_steps": 15,
112
+ "operations": ["fill_missing", "fix_dtype", "finish"]
113
+ },
114
+ {
115
+ "task_id": "hard_full_pipeline",
116
+ "difficulty": "hard",
117
+ "description": (
118
+ "Run a full cleaning pipeline: remove duplicates, "
119
+ "fill missing values, fix dtypes, remove outliers, "
120
+ "and validate schema on an orders dataset."
121
+ ),
122
+ "max_steps": 20,
123
+ "operations": [
124
+ "remove_duplicates", "fill_missing", "fix_dtype",
125
+ "remove_outliers", "validate_schema", "finish"
126
+ ]
127
+ }
128
+ ]
129
+ }
130
+
131
+
132
+ @app.post("/reset/{task_id}")
133
+ def reset(task_id: str):
134
+ """Reset environment and start fresh episode."""
135
+ env = get_env(task_id)
136
+ try:
137
+ result = env.reset()
138
+ return result.dict()
139
+ except Exception as e:
140
+ raise HTTPException(
141
+ status_code=500,
142
+ detail=f"Reset failed: {str(e)}"
143
+ )
144
+
145
+
146
+ @app.post("/step/{task_id}")
147
+ def step(task_id: str, action: Action):
148
+ """Take one action in the environment."""
149
+ env = get_env(task_id)
150
+ if env.current_df is None:
151
+ raise HTTPException(
152
+ status_code=400,
153
+ detail="Environment not initialized. Call /reset/{task_id} first."
154
+ )
155
+ try:
156
+ result = env.step(action)
157
+ return result.dict()
158
+ except Exception as e:
159
+ raise HTTPException(
160
+ status_code=500,
161
+ detail=f"Step failed: {str(e)}"
162
+ )
163
+
164
+
165
+ @app.get("/state/{task_id}")
166
+ def state(task_id: str):
167
+ """Get current environment state."""
168
+ env = get_env(task_id)
169
+ try:
170
+ return env.state()
171
+ except Exception as e:
172
+ raise HTTPException(
173
+ status_code=500,
174
+ detail=f"State failed: {str(e)}"
175
+ )
176
+
177
+
178
+ @app.get("/validate")
179
+ def validate():
180
+ """OpenEnv spec validation endpoint."""
181
+ results = {}
182
+ for task_id in VALID_TASKS:
183
+ try:
184
+ env = DataCleaningEnv(task_id=task_id)
185
+ # Test reset
186
+ reset_result = env.reset()
187
+ assert reset_result.observation is not None
188
+ assert reset_result.reward is not None
189
+ assert reset_result.done == False
190
+
191
+ # Test step
192
+ from models import Action
193
+ action = Action(
194
+ operation="remove_duplicates",
195
+ parameters={}
196
+ )
197
+ step_result = env.step(action)
198
+ assert step_result.observation is not None
199
+ assert 0.0 <= step_result.reward.total <= 1.0
200
+
201
+ # Test state
202
+ state_result = env.state()
203
+ assert "task_id" in state_result
204
+
205
+ results[task_id] = {
206
+ "status": "passed",
207
+ "reset": "ok",
208
+ "step": "ok",
209
+ "state": "ok",
210
+ "reward_range": f"{step_result.reward.total}"
211
+ }
212
+ except Exception as e:
213
+ results[task_id] = {
214
+ "status": "failed",
215
+ "error": str(e)
216
+ }
217
+
218
+ all_passed = all(r["status"] == "passed" for r in results.values())
219
+ return {
220
+ "openenv_valid": all_passed,
221
+ "tasks": results
222
+ }