thorodin103 commited on
Commit
7fbdbee
·
verified ·
1 Parent(s): 9493f84

Upload static/index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. static/index.html +580 -348
static/index.html CHANGED
@@ -1,3 +1,4 @@
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
@@ -6,106 +7,461 @@
6
  <title>Data Cleaning OpenEnv</title>
7
  <style>
8
  * { margin: 0; padding: 0; box-sizing: border-box; }
9
- body { font-family: 'Segoe UI', sans-serif; background: #0f1117; color: #e0e0e0; min-height: 100vh; }
10
- .header { background: linear-gradient(135deg, #1a1f2e, #2d3561); padding: 30px 40px; border-bottom: 2px solid #3d4f7c; display: flex; align-items: center; gap: 20px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  .header-icon { font-size: 48px; }
12
- .header h1 { font-size: 28px; color: #7eb8f7; margin-bottom: 6px; }
13
- .header p { color: #8892b0; font-size: 14px; }
14
- .badge { background: #2ecc71; color: #000; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; margin-left: 10px; }
15
- .container { max-width: 1200px; margin: 0 auto; padding: 30px 20px; }
16
- .stats-bar { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 30px; }
17
- .stat-card { background: #1a1f2e; border: 1px solid #2d3561; border-radius: 12px; padding: 20px; text-align: center; }
18
- .stat-value { font-size: 32px; font-weight: bold; color: #7eb8f7; }
19
- .stat-label { font-size: 12px; color: #8892b0; margin-top: 4px; }
20
- .section-title { font-size: 18px; color: #7eb8f7; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 1px solid #2d3561; }
21
- .tasks-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-bottom: 30px; }
22
- .task-card { background: #1a1f2e; border: 1px solid #2d3561; border-radius: 12px; padding: 20px; cursor: pointer; transition: all 0.2s; }
23
- .task-card:hover { border-color: #7eb8f7; transform: translateY(-2px); }
24
- .task-card.active { border-color: #7eb8f7; background: #1e2a3a; }
25
- .task-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
26
- .task-name { font-size: 14px; font-weight: bold; color: #e0e0e0; }
27
- .difficulty { padding: 3px 10px; border-radius: 20px; font-size: 11px; font-weight: bold; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  .easy { background: #1a472a; color: #2ecc71; }
29
  .medium { background: #472a1a; color: #f39c12; }
30
  .hard { background: #471a1a; color: #e74c3c; }
31
- .expert { background: #3d1a47; color: #9b59b6; }
32
- .task-desc { font-size: 12px; color: #8892b0; line-height: 1.5; }
33
- .task-score { margin-top: 12px; display: flex; align-items: center; gap: 8px; }
34
- .score-bar-bg { flex: 1; height: 6px; background: #2d3561; border-radius: 3px; overflow: hidden; }
35
- .score-bar-fill { height: 100%; border-radius: 3px; background: linear-gradient(90deg, #3498db, #2ecc71); }
36
- .score-text { font-size: 12px; color: #7eb8f7; min-width: 40px; }
37
- .demo-section { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px; }
38
- .panel { background: #1a1f2e; border: 1px solid #2d3561; border-radius: 12px; padding: 20px; }
39
- .panel-title { font-size: 14px; font-weight: bold; color: #7eb8f7; margin-bottom: 16px; display: flex; align-items: center; gap: 8px; }
40
- .table-wrapper { overflow-x: auto; max-height: 200px; overflow-y: auto; }
41
- table { width: 100%; border-collapse: collapse; font-size: 12px; }
42
- th { background: #2d3561; color: #7eb8f7; padding: 8px 12px; text-align: left; position: sticky; top: 0; }
43
- td { padding: 6px 12px; border-bottom: 1px solid #1e2438; color: #c0c0c0; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  tr:hover td { background: #1e2438; }
45
  .null-cell { color: #e74c3c; font-style: italic; }
46
- .actions-list { display: flex; flex-direction: column; gap: 8px; }
47
- .action-btn { background: #1e2438; border: 1px solid #2d3561; border-radius: 8px; padding: 10px 16px; color: #e0e0e0; cursor: pointer; font-size: 13px; text-align: left; transition: all 0.2s; display: flex; justify-content: space-between; align-items: center; }
48
- .action-btn:hover:not(:disabled) { border-color: #7eb8f7; background: #253050; }
49
- .action-btn:disabled { opacity: 0.4; cursor: not-allowed; }
50
- .action-btn.used { border-color: #2ecc71; color: #2ecc71; }
51
- .action-tag { font-size: 10px; padding: 2px 8px; border-radius: 10px; background: #2d3561; color: #8892b0; }
52
- .reward-display { text-align: center; padding: 10px; }
53
- .reward-circle { width: 100px; height: 100px; border-radius: 50%; border: 4px solid #2d3561; display: flex; flex-direction: column; align-items: center; justify-content: center; margin: 0 auto 16px; background: #0f1117; transition: all 0.3s; }
54
- .reward-number { font-size: 24px; font-weight: bold; color: #7eb8f7; }
55
- .reward-label { font-size: 10px; color: #8892b0; }
56
- .reward-components { display: flex; flex-direction: column; gap: 6px; text-align: left; }
57
- .component-row { display: flex; align-items: center; gap: 8px; font-size: 12px; }
58
- .component-name { width: 130px; color: #8892b0; }
59
- .component-bar-bg { flex: 1; height: 8px; background: #2d3561; border-radius: 4px; overflow: hidden; }
60
- .component-bar-fill { height: 100%; border-radius: 4px; transition: width 0.5s; }
61
- .component-val { width: 40px; text-align: right; color: #7eb8f7; }
62
- .message-log { background: #0f1117; border: 1px solid #2d3561; border-radius: 8px; padding: 12px; max-height: 140px; overflow-y: auto; font-family: monospace; font-size: 12px; }
63
- .log-entry { padding: 3px 0; border-bottom: 1px solid #1a1f2e; color: #8892b0; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  .log-entry.success { color: #2ecc71; }
65
  .log-entry.info { color: #7eb8f7; }
66
  .log-entry.warning { color: #f39c12; }
67
- .controls { display: flex; gap: 12px; margin-bottom: 20px; flex-wrap: wrap; align-items: center; }
68
- .btn { padding: 10px 24px; border-radius: 8px; border: none; cursor: pointer; font-size: 14px; font-weight: bold; transition: all 0.2s; }
69
- .btn-primary { background: #3d6ef7; color: white; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  .btn-primary:hover { background: #5a87ff; }
71
- .btn-secondary { background: #2d3561; color: #e0e0e0; }
 
 
 
72
  .btn-secondary:hover { background: #3d4f7c; }
73
- .done-banner { background: #1a472a; border: 1px solid #2ecc71; border-radius: 8px; padding: 12px 20px; color: #2ecc71; text-align: center; font-weight: bold; display: none; margin-top: 12px; }
74
- .api-section { margin-bottom: 30px; }
75
- .endpoint-list { display: flex; flex-direction: column; gap: 8px; }
76
- .endpoint { background: #1a1f2e; border: 1px solid #2d3561; border-radius: 8px; padding: 12px 16px; display: flex; align-items: center; gap: 12px; font-size: 13px; }
77
- .method { padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: bold; min-width: 45px; text-align: center; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  .get { background: #1a472a; color: #2ecc71; }
79
  .post { background: #472a1a; color: #f39c12; }
80
- .endpoint-path { color: #7eb8f7; font-family: monospace; flex: 1; }
81
- .endpoint-desc { color: #8892b0; font-size: 12px; }
82
- .endpoint a { color: #7eb8f7; text-decoration: none; }
83
- .status-dot { width: 8px; height: 8px; border-radius: 50%; background: #2ecc71; display: inline-block; margin-right: 6px; animation: pulse 2s infinite; }
84
- @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
85
- .loading { display: inline-block; width: 16px; height: 16px; border: 2px solid #2d3561; border-top-color: #7eb8f7; border-radius: 50%; animation: spin 0.8s linear infinite; }
86
- @keyframes spin { to { transform: rotate(360deg); } }
87
- .lb-table { width: 100%; border-collapse: collapse; font-size: 13px; }
88
- .lb-table th { background: #2d3561; color: #7eb8f7; padding: 8px 12px; text-align: left; }
89
- .lb-table td { padding: 8px 12px; border-bottom: 1px solid #1e2438; color: #c0c0c0; }
90
- .lb-table tr:hover td { background: #1e2438; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  </style>
92
  </head>
93
  <body>
94
 
 
95
  <div class="header">
96
  <div class="header-icon">🧹</div>
97
  <div>
98
- <h1>Data Cleaning OpenEnv <span class="badge">● LIVE</span></h1>
 
 
99
  <p>An OpenEnv-compliant environment where AI agents learn to clean messy real-world datasets</p>
100
  </div>
101
  </div>
102
 
103
  <div class="container">
104
 
105
- <!-- Stats -->
106
  <div class="stats-bar">
107
  <div class="stat-card">
108
- <div class="stat-value">4</div>
109
  <div class="stat-label">Tasks Available</div>
110
  </div>
111
  <div class="stat-card">
@@ -113,7 +469,7 @@
113
  <div class="stat-label">Cleaning Operations</div>
114
  </div>
115
  <div class="stat-card">
116
- <div class="stat-value" id="live-avg-score">1.00</div>
117
  <div class="stat-label">Baseline Avg Score</div>
118
  </div>
119
  <div class="stat-card">
@@ -130,9 +486,13 @@
130
  <div class="task-name">Deduplicate & Rename</div>
131
  <div class="difficulty easy">EASY</div>
132
  </div>
133
- <div class="task-desc">Remove duplicate rows and rename columns to snake_case in an employee dataset.</div>
 
 
134
  <div class="task-score">
135
- <div class="score-bar-bg"><div class="score-bar-fill" style="width:100%"></div></div>
 
 
136
  <div class="score-text">1.00</div>
137
  </div>
138
  </div>
@@ -141,10 +501,14 @@
141
  <div class="task-name">Missing Values & Types</div>
142
  <div class="difficulty medium">MEDIUM</div>
143
  </div>
144
- <div class="task-desc">Fill missing values using correct strategies and fix wrong data types in a customer dataset.</div>
 
 
145
  <div class="task-score">
146
- <div class="score-bar-bg"><div class="score-bar-fill" style="width:100%"></div></div>
147
- <div class="score-text">1.00</div>
 
 
148
  </div>
149
  </div>
150
  <div class="task-card" onclick="selectTask('hard_full_pipeline')" id="card-hard">
@@ -152,52 +516,66 @@
152
  <div class="task-name">Full Pipeline</div>
153
  <div class="difficulty hard">HARD</div>
154
  </div>
155
- <div class="task-desc">Complete cleaning pipeline: duplicates + missing + types + outliers + schema validation.</div>
156
- <div class="task-score">
157
- <div class="score-bar-bg"><div class="score-bar-fill" style="width:100%"></div></div>
158
- <div class="score-text">1.00</div>
159
- </div>
160
- </div>
161
- <div class="task-card" onclick="selectTask('expert_sales_pipeline')" id="card-expert">
162
- <div class="task-header">
163
- <div class="task-name">Sales Pipeline</div>
164
- <div class="difficulty expert">EXPERT</div>
165
  </div>
166
- <div class="task-desc">Expert level: sales data with duplicates + missing + case standardization + outliers + schema.</div>
167
  <div class="task-score">
168
- <div class="score-bar-bg"><div class="score-bar-fill" style="width:100%"></div></div>
169
- <div class="score-text">1.00</div>
 
 
170
  </div>
171
  </div>
172
  </div>
173
 
174
- <!-- Live Demo -->
175
  <div class="section-title">🎮 Live Demo — Try It Yourself</div>
176
  <div class="controls">
177
- <button class="btn btn-primary" onclick="resetEnv()" id="reset-btn">🔄 Reset Environment</button>
178
- <button class="btn btn-secondary" onclick="resetAll()">⟳ Start Over</button>
179
- <span id="task-label" style="align-self:center;color:#8892b0;font-size:13px;">Task: easy_dedup_rename</span>
 
 
 
 
 
 
180
  </div>
181
 
182
  <div class="demo-section">
183
- <div style="display:flex;flex-direction:column;gap:16px;">
 
 
 
 
184
  <div class="panel">
185
- <div class="panel-title">📊 Current Dataset
186
- <span id="shape-badge" style="font-size:11px;color:#8892b0;font-weight:normal;">— reset to load</span>
 
 
 
187
  </div>
188
  <div class="table-wrapper" id="table-wrapper">
189
- <div style="color:#8892b0;font-size:13px;padding:20px;text-align:center;">Click Reset Environment to load dirty dataset</div>
 
 
190
  </div>
191
  </div>
 
 
192
  <div class="panel">
193
  <div class="panel-title">⚡ Available Actions</div>
194
  <div class="actions-list" id="actions-list">
195
- <div style="color:#8892b0;font-size:13px;">Reset first to enable actions</div>
196
  </div>
197
  </div>
 
198
  </div>
199
 
200
- <div style="display:flex;flex-direction:column;gap:16px;">
 
 
 
201
  <div class="panel">
202
  <div class="panel-title">🏆 Reward Score</div>
203
  <div class="reward-display">
@@ -206,50 +584,24 @@
206
  <div class="reward-label">SCORE</div>
207
  </div>
208
  <div class="reward-components" id="reward-components">
209
- <div style="color:#8892b0;font-size:12px;text-align:center;">Reset to see breakdown</div>
 
 
210
  </div>
211
  </div>
212
- <div class="done-banner" id="done-banner">✅ Episode Complete!</div>
213
-
214
- <!-- Submit to leaderboard -->
215
- <div id="submit-section" style="display:none;margin-top:12px;">
216
- <div style="font-size:12px;color:#8892b0;margin-bottom:8px;">
217
- 🏅 Submit your score to leaderboard:
218
- </div>
219
- <div style="display:flex;gap:8px;">
220
- <input
221
- id="model-name-input"
222
- type="text"
223
- placeholder="Your model/name (e.g. gpt-4o-mini)"
224
- style="flex:1;background:#0f1117;border:1px solid #2d3561;
225
- border-radius:6px;padding:8px 12px;color:#e0e0e0;
226
- font-size:13px;outline:none;"
227
- />
228
- <button
229
- onclick="submitToLeaderboard()"
230
- style="background:#2ecc71;color:#000;border:none;
231
- border-radius:6px;padding:8px 16px;cursor:pointer;
232
- font-weight:bold;font-size:13px;">
233
- Submit
234
- </button>
235
- </div>
236
- <div id="submit-msg" style="font-size:11px;margin-top:6px;"></div>
237
  </div>
238
  </div>
 
 
239
  <div class="panel">
240
  <div class="panel-title">📝 Action Log</div>
241
  <div class="message-log" id="message-log">
242
  <div class="log-entry info">Waiting for reset...</div>
243
  </div>
244
  </div>
245
- </div>
246
- </div>
247
 
248
- <!-- Leaderboard -->
249
- <div class="api-section">
250
- <div class="section-title">🏅 Leaderboard</div>
251
- <div id="leaderboard-table" style="background:#1a1f2e;border:1px solid #2d3561;border-radius:12px;padding:20px;">
252
- <div style="color:#8892b0;text-align:center;">Loading leaderboard...</div>
253
  </div>
254
  </div>
255
 
@@ -260,46 +612,40 @@
260
  <div class="endpoint">
261
  <span class="method get">GET</span>
262
  <span class="endpoint-path">/health</span>
263
- <span class="endpoint-desc">Health check</span>
264
  <a href="/health" target="_blank">Try →</a>
265
  </div>
266
  <div class="endpoint">
267
  <span class="method get">GET</span>
268
  <span class="endpoint-path">/tasks</span>
269
- <span class="endpoint-desc">List all tasks</span>
270
  <a href="/tasks" target="_blank">Try →</a>
271
  </div>
272
  <div class="endpoint">
273
  <span class="method post">POST</span>
274
  <span class="endpoint-path">/reset/{task_id}</span>
275
- <span class="endpoint-desc">Reset environment</span>
276
  </div>
277
  <div class="endpoint">
278
  <span class="method post">POST</span>
279
  <span class="endpoint-path">/step/{task_id}</span>
280
- <span class="endpoint-desc">Take action, get reward</span>
281
  </div>
282
  <div class="endpoint">
283
  <span class="method get">GET</span>
284
  <span class="endpoint-path">/state/{task_id}</span>
285
- <span class="endpoint-desc">Current state</span>
286
- </div>
287
- <div class="endpoint">
288
- <span class="method get">GET</span>
289
- <span class="endpoint-path">/leaderboard</span>
290
- <span class="endpoint-desc">View rankings</span>
291
- <a href="/leaderboard" target="_blank">Try →</a>
292
  </div>
293
  <div class="endpoint">
294
  <span class="method get">GET</span>
295
  <span class="endpoint-path">/validate</span>
296
- <span class="endpoint-desc">OpenEnv validation</span>
297
  <a href="/validate" target="_blank">Try →</a>
298
  </div>
299
  <div class="endpoint">
300
  <span class="method get">GET</span>
301
  <span class="endpoint-path">/docs</span>
302
- <span class="endpoint-desc">API documentation</span>
303
  <a href="/docs" target="_blank">Try →</a>
304
  </div>
305
  </div>
@@ -315,34 +661,29 @@
315
  let usedActions = new Set();
316
 
317
  const TASK_OPERATIONS = {
318
- 'easy_dedup_rename': ['remove_duplicates', 'rename_columns', 'finish'],
319
- 'medium_missing_dtype': ['fill_missing_mean', 'fill_missing_mode', 'fill_missing_median', 'fix_dtype', 'finish'],
320
- 'hard_full_pipeline': ['remove_duplicates', 'fill_missing_mean', 'fill_missing_mode', 'fill_missing_median', 'fix_dtype', 'remove_outliers', 'validate_schema', 'finish'],
321
- 'expert_sales_pipeline': ['remove_duplicates', 'rename_columns', 'fill_missing_mean', 'fill_missing_mode', 'fill_missing_median', 'fix_dtype', 'remove_outliers', 'validate_schema', 'finish']
322
  };
323
 
324
  const ACTION_PARAMS = {
325
- 'remove_duplicates': {},
326
- 'rename_columns': {},
327
- 'fill_missing_mean': {strategy: 'mean'},
328
- 'fill_missing_mode': {strategy: 'mode'},
329
- 'fill_missing_median': {strategy: 'median'},
330
- 'fix_dtype': {dtype: 'auto'},
331
- 'remove_outliers': {method: 'iqr'},
332
- 'validate_schema': {},
333
- 'finish': {}
334
  };
335
 
336
  const ACTION_DESC = {
337
- 'remove_duplicates': 'Delete repeated rows',
338
- 'rename_columns': 'Convert to snake_case',
339
- 'fill_missing_mean': 'Fill cells with MEAN',
340
- 'fill_missing_mode': 'Fill cells with MODE',
341
- 'fill_missing_median': 'Fill cells with MEDIAN',
342
- 'fix_dtype': 'Fix wrong data types',
343
- 'remove_outliers': 'Remove extreme values',
344
- 'validate_schema': 'Check column names',
345
- 'finish': 'Submit cleaned dataset'
346
  };
347
 
348
  const COMPONENT_COLORS = {
@@ -353,17 +694,15 @@
353
  'schema_score': '#2ecc71',
354
  };
355
 
356
- const CARD_MAP = {
357
- 'easy_dedup_rename': 'card-easy',
358
- 'medium_missing_dtype': 'card-medium',
359
- 'hard_full_pipeline': 'card-hard',
360
- 'expert_sales_pipeline': 'card-expert'
361
- };
362
-
363
  function selectTask(taskId) {
364
  currentTask = taskId;
365
  document.querySelectorAll('.task-card').forEach(c => c.classList.remove('active'));
366
- document.getElementById(CARD_MAP[taskId]).classList.add('active');
 
 
 
 
 
367
  document.getElementById('task-label').textContent = 'Task: ' + taskId;
368
  resetAll();
369
  addLog('Switched to task: ' + taskId, 'info');
@@ -375,17 +714,15 @@
375
  usedActions = new Set();
376
  document.getElementById('reward-number').textContent = '—';
377
  document.getElementById('reward-circle').style.borderColor = '#2d3561';
378
- document.getElementById('reward-components').innerHTML =
379
- '<div style="color:#8892b0;font-size:12px;text-align:center;">Reset to see breakdown</div>';
380
- document.getElementById('table-wrapper').innerHTML =
381
- '<div style="color:#8892b0;font-size:13px;padding:20px;text-align:center;">Click Reset Environment to load dirty dataset</div>';
382
- document.getElementById('actions-list').innerHTML =
383
  '<div style="color:#8892b0;font-size:13px;">Reset first to enable actions</div>';
384
  document.getElementById('shape-badge').textContent = '— reset to load';
385
  document.getElementById('done-banner').style.display = 'none';
386
- document.getElementById('submit-section').style.display = 'none';
387
- document.getElementById('submit-msg').textContent = '';
388
- document.getElementById('message-log').innerHTML =
389
  '<div class="log-entry info">Ready — click Reset Environment</div>';
390
  }
391
 
@@ -402,36 +739,36 @@
402
  const res = await fetch(BASE + '/reset/' + currentTask, {method: 'POST'});
403
  const data = await res.json();
404
  const obs = data.observation;
 
405
  updateTable(obs.sample_rows, obs.columns, obs.duplicate_count, obs.missing_values);
406
  updateActions(obs.available_operations, false);
407
- document.getElementById('shape-badge').textContent =
408
- obs.shape[0] + ' rows x ' + obs.shape[1] + ' cols | ' +
409
- obs.duplicate_count + ' dups | ' +
410
  Object.values(obs.missing_values).reduce((a,b)=>a+b,0) + ' missing';
411
  document.getElementById('reward-number').textContent = '0.00';
412
  document.getElementById('reward-circle').style.borderColor = '#2d3561';
413
- addLog('Environment reset! ' + obs.message, 'success');
414
- addLog(obs.task_description.substring(0,100) + '...', 'info');
415
  } catch(e) {
416
- addLog('Reset failed: ' + e.message, 'warning');
417
  }
 
418
  btn.disabled = false;
419
  btn.innerHTML = '🔄 Reset Environment';
420
  }
421
 
422
  async function takeAction(operation) {
423
- if (episodeDone) { addLog('Episode done — reset first', 'warning'); return; }
424
- stepCount++;
425
- const params = ACTION_PARAMS[operation] || {};
426
-
427
- // Map fill_missing_* to fill_missing operation
428
- let actualOperation = operation;
429
- if (operation.startsWith('fill_missing_')) {
430
- actualOperation = 'fill_missing';
431
  }
432
 
 
 
433
  addLog('[Step ' + stepCount + '] → ' + operation + ' ' + JSON.stringify(params), 'info');
434
 
 
435
  const btn = document.getElementById('btn-' + operation);
436
  if (btn) { btn.disabled = true; btn.innerHTML = '<span class="loading"></span> Running...'; }
437
 
@@ -439,33 +776,42 @@
439
  const res = await fetch(BASE + '/step/' + currentTask, {
440
  method: 'POST',
441
  headers: {'Content-Type': 'application/json'},
442
- body: JSON.stringify({operation: actualOperation, parameters: params})
443
  });
444
  const data = await res.json();
445
- const obs = data.observation;
446
- const rew = data.reward;
447
 
 
448
  updateTable(obs.sample_rows, obs.columns, obs.duplicate_count, obs.missing_values);
449
- document.getElementById('shape-badge').textContent =
450
- obs.shape[0] + ' rows x ' + obs.shape[1] + ' cols | ' +
451
- obs.duplicate_count + ' dups | ' +
452
  Object.values(obs.missing_values).reduce((a,b)=>a+b,0) + ' missing';
 
 
453
  updateReward(rew);
454
- addLog(' ' + obs.message.substring(0,80), rew.total > 0.5 ? 'success' : 'info');
455
- addLog(' Reward: ' + rew.total.toFixed(4), 'success');
456
 
 
 
 
 
 
457
  usedActions.add(operation);
458
- if (btn) { btn.classList.add('used'); btn.innerHTML = '✅ ' + operation; }
 
 
 
459
 
460
  if (data.done) {
461
  episodeDone = true;
462
  document.getElementById('done-banner').style.display = 'block';
463
- document.getElementById('submit-section').style.display = 'block';
464
- addLog('Episode complete! Final score: ' + rew.total.toFixed(4), 'success');
465
  updateActions(obs.available_operations, true);
466
  }
 
467
  } catch(e) {
468
- addLog('Action failed: ' + e.message, 'warning');
469
  if (btn) { btn.disabled = false; btn.innerHTML = operation; }
470
  }
471
  }
@@ -499,8 +845,10 @@
499
  btn.className = 'action-btn' + (usedActions.has(op) ? ' used' : '');
500
  btn.id = 'btn-' + op;
501
  btn.disabled = done;
502
- btn.innerHTML = '<span>' + (usedActions.has(op) ? '✅ ' : '') + op + '</span>' +
503
- '<span class="action-tag">' + (ACTION_DESC[op] || '') + '</span>';
 
 
504
  btn.onclick = () => takeAction(op);
505
  list.appendChild(btn);
506
  });
@@ -508,23 +856,32 @@
508
 
509
  function updateReward(rew) {
510
  const total = rew.total || 0;
511
- finalReward = total;
512
  document.getElementById('reward-number').textContent = total.toFixed(4);
 
 
513
  let color = '#e74c3c';
514
  if (total >= 0.8) color = '#2ecc71';
515
  else if (total >= 0.5) color = '#f39c12';
516
  document.getElementById('reward-circle').style.borderColor = color;
517
 
 
518
  const comps = ['duplicate_score','missing_score','dtype_score','outlier_score','schema_score'];
519
  let html = '';
520
  comps.forEach(c => {
521
  const val = rew[c] || 0;
 
522
  const color = COMPONENT_COLORS[c] || '#7eb8f7';
523
- html += '<div class="component-row">' +
524
- '<div class="component-name">' + c.replace('_score','') + '</div>' +
525
- '<div class="component-bar-bg"><div class="component-bar-fill" style="width:' +
526
- (val*100) + '%;background:' + color + '"></div></div>' +
527
- '<div class="component-val">' + val.toFixed(2) + '</div></div>';
 
 
 
 
 
 
528
  });
529
  document.getElementById('reward-components').innerHTML = html;
530
  }
@@ -538,136 +895,11 @@
538
  log.scrollTop = log.scrollHeight;
539
  }
540
 
541
- // Track final reward for submission
542
- let finalReward = 0.0;
543
-
544
- async function submitToLeaderboard() {
545
- const modelName = document.getElementById('model-name-input').value.trim();
546
- if (!modelName) {
547
- document.getElementById('submit-msg').style.color = '#e74c3c';
548
- document.getElementById('submit-msg').textContent = '❌ Please enter your model/name first!';
549
- return;
550
- }
551
-
552
- const entry = {
553
- model_name: modelName,
554
- task_id: currentTask,
555
- score: finalReward,
556
- steps: stepCount
557
- };
558
-
559
- try {
560
- const r = await fetch(BASE + '/leaderboard/submit', {
561
- method: 'POST',
562
- headers: {'Content-Type': 'application/json'},
563
- body: JSON.stringify(entry)
564
- });
565
- const data = await r.json();
566
- if (data.status === 'submitted') {
567
- document.getElementById('submit-msg').style.color = '#2ecc71';
568
- document.getElementById('submit-msg').textContent =
569
- '✅ Score ' + finalReward.toFixed(4) + ' submitted for ' + modelName + '!';
570
- addLog('Submitted to leaderboard: ' + modelName + ' = ' + finalReward.toFixed(4), 'success');
571
- // Refresh leaderboard
572
- setTimeout(loadLeaderboard, 1000);
573
- }
574
- } catch(e) {
575
- document.getElementById('submit-msg').style.color = '#e74c3c';
576
- document.getElementById('submit-msg').textContent = '❌ Submit failed: ' + e.message;
577
- }
578
- }
579
-
580
- async function loadLeaderboard() {
581
- try {
582
- const r = await fetch(BASE + '/leaderboard');
583
- const data = await r.json();
584
- const lb = data.leaderboard;
585
- let html = '<table class="lb-table"><thead><tr>';
586
- ['Rank','Model','Easy','Medium','Hard','Expert','Avg'].forEach(h => {
587
- html += '<th>' + h + '</th>';
588
- });
589
- html += '</tr></thead><tbody>';
590
- lb.forEach(entry => {
591
- const scores = entry.scores || {};
592
- html += '<tr>' +
593
- '<td style="color:#f39c12;">#' + entry.rank + '</td>' +
594
- '<td>' + entry.model_name + '</td>' +
595
- '<td style="color:#2ecc71;text-align:center;">' + (entry.easy_score || scores.easy_dedup_rename || '-') + '</td>' +
596
- '<td style="color:#f39c12;text-align:center;">' + (entry.medium_score || scores.medium_missing_dtype || '-') + '</td>' +
597
- '<td style="color:#e74c3c;text-align:center;">' + (entry.hard_score || scores.hard_full_pipeline || '-') + '</td>' +
598
- '<td style="color:#9b59b6;text-align:center;">' + (entry.expert_score || scores.expert_sales_pipeline || '-') + '</td>' +
599
- '<td style="color:#7eb8f7;font-weight:bold;text-align:center;">' + entry.avg_score + '</td>' +
600
- '</tr>';
601
- });
602
- html += '</tbody></table>';
603
- html += '<div style="color:#8892b0;font-size:11px;margin-top:8px;">Total submissions: ' + data.total_submissions + '</div>';
604
- document.getElementById('leaderboard-table').innerHTML = html;
605
- } catch(e) {
606
- document.getElementById('leaderboard-table').innerHTML =
607
- '<div style="color:#e74c3c;">Failed to load leaderboard</div>';
608
- }
609
- }
610
-
611
- // On load — show actions for default task immediately
612
- function showDefaultActions(taskId) {
613
- const ops = TASK_OPERATIONS[taskId] || [];
614
- const list = document.getElementById('actions-list');
615
- list.innerHTML = '';
616
-
617
- const TASK_INFO = {
618
- 'easy_dedup_rename': '🟢 EASY — ' + ops.length + ' actions | Max 10 steps',
619
- 'medium_missing_dtype': '🟡 MEDIUM — ' + ops.length + ' actions | Max 15 steps',
620
- 'hard_full_pipeline': '🔴 HARD — ' + ops.length + ' actions | Max 20 steps',
621
- 'expert_sales_pipeline': '🟣 EXPERT — ' + ops.length + ' actions | Max 25 steps'
622
- };
623
-
624
- const banner = document.createElement('div');
625
- banner.style.cssText = 'background:#1e2438;border:1px solid #3d4f7c;border-radius:8px;padding:10px;margin-bottom:10px;font-size:12px;color:#8892b0;';
626
- banner.textContent = TASK_INFO[taskId] || '';
627
- list.appendChild(banner);
628
-
629
- ops.forEach(op => {
630
- const btn = document.createElement('button');
631
- btn.className = 'action-btn';
632
- btn.id = 'btn-' + op;
633
- btn.disabled = true;
634
- btn.innerHTML = '<span>' + op + '</span>' +
635
- '<span class="action-tag">' + (ACTION_DESC[op] || '') + '</span>';
636
- btn.onclick = () => takeAction(op);
637
- list.appendChild(btn);
638
- });
639
-
640
- const hint = document.createElement('div');
641
- hint.style.cssText = 'color:#f39c12;font-size:11px;margin-top:8px;text-align:center;';
642
- hint.textContent = '⚠️ Click Reset Environment to enable actions';
643
- list.appendChild(hint);
644
- }
645
-
646
- // Show actions for default task on page load
647
- showDefaultActions(currentTask);
648
-
649
- // On load
650
  fetch(BASE + '/health')
651
  .then(r => r.json())
652
- .then(d => addLog('Environment online — ' + d.tasks_loaded + ' tasks loaded', 'success'))
653
- .catch(() => addLog('Cannot reach environment', 'warning'));
654
-
655
- // Load live avg score from leaderboard
656
- fetch(BASE + '/leaderboard')
657
- .then(r => r.json())
658
- .then(data => {
659
- if (data.leaderboard && data.leaderboard.length > 0) {
660
- const best = data.leaderboard[0].avg_score;
661
- document.getElementById('live-avg-score').textContent = best;
662
- } else {
663
- document.getElementById('live-avg-score').textContent = 'N/A';
664
- }
665
- })
666
- .catch(() => {
667
- document.getElementById('live-avg-score').textContent = '0.86';
668
- });
669
-
670
- loadLeaderboard();
671
  </script>
672
  </body>
673
- </html>
 
1
+
2
  <!DOCTYPE html>
3
  <html lang="en">
4
  <head>
 
7
  <title>Data Cleaning OpenEnv</title>
8
  <style>
9
  * { margin: 0; padding: 0; box-sizing: border-box; }
10
+
11
+ body {
12
+ font-family: 'Segoe UI', sans-serif;
13
+ background: #0f1117;
14
+ color: #e0e0e0;
15
+ min-height: 100vh;
16
+ }
17
+
18
+ /* ── Header ── */
19
+ .header {
20
+ background: linear-gradient(135deg, #1a1f2e, #2d3561);
21
+ padding: 30px 40px;
22
+ border-bottom: 2px solid #3d4f7c;
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 20px;
26
+ }
27
  .header-icon { font-size: 48px; }
28
+ .header h1 {
29
+ font-size: 28px;
30
+ color: #7eb8f7;
31
+ margin-bottom: 6px;
32
+ }
33
+ .header p {
34
+ color: #8892b0;
35
+ font-size: 14px;
36
+ }
37
+ .badge {
38
+ background: #2ecc71;
39
+ color: #000;
40
+ padding: 4px 12px;
41
+ border-radius: 20px;
42
+ font-size: 12px;
43
+ font-weight: bold;
44
+ margin-left: 10px;
45
+ }
46
+
47
+ /* ── Layout ── */
48
+ .container {
49
+ max-width: 1200px;
50
+ margin: 0 auto;
51
+ padding: 30px 20px;
52
+ }
53
+
54
+ /* ── Stats Bar ── */
55
+ .stats-bar {
56
+ display: grid;
57
+ grid-template-columns: repeat(4, 1fr);
58
+ gap: 16px;
59
+ margin-bottom: 30px;
60
+ }
61
+ .stat-card {
62
+ background: #1a1f2e;
63
+ border: 1px solid #2d3561;
64
+ border-radius: 12px;
65
+ padding: 20px;
66
+ text-align: center;
67
+ }
68
+ .stat-value {
69
+ font-size: 32px;
70
+ font-weight: bold;
71
+ color: #7eb8f7;
72
+ }
73
+ .stat-label {
74
+ font-size: 12px;
75
+ color: #8892b0;
76
+ margin-top: 4px;
77
+ }
78
+
79
+ /* ── Tasks Grid ── */
80
+ .section-title {
81
+ font-size: 18px;
82
+ color: #7eb8f7;
83
+ margin-bottom: 16px;
84
+ padding-bottom: 8px;
85
+ border-bottom: 1px solid #2d3561;
86
+ }
87
+ .tasks-grid {
88
+ display: grid;
89
+ grid-template-columns: repeat(3, 1fr);
90
+ gap: 20px;
91
+ margin-bottom: 30px;
92
+ }
93
+ .task-card {
94
+ background: #1a1f2e;
95
+ border: 1px solid #2d3561;
96
+ border-radius: 12px;
97
+ padding: 20px;
98
+ cursor: pointer;
99
+ transition: all 0.2s;
100
+ }
101
+ .task-card:hover {
102
+ border-color: #7eb8f7;
103
+ transform: translateY(-2px);
104
+ }
105
+ .task-card.active {
106
+ border-color: #7eb8f7;
107
+ background: #1e2a3a;
108
+ }
109
+ .task-header {
110
+ display: flex;
111
+ justify-content: space-between;
112
+ align-items: center;
113
+ margin-bottom: 10px;
114
+ }
115
+ .task-name {
116
+ font-size: 14px;
117
+ font-weight: bold;
118
+ color: #e0e0e0;
119
+ }
120
+ .difficulty {
121
+ padding: 3px 10px;
122
+ border-radius: 20px;
123
+ font-size: 11px;
124
+ font-weight: bold;
125
+ }
126
  .easy { background: #1a472a; color: #2ecc71; }
127
  .medium { background: #472a1a; color: #f39c12; }
128
  .hard { background: #471a1a; color: #e74c3c; }
129
+ .task-desc {
130
+ font-size: 12px;
131
+ color: #8892b0;
132
+ line-height: 1.5;
133
+ }
134
+ .task-score {
135
+ margin-top: 12px;
136
+ display: flex;
137
+ align-items: center;
138
+ gap: 8px;
139
+ }
140
+ .score-bar-bg {
141
+ flex: 1;
142
+ height: 6px;
143
+ background: #2d3561;
144
+ border-radius: 3px;
145
+ overflow: hidden;
146
+ }
147
+ .score-bar-fill {
148
+ height: 100%;
149
+ border-radius: 3px;
150
+ background: linear-gradient(90deg, #3498db, #2ecc71);
151
+ transition: width 0.5s;
152
+ }
153
+ .score-text {
154
+ font-size: 12px;
155
+ color: #7eb8f7;
156
+ min-width: 40px;
157
+ }
158
+
159
+ /* ── Demo Section ── */
160
+ .demo-section {
161
+ display: grid;
162
+ grid-template-columns: 1fr 1fr;
163
+ gap: 20px;
164
+ margin-bottom: 30px;
165
+ }
166
+ .panel {
167
+ background: #1a1f2e;
168
+ border: 1px solid #2d3561;
169
+ border-radius: 12px;
170
+ padding: 20px;
171
+ }
172
+ .panel-title {
173
+ font-size: 14px;
174
+ font-weight: bold;
175
+ color: #7eb8f7;
176
+ margin-bottom: 16px;
177
+ display: flex;
178
+ align-items: center;
179
+ gap: 8px;
180
+ }
181
+
182
+ /* ── Dataset Table ── */
183
+ .table-wrapper {
184
+ overflow-x: auto;
185
+ max-height: 240px;
186
+ overflow-y: auto;
187
+ }
188
+ table {
189
+ width: 100%;
190
+ border-collapse: collapse;
191
+ font-size: 12px;
192
+ }
193
+ th {
194
+ background: #2d3561;
195
+ color: #7eb8f7;
196
+ padding: 8px 12px;
197
+ text-align: left;
198
+ position: sticky;
199
+ top: 0;
200
+ }
201
+ td {
202
+ padding: 6px 12px;
203
+ border-bottom: 1px solid #1e2438;
204
+ color: #c0c0c0;
205
+ }
206
  tr:hover td { background: #1e2438; }
207
  .null-cell { color: #e74c3c; font-style: italic; }
208
+ .dup-row td { background: #2a1a1a !important; }
209
+
210
+ /* ── Actions ── */
211
+ .actions-list {
212
+ display: flex;
213
+ flex-direction: column;
214
+ gap: 8px;
215
+ }
216
+ .action-btn {
217
+ background: #1e2438;
218
+ border: 1px solid #2d3561;
219
+ border-radius: 8px;
220
+ padding: 10px 16px;
221
+ color: #e0e0e0;
222
+ cursor: pointer;
223
+ font-size: 13px;
224
+ text-align: left;
225
+ transition: all 0.2s;
226
+ display: flex;
227
+ justify-content: space-between;
228
+ align-items: center;
229
+ }
230
+ .action-btn:hover:not(:disabled) {
231
+ border-color: #7eb8f7;
232
+ background: #253050;
233
+ }
234
+ .action-btn:disabled {
235
+ opacity: 0.4;
236
+ cursor: not-allowed;
237
+ }
238
+ .action-btn.used {
239
+ border-color: #2ecc71;
240
+ color: #2ecc71;
241
+ }
242
+ .action-tag {
243
+ font-size: 10px;
244
+ padding: 2px 8px;
245
+ border-radius: 10px;
246
+ background: #2d3561;
247
+ color: #8892b0;
248
+ }
249
+
250
+ /* ── Reward Display ── */
251
+ .reward-display {
252
+ text-align: center;
253
+ padding: 20px;
254
+ }
255
+ .reward-circle {
256
+ width: 120px;
257
+ height: 120px;
258
+ border-radius: 50%;
259
+ border: 4px solid #2d3561;
260
+ display: flex;
261
+ flex-direction: column;
262
+ align-items: center;
263
+ justify-content: center;
264
+ margin: 0 auto 16px;
265
+ background: #0f1117;
266
+ transition: all 0.3s;
267
+ }
268
+ .reward-number {
269
+ font-size: 28px;
270
+ font-weight: bold;
271
+ color: #7eb8f7;
272
+ }
273
+ .reward-label {
274
+ font-size: 10px;
275
+ color: #8892b0;
276
+ }
277
+ .reward-components {
278
+ display: flex;
279
+ flex-direction: column;
280
+ gap: 8px;
281
+ text-align: left;
282
+ }
283
+ .component-row {
284
+ display: flex;
285
+ align-items: center;
286
+ gap: 8px;
287
+ font-size: 12px;
288
+ }
289
+ .component-name {
290
+ width: 130px;
291
+ color: #8892b0;
292
+ }
293
+ .component-bar-bg {
294
+ flex: 1;
295
+ height: 8px;
296
+ background: #2d3561;
297
+ border-radius: 4px;
298
+ overflow: hidden;
299
+ }
300
+ .component-bar-fill {
301
+ height: 100%;
302
+ border-radius: 4px;
303
+ transition: width 0.5s;
304
+ }
305
+ .component-val {
306
+ width: 40px;
307
+ text-align: right;
308
+ color: #7eb8f7;
309
+ }
310
+
311
+ /* ── Message Log ── */
312
+ .message-log {
313
+ background: #0f1117;
314
+ border: 1px solid #2d3561;
315
+ border-radius: 8px;
316
+ padding: 12px;
317
+ max-height: 160px;
318
+ overflow-y: auto;
319
+ font-family: monospace;
320
+ font-size: 12px;
321
+ }
322
+ .log-entry {
323
+ padding: 4px 0;
324
+ border-bottom: 1px solid #1a1f2e;
325
+ color: #8892b0;
326
+ }
327
  .log-entry.success { color: #2ecc71; }
328
  .log-entry.info { color: #7eb8f7; }
329
  .log-entry.warning { color: #f39c12; }
330
+
331
+ /* ── Control Buttons ── */
332
+ .controls {
333
+ display: flex;
334
+ gap: 12px;
335
+ margin-bottom: 20px;
336
+ }
337
+ .btn {
338
+ padding: 10px 24px;
339
+ border-radius: 8px;
340
+ border: none;
341
+ cursor: pointer;
342
+ font-size: 14px;
343
+ font-weight: bold;
344
+ transition: all 0.2s;
345
+ }
346
+ .btn-primary {
347
+ background: #3d6ef7;
348
+ color: white;
349
+ }
350
  .btn-primary:hover { background: #5a87ff; }
351
+ .btn-secondary {
352
+ background: #2d3561;
353
+ color: #e0e0e0;
354
+ }
355
  .btn-secondary:hover { background: #3d4f7c; }
356
+ .btn:disabled {
357
+ opacity: 0.4;
358
+ cursor: not-allowed;
359
+ }
360
+
361
+ /* ── API Docs ── */
362
+ .api-section {
363
+ margin-bottom: 30px;
364
+ }
365
+ .endpoint-list {
366
+ display: flex;
367
+ flex-direction: column;
368
+ gap: 8px;
369
+ }
370
+ .endpoint {
371
+ background: #1a1f2e;
372
+ border: 1px solid #2d3561;
373
+ border-radius: 8px;
374
+ padding: 12px 16px;
375
+ display: flex;
376
+ align-items: center;
377
+ gap: 12px;
378
+ font-size: 13px;
379
+ }
380
+ .method {
381
+ padding: 3px 8px;
382
+ border-radius: 4px;
383
+ font-size: 11px;
384
+ font-weight: bold;
385
+ min-width: 45px;
386
+ text-align: center;
387
+ }
388
  .get { background: #1a472a; color: #2ecc71; }
389
  .post { background: #472a1a; color: #f39c12; }
390
+ .endpoint-path {
391
+ color: #7eb8f7;
392
+ font-family: monospace;
393
+ flex: 1;
394
+ }
395
+ .endpoint-desc {
396
+ color: #8892b0;
397
+ font-size: 12px;
398
+ }
399
+ .endpoint a {
400
+ color: #7eb8f7;
401
+ text-decoration: none;
402
+ }
403
+
404
+ /* ── Status indicator ── */
405
+ .status-dot {
406
+ width: 8px;
407
+ height: 8px;
408
+ border-radius: 50%;
409
+ background: #2ecc71;
410
+ display: inline-block;
411
+ margin-right: 6px;
412
+ animation: pulse 2s infinite;
413
+ }
414
+ @keyframes pulse {
415
+ 0%, 100% { opacity: 1; }
416
+ 50% { opacity: 0.4; }
417
+ }
418
+
419
+ /* ── Loading ── */
420
+ .loading {
421
+ display: inline-block;
422
+ width: 16px;
423
+ height: 16px;
424
+ border: 2px solid #2d3561;
425
+ border-top-color: #7eb8f7;
426
+ border-radius: 50%;
427
+ animation: spin 0.8s linear infinite;
428
+ }
429
+ @keyframes spin {
430
+ to { transform: rotate(360deg); }
431
+ }
432
+
433
+ .done-banner {
434
+ background: #1a472a;
435
+ border: 1px solid #2ecc71;
436
+ border-radius: 8px;
437
+ padding: 12px 20px;
438
+ color: #2ecc71;
439
+ text-align: center;
440
+ font-weight: bold;
441
+ display: none;
442
+ margin-top: 12px;
443
+ }
444
  </style>
445
  </head>
446
  <body>
447
 
448
+ <!-- Header -->
449
  <div class="header">
450
  <div class="header-icon">🧹</div>
451
  <div>
452
+ <h1>Data Cleaning OpenEnv
453
+ <span class="badge">● LIVE</span>
454
+ </h1>
455
  <p>An OpenEnv-compliant environment where AI agents learn to clean messy real-world datasets</p>
456
  </div>
457
  </div>
458
 
459
  <div class="container">
460
 
461
+ <!-- Stats Bar -->
462
  <div class="stats-bar">
463
  <div class="stat-card">
464
+ <div class="stat-value">3</div>
465
  <div class="stat-label">Tasks Available</div>
466
  </div>
467
  <div class="stat-card">
 
469
  <div class="stat-label">Cleaning Operations</div>
470
  </div>
471
  <div class="stat-card">
472
+ <div class="stat-value" id="avg-score">0.83</div>
473
  <div class="stat-label">Baseline Avg Score</div>
474
  </div>
475
  <div class="stat-card">
 
486
  <div class="task-name">Deduplicate & Rename</div>
487
  <div class="difficulty easy">EASY</div>
488
  </div>
489
+ <div class="task-desc">
490
+ Remove duplicate rows and rename columns to snake_case in an employee dataset.
491
+ </div>
492
  <div class="task-score">
493
+ <div class="score-bar-bg">
494
+ <div class="score-bar-fill" style="width: 100%"></div>
495
+ </div>
496
  <div class="score-text">1.00</div>
497
  </div>
498
  </div>
 
501
  <div class="task-name">Missing Values & Types</div>
502
  <div class="difficulty medium">MEDIUM</div>
503
  </div>
504
+ <div class="task-desc">
505
+ Fill missing values using correct strategies and fix wrong data types in a customer dataset.
506
+ </div>
507
  <div class="task-score">
508
+ <div class="score-bar-bg">
509
+ <div class="score-bar-fill" style="width: 66.4%"></div>
510
+ </div>
511
+ <div class="score-text">0.66</div>
512
  </div>
513
  </div>
514
  <div class="task-card" onclick="selectTask('hard_full_pipeline')" id="card-hard">
 
516
  <div class="task-name">Full Pipeline</div>
517
  <div class="difficulty hard">HARD</div>
518
  </div>
519
+ <div class="task-desc">
520
+ Complete cleaning pipeline: duplicates + missing + types + outliers + schema validation.
 
 
 
 
 
 
 
 
521
  </div>
 
522
  <div class="task-score">
523
+ <div class="score-bar-bg">
524
+ <div class="score-bar-fill" style="width: 83.9%"></div>
525
+ </div>
526
+ <div class="score-text">0.84</div>
527
  </div>
528
  </div>
529
  </div>
530
 
531
+ <!-- Demo Section -->
532
  <div class="section-title">🎮 Live Demo — Try It Yourself</div>
533
  <div class="controls">
534
+ <button class="btn btn-primary" onclick="resetEnv()" id="reset-btn">
535
+ 🔄 Reset Environment
536
+ </button>
537
+ <button class="btn btn-secondary" onclick="resetAll()">
538
+ ⟳ Start Over
539
+ </button>
540
+ <span id="task-label" style="align-self:center; color:#8892b0; font-size:13px;">
541
+ Task: easy_dedup_rename
542
+ </span>
543
  </div>
544
 
545
  <div class="demo-section">
546
+
547
+ <!-- Left: Dataset + Actions -->
548
+ <div style="display:flex; flex-direction:column; gap:16px;">
549
+
550
+ <!-- Dataset Table -->
551
  <div class="panel">
552
+ <div class="panel-title">
553
+ 📊 Current Dataset
554
+ <span id="shape-badge" style="font-size:11px; color:#8892b0; font-weight:normal;">
555
+ — reset to load
556
+ </span>
557
  </div>
558
  <div class="table-wrapper" id="table-wrapper">
559
+ <div style="color:#8892b0; font-size:13px; padding:20px; text-align:center;">
560
+ Click "Reset Environment" to load dirty dataset
561
+ </div>
562
  </div>
563
  </div>
564
+
565
+ <!-- Actions -->
566
  <div class="panel">
567
  <div class="panel-title">⚡ Available Actions</div>
568
  <div class="actions-list" id="actions-list">
569
+ <div style="color:#8892b0; font-size:13px;">Reset first to enable actions</div>
570
  </div>
571
  </div>
572
+
573
  </div>
574
 
575
+ <!-- Right: Reward + Log -->
576
+ <div style="display:flex; flex-direction:column; gap:16px;">
577
+
578
+ <!-- Reward -->
579
  <div class="panel">
580
  <div class="panel-title">🏆 Reward Score</div>
581
  <div class="reward-display">
 
584
  <div class="reward-label">SCORE</div>
585
  </div>
586
  <div class="reward-components" id="reward-components">
587
+ <div style="color:#8892b0; font-size:12px; text-align:center;">
588
+ Reset to see reward breakdown
589
+ </div>
590
  </div>
591
  </div>
592
+ <div class="done-banner" id="done-banner">
593
+ ✅ Episode Complete!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
594
  </div>
595
  </div>
596
+
597
+ <!-- Log -->
598
  <div class="panel">
599
  <div class="panel-title">📝 Action Log</div>
600
  <div class="message-log" id="message-log">
601
  <div class="log-entry info">Waiting for reset...</div>
602
  </div>
603
  </div>
 
 
604
 
 
 
 
 
 
605
  </div>
606
  </div>
607
 
 
612
  <div class="endpoint">
613
  <span class="method get">GET</span>
614
  <span class="endpoint-path">/health</span>
615
+ <span class="endpoint-desc">Health check — returns status OK</span>
616
  <a href="/health" target="_blank">Try →</a>
617
  </div>
618
  <div class="endpoint">
619
  <span class="method get">GET</span>
620
  <span class="endpoint-path">/tasks</span>
621
+ <span class="endpoint-desc">List all available tasks</span>
622
  <a href="/tasks" target="_blank">Try →</a>
623
  </div>
624
  <div class="endpoint">
625
  <span class="method post">POST</span>
626
  <span class="endpoint-path">/reset/{task_id}</span>
627
+ <span class="endpoint-desc">Reset environment, get initial observation</span>
628
  </div>
629
  <div class="endpoint">
630
  <span class="method post">POST</span>
631
  <span class="endpoint-path">/step/{task_id}</span>
632
+ <span class="endpoint-desc">Take action, get reward + next observation</span>
633
  </div>
634
  <div class="endpoint">
635
  <span class="method get">GET</span>
636
  <span class="endpoint-path">/state/{task_id}</span>
637
+ <span class="endpoint-desc">Get current environment state</span>
 
 
 
 
 
 
638
  </div>
639
  <div class="endpoint">
640
  <span class="method get">GET</span>
641
  <span class="endpoint-path">/validate</span>
642
+ <span class="endpoint-desc">OpenEnv spec validation</span>
643
  <a href="/validate" target="_blank">Try →</a>
644
  </div>
645
  <div class="endpoint">
646
  <span class="method get">GET</span>
647
  <span class="endpoint-path">/docs</span>
648
+ <span class="endpoint-desc">Interactive API documentation</span>
649
  <a href="/docs" target="_blank">Try →</a>
650
  </div>
651
  </div>
 
661
  let usedActions = new Set();
662
 
663
  const TASK_OPERATIONS = {
664
+ 'easy_dedup_rename': ['remove_duplicates', 'rename_columns', 'finish'],
665
+ 'medium_missing_dtype': ['fill_missing', 'fix_dtype', 'finish'],
666
+ 'hard_full_pipeline': ['remove_duplicates', 'fill_missing', 'fix_dtype', 'remove_outliers', 'validate_schema', 'finish']
 
667
  };
668
 
669
  const ACTION_PARAMS = {
670
+ 'remove_duplicates': {},
671
+ 'rename_columns': {},
672
+ 'fill_missing': {strategy: 'mean'},
673
+ 'fix_dtype': {dtype: 'auto'},
674
+ 'remove_outliers': {method: 'iqr'},
675
+ 'validate_schema': {},
676
+ 'finish': {}
 
 
677
  };
678
 
679
  const ACTION_DESC = {
680
+ 'remove_duplicates': 'Delete repeated rows',
681
+ 'rename_columns': 'Convert to snake_case',
682
+ 'fill_missing': 'Fill empty cells (mean)',
683
+ 'fix_dtype': 'Fix wrong data types',
684
+ 'remove_outliers': 'Remove extreme values',
685
+ 'validate_schema': 'Check column names',
686
+ 'finish': 'Submit cleaned dataset'
 
 
687
  };
688
 
689
  const COMPONENT_COLORS = {
 
694
  'schema_score': '#2ecc71',
695
  };
696
 
 
 
 
 
 
 
 
697
  function selectTask(taskId) {
698
  currentTask = taskId;
699
  document.querySelectorAll('.task-card').forEach(c => c.classList.remove('active'));
700
+ const map = {
701
+ 'easy_dedup_rename': 'card-easy',
702
+ 'medium_missing_dtype': 'card-medium',
703
+ 'hard_full_pipeline': 'card-hard'
704
+ };
705
+ document.getElementById(map[taskId]).classList.add('active');
706
  document.getElementById('task-label').textContent = 'Task: ' + taskId;
707
  resetAll();
708
  addLog('Switched to task: ' + taskId, 'info');
 
714
  usedActions = new Set();
715
  document.getElementById('reward-number').textContent = '—';
716
  document.getElementById('reward-circle').style.borderColor = '#2d3561';
717
+ document.getElementById('reward-components').innerHTML =
718
+ '<div style="color:#8892b0;font-size:12px;text-align:center;">Reset to see reward breakdown</div>';
719
+ document.getElementById('table-wrapper').innerHTML =
720
+ '<div style="color:#8892b0;font-size:13px;padding:20px;text-align:center;">Click "Reset Environment" to load dirty dataset</div>';
721
+ document.getElementById('actions-list').innerHTML =
722
  '<div style="color:#8892b0;font-size:13px;">Reset first to enable actions</div>';
723
  document.getElementById('shape-badge').textContent = '— reset to load';
724
  document.getElementById('done-banner').style.display = 'none';
725
+ document.getElementById('message-log').innerHTML =
 
 
726
  '<div class="log-entry info">Ready — click Reset Environment</div>';
727
  }
728
 
 
739
  const res = await fetch(BASE + '/reset/' + currentTask, {method: 'POST'});
740
  const data = await res.json();
741
  const obs = data.observation;
742
+
743
  updateTable(obs.sample_rows, obs.columns, obs.duplicate_count, obs.missing_values);
744
  updateActions(obs.available_operations, false);
745
+ document.getElementById('shape-badge').textContent =
746
+ obs.shape[0] + ' rows × ' + obs.shape[1] + ' cols | ' +
747
+ obs.duplicate_count + ' dups | ' +
748
  Object.values(obs.missing_values).reduce((a,b)=>a+b,0) + ' missing';
749
  document.getElementById('reward-number').textContent = '0.00';
750
  document.getElementById('reward-circle').style.borderColor = '#2d3561';
751
+ addLog('Environment reset! ' + obs.message, 'success');
752
+ addLog('📋 ' + obs.task_description.substring(0,80) + '...', 'info');
753
  } catch(e) {
754
+ addLog('Reset failed: ' + e.message, 'warning');
755
  }
756
+
757
  btn.disabled = false;
758
  btn.innerHTML = '🔄 Reset Environment';
759
  }
760
 
761
  async function takeAction(operation) {
762
+ if (episodeDone) {
763
+ addLog('⚠️ Episode done — reset first', 'warning');
764
+ return;
 
 
 
 
 
765
  }
766
 
767
+ stepCount++;
768
+ const params = ACTION_PARAMS[operation] || {};
769
  addLog('[Step ' + stepCount + '] → ' + operation + ' ' + JSON.stringify(params), 'info');
770
 
771
+ // Disable button while running
772
  const btn = document.getElementById('btn-' + operation);
773
  if (btn) { btn.disabled = true; btn.innerHTML = '<span class="loading"></span> Running...'; }
774
 
 
776
  const res = await fetch(BASE + '/step/' + currentTask, {
777
  method: 'POST',
778
  headers: {'Content-Type': 'application/json'},
779
+ body: JSON.stringify({operation, parameters: params})
780
  });
781
  const data = await res.json();
782
+ const obs = data.observation;
783
+ const rew = data.reward;
784
 
785
+ // Update table
786
  updateTable(obs.sample_rows, obs.columns, obs.duplicate_count, obs.missing_values);
787
+ document.getElementById('shape-badge').textContent =
788
+ obs.shape[0] + ' rows × ' + obs.shape[1] + ' cols | ' +
789
+ obs.duplicate_count + ' dups | ' +
790
  Object.values(obs.missing_values).reduce((a,b)=>a+b,0) + ' missing';
791
+
792
+ // Update reward
793
  updateReward(rew);
 
 
794
 
795
+ // Log message
796
+ addLog(' ↳ ' + obs.message.substring(0,80), rew.total > 0.5 ? 'success' : 'info');
797
+ addLog(' ↳ Reward: ' + rew.total.toFixed(4), 'success');
798
+
799
+ // Mark action used
800
  usedActions.add(operation);
801
+ if (btn) {
802
+ btn.classList.add('used');
803
+ btn.innerHTML = '✅ ' + operation;
804
+ }
805
 
806
  if (data.done) {
807
  episodeDone = true;
808
  document.getElementById('done-banner').style.display = 'block';
809
+ addLog('🏁 Episode complete! Final score: ' + rew.total.toFixed(4), 'success');
 
810
  updateActions(obs.available_operations, true);
811
  }
812
+
813
  } catch(e) {
814
+ addLog('Action failed: ' + e.message, 'warning');
815
  if (btn) { btn.disabled = false; btn.innerHTML = operation; }
816
  }
817
  }
 
845
  btn.className = 'action-btn' + (usedActions.has(op) ? ' used' : '');
846
  btn.id = 'btn-' + op;
847
  btn.disabled = done;
848
+ btn.innerHTML = `
849
+ <span>${usedActions.has(op) ? '' : ''}${op}</span>
850
+ <span class="action-tag">${ACTION_DESC[op] || ''}</span>
851
+ `;
852
  btn.onclick = () => takeAction(op);
853
  list.appendChild(btn);
854
  });
 
856
 
857
  function updateReward(rew) {
858
  const total = rew.total || 0;
 
859
  document.getElementById('reward-number').textContent = total.toFixed(4);
860
+
861
+ // Color circle based on score
862
  let color = '#e74c3c';
863
  if (total >= 0.8) color = '#2ecc71';
864
  else if (total >= 0.5) color = '#f39c12';
865
  document.getElementById('reward-circle').style.borderColor = color;
866
 
867
+ // Components
868
  const comps = ['duplicate_score','missing_score','dtype_score','outlier_score','schema_score'];
869
  let html = '';
870
  comps.forEach(c => {
871
  const val = rew[c] || 0;
872
+ if (val === 0 && !['duplicate_score','schema_score'].includes(c)) return;
873
  const color = COMPONENT_COLORS[c] || '#7eb8f7';
874
+ html += `
875
+ <div class="component-row">
876
+ <div class="component-name">${c.replace('_score','')}</div>
877
+ <div class="component-bar-bg">
878
+ <div class="component-bar-fill"
879
+ style="width:${val*100}%;background:${color}">
880
+ </div>
881
+ </div>
882
+ <div class="component-val">${val.toFixed(2)}</div>
883
+ </div>
884
+ `;
885
  });
886
  document.getElementById('reward-components').innerHTML = html;
887
  }
 
895
  log.scrollTop = log.scrollHeight;
896
  }
897
 
898
+ // Auto ping health on load
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
899
  fetch(BASE + '/health')
900
  .then(r => r.json())
901
+ .then(d => addLog('🟢 Environment online — ' + d.tasks_loaded + ' tasks loaded', 'success'))
902
+ .catch(() => addLog('🔴 Cannot reach environment', 'warning'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
903
  </script>
904
  </body>
905
+ </html>