harsharajkumar273 commited on
Commit
93634bf
·
verified ·
1 Parent(s): 4b1e43c

Polish Space landing page with live demo UI

Browse files
Files changed (1) hide show
  1. server/app.py +438 -31
server/app.py CHANGED
@@ -29,46 +29,453 @@ def root() -> HTMLResponse:
29
  <meta name="viewport" content="width=device-width, initial-scale=1" />
30
  <title>CleanOps OpenEnv</title>
31
  <style>
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  body {
33
- font-family: Arial, sans-serif;
34
- max-width: 900px;
35
- margin: 40px auto;
36
- padding: 0 20px;
37
- line-height: 1.5;
38
- color: #111827;
 
39
  }
40
- code, pre {
41
- background: #f3f4f6;
42
- border-radius: 6px;
 
43
  }
44
- code { padding: 2px 6px; }
45
- pre {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  padding: 14px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  overflow-x: auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
- a { color: #2563eb; }
50
  </style>
51
  </head>
52
  <body>
53
- <h1>CleanOps OpenEnv is running</h1>
54
- <p>
55
- This Hugging Face Space hosts the CleanOps OpenEnv benchmark for
56
- operational data cleaning tasks.
57
- </p>
58
- <p>
59
- The competition validator uses the API endpoints below rather than
60
- this browser page.
61
- </p>
62
- <ul>
63
- <li><a href="/health">GET /health</a></li>
64
- <li><a href="/schema">GET /schema</a></li>
65
- <li><a href="/docs">GET /docs</a></li>
66
- <li><code>POST /reset</code></li>
67
- <li><code>POST /step</code></li>
68
- <li><code>GET /state</code></li>
69
- </ul>
70
- <p>Example:</p>
71
- <pre>curl -X POST /reset -H "Content-Type: application/json" -d '{"task_id":"customer_contacts_easy","seed":7}'</pre>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  </body>
73
  </html>
74
  """
 
29
  <meta name="viewport" content="width=device-width, initial-scale=1" />
30
  <title>CleanOps OpenEnv</title>
31
  <style>
32
+ :root {
33
+ color-scheme: light;
34
+ --bg: #f5f7fb;
35
+ --panel: #ffffff;
36
+ --panel-2: #eef4ff;
37
+ --text: #111827;
38
+ --muted: #5b6472;
39
+ --line: #d8e1ef;
40
+ --accent: #2563eb;
41
+ --accent-2: #0f766e;
42
+ --danger: #b91c1c;
43
+ --shadow: 0 18px 40px rgba(15, 23, 42, 0.08);
44
+ }
45
  body {
46
+ font-family: Inter, Arial, sans-serif;
47
+ margin: 0;
48
+ background:
49
+ radial-gradient(circle at top left, rgba(37, 99, 235, 0.14), transparent 28%),
50
+ radial-gradient(circle at top right, rgba(15, 118, 110, 0.14), transparent 24%),
51
+ var(--bg);
52
+ color: var(--text);
53
  }
54
+ .shell {
55
+ max-width: 1080px;
56
+ margin: 0 auto;
57
+ padding: 40px 20px 56px;
58
  }
59
+ .hero {
60
+ background: linear-gradient(135deg, rgba(37, 99, 235, 0.96), rgba(15, 118, 110, 0.94));
61
+ border-radius: 24px;
62
+ padding: 30px;
63
+ color: white;
64
+ box-shadow: var(--shadow);
65
+ display: grid;
66
+ grid-template-columns: 1.2fr 0.8fr;
67
+ gap: 24px;
68
+ align-items: stretch;
69
+ }
70
+ .badge-row {
71
+ display: flex;
72
+ gap: 10px;
73
+ flex-wrap: wrap;
74
+ margin-bottom: 16px;
75
+ }
76
+ .badge {
77
+ display: inline-flex;
78
+ align-items: center;
79
+ gap: 8px;
80
+ font-size: 13px;
81
+ padding: 7px 12px;
82
+ border-radius: 999px;
83
+ background: rgba(255, 255, 255, 0.16);
84
+ border: 1px solid rgba(255, 255, 255, 0.18);
85
+ }
86
+ h1 {
87
+ margin: 0 0 12px;
88
+ font-size: 36px;
89
+ line-height: 1.1;
90
+ }
91
+ .hero p {
92
+ margin: 0;
93
+ font-size: 16px;
94
+ color: rgba(255, 255, 255, 0.92);
95
+ max-width: 60ch;
96
+ }
97
+ .hero-card {
98
+ background: rgba(255, 255, 255, 0.12);
99
+ border: 1px solid rgba(255, 255, 255, 0.18);
100
+ border-radius: 18px;
101
+ padding: 18px;
102
+ display: flex;
103
+ flex-direction: column;
104
+ gap: 14px;
105
+ }
106
+ .hero-card h2 {
107
+ margin: 0;
108
+ font-size: 16px;
109
+ }
110
+ .hero-card p {
111
+ font-size: 14px;
112
+ }
113
+ .pill-grid {
114
+ display: grid;
115
+ grid-template-columns: repeat(2, minmax(0, 1fr));
116
+ gap: 10px;
117
+ }
118
+ .pill {
119
+ background: rgba(255, 255, 255, 0.13);
120
+ border: 1px solid rgba(255, 255, 255, 0.18);
121
+ border-radius: 14px;
122
+ padding: 12px;
123
+ }
124
+ .pill strong {
125
+ display: block;
126
+ font-size: 14px;
127
+ margin-bottom: 4px;
128
+ }
129
+ .grid {
130
+ display: grid;
131
+ grid-template-columns: 1.1fr 0.9fr;
132
+ gap: 22px;
133
+ margin-top: 22px;
134
+ }
135
+ .panel {
136
+ background: var(--panel);
137
+ border: 1px solid var(--line);
138
+ border-radius: 20px;
139
+ box-shadow: var(--shadow);
140
+ padding: 22px;
141
+ }
142
+ .panel h2 {
143
+ margin: 0 0 10px;
144
+ font-size: 20px;
145
+ }
146
+ .panel p {
147
+ margin: 0 0 14px;
148
+ color: var(--muted);
149
+ }
150
+ .button-row {
151
+ display: flex;
152
+ gap: 10px;
153
+ flex-wrap: wrap;
154
+ margin: 18px 0 16px;
155
+ }
156
+ button,
157
+ .link-btn {
158
+ appearance: none;
159
+ border: none;
160
+ border-radius: 12px;
161
+ padding: 11px 15px;
162
+ font-size: 14px;
163
+ font-weight: 600;
164
+ cursor: pointer;
165
+ text-decoration: none;
166
+ transition: transform 0.15s ease, opacity 0.15s ease, box-shadow 0.15s ease;
167
+ display: inline-flex;
168
+ align-items: center;
169
+ justify-content: center;
170
+ }
171
+ button:hover,
172
+ .link-btn:hover {
173
+ transform: translateY(-1px);
174
+ }
175
+ .primary {
176
+ background: var(--accent);
177
+ color: white;
178
+ box-shadow: 0 8px 18px rgba(37, 99, 235, 0.22);
179
+ }
180
+ .secondary {
181
+ background: var(--panel-2);
182
+ color: var(--accent);
183
+ border: 1px solid #cfe0ff;
184
+ }
185
+ .status {
186
+ display: inline-flex;
187
+ align-items: center;
188
+ gap: 8px;
189
+ background: #ecfdf5;
190
+ color: #065f46;
191
+ border: 1px solid #bbf7d0;
192
+ padding: 10px 12px;
193
+ border-radius: 12px;
194
+ font-size: 14px;
195
+ font-weight: 600;
196
+ }
197
+ .status.error {
198
+ background: #fef2f2;
199
+ color: var(--danger);
200
+ border-color: #fecaca;
201
+ }
202
+ .kpis {
203
+ display: grid;
204
+ grid-template-columns: repeat(3, minmax(0, 1fr));
205
+ gap: 12px;
206
+ margin-top: 16px;
207
+ }
208
+ .kpi {
209
+ background: #f8fafc;
210
+ border: 1px solid var(--line);
211
+ border-radius: 14px;
212
  padding: 14px;
213
+ }
214
+ .kpi span {
215
+ display: block;
216
+ font-size: 12px;
217
+ color: var(--muted);
218
+ margin-bottom: 6px;
219
+ }
220
+ .kpi strong {
221
+ font-size: 22px;
222
+ }
223
+ pre {
224
+ margin: 0;
225
+ background: #0f172a;
226
+ color: #e2e8f0;
227
+ border-radius: 16px;
228
+ padding: 16px;
229
  overflow-x: auto;
230
+ font-size: 13px;
231
+ line-height: 1.5;
232
+ }
233
+ code {
234
+ background: #eef2ff;
235
+ color: #3730a3;
236
+ border-radius: 8px;
237
+ padding: 2px 7px;
238
+ }
239
+ .endpoint-list {
240
+ display: grid;
241
+ gap: 10px;
242
+ }
243
+ .endpoint {
244
+ display: flex;
245
+ justify-content: space-between;
246
+ gap: 10px;
247
+ align-items: center;
248
+ background: #f8fafc;
249
+ border: 1px solid var(--line);
250
+ border-radius: 14px;
251
+ padding: 12px 14px;
252
+ }
253
+ .endpoint small {
254
+ color: var(--muted);
255
+ }
256
+ a {
257
+ color: var(--accent);
258
+ }
259
+ .footer {
260
+ margin-top: 20px;
261
+ color: var(--muted);
262
+ font-size: 13px;
263
+ }
264
+ @media (max-width: 860px) {
265
+ .hero,
266
+ .grid {
267
+ grid-template-columns: 1fr;
268
+ }
269
+ .kpis,
270
+ .pill-grid {
271
+ grid-template-columns: 1fr;
272
+ }
273
+ h1 {
274
+ font-size: 30px;
275
+ }
276
  }
 
277
  </style>
278
  </head>
279
  <body>
280
+ <div class="shell">
281
+ <section class="hero">
282
+ <div>
283
+ <div class="badge-row">
284
+ <div class="badge">OpenEnv Benchmark</div>
285
+ <div class="badge">Real-world Data Cleaning</div>
286
+ <div class="badge">3 Graded Tasks</div>
287
+ </div>
288
+ <h1>CleanOps OpenEnv</h1>
289
+ <p>
290
+ A hosted benchmark for operational data cleaning tasks across
291
+ CRM, orders, subscriptions, and payments. This Space exposes
292
+ the OpenEnv API and a live demo so reviewers can see that the
293
+ environment is actually working.
294
+ </p>
295
+ </div>
296
+ <div class="hero-card">
297
+ <h2>What this Space demonstrates</h2>
298
+ <div class="pill-grid">
299
+ <div class="pill">
300
+ <strong>Easy</strong>
301
+ Customer contact cleanup
302
+ </div>
303
+ <div class="pill">
304
+ <strong>Medium</strong>
305
+ Order reconciliation
306
+ </div>
307
+ <div class="pill">
308
+ <strong>Hard</strong>
309
+ CRM migration repair
310
+ </div>
311
+ <div class="pill">
312
+ <strong>API-ready</strong>
313
+ <code>/reset</code>, <code>/step</code>, <code>/state</code>
314
+ </div>
315
+ </div>
316
+ </div>
317
+ </section>
318
+
319
+ <section class="grid">
320
+ <div class="panel">
321
+ <h2>Live Demo</h2>
322
+ <p>
323
+ Run one of the benchmark tasks below. This sends a real
324
+ <code>POST /reset</code> request and renders a summary from the
325
+ returned observation.
326
+ </p>
327
+
328
+ <div id="health" class="status">Checking API health...</div>
329
+
330
+ <div class="button-row">
331
+ <button class="primary" data-task="customer_contacts_easy">Run Easy Task</button>
332
+ <button class="primary" data-task="orders_reconciliation_medium">Run Medium Task</button>
333
+ <button class="primary" data-task="crm_migration_hard">Run Hard Task</button>
334
+ <a class="link-btn secondary" href="/docs">Open API Docs</a>
335
+ </div>
336
+
337
+ <div class="kpis">
338
+ <div class="kpi">
339
+ <span>Task</span>
340
+ <strong id="taskId">-</strong>
341
+ </div>
342
+ <div class="kpi">
343
+ <span>Initial Score</span>
344
+ <strong id="score">-</strong>
345
+ </div>
346
+ <div class="kpi">
347
+ <span>Validation Issues</span>
348
+ <strong id="issues">-</strong>
349
+ </div>
350
+ </div>
351
+
352
+ <div style="margin-top: 18px;">
353
+ <pre id="output">Click one of the task buttons to fetch a live observation from /reset.</pre>
354
+ </div>
355
+ </div>
356
+
357
+ <div class="panel">
358
+ <h2>Endpoints</h2>
359
+ <p>
360
+ The competition validator checks the API, not just the page UI.
361
+ </p>
362
+ <div class="endpoint-list">
363
+ <div class="endpoint">
364
+ <div>
365
+ <strong>GET /health</strong><br />
366
+ <small>Service liveness check</small>
367
+ </div>
368
+ <a href="/health">Open</a>
369
+ </div>
370
+ <div class="endpoint">
371
+ <div>
372
+ <strong>GET /schema</strong><br />
373
+ <small>Typed OpenEnv schema</small>
374
+ </div>
375
+ <a href="/schema">Open</a>
376
+ </div>
377
+ <div class="endpoint">
378
+ <div>
379
+ <strong>GET /docs</strong><br />
380
+ <small>Interactive FastAPI docs</small>
381
+ </div>
382
+ <a href="/docs">Open</a>
383
+ </div>
384
+ <div class="endpoint">
385
+ <div>
386
+ <strong>POST /reset</strong><br />
387
+ <small>Start a task episode</small>
388
+ </div>
389
+ <code>API</code>
390
+ </div>
391
+ <div class="endpoint">
392
+ <div>
393
+ <strong>POST /step</strong><br />
394
+ <small>Apply a typed action</small>
395
+ </div>
396
+ <code>API</code>
397
+ </div>
398
+ <div class="endpoint">
399
+ <div>
400
+ <strong>GET /state</strong><br />
401
+ <small>Inspect current environment state</small>
402
+ </div>
403
+ <code>API</code>
404
+ </div>
405
+ </div>
406
+
407
+ <div style="margin-top: 18px;">
408
+ <pre>curl -X POST /reset -H "Content-Type: application/json" -d '{"task_id":"customer_contacts_easy","seed":7}'</pre>
409
+ </div>
410
+
411
+ <div class="footer">
412
+ This Space hosts a deterministic OpenEnv benchmark with fixed
413
+ tasks and graders for reproducible evaluation.
414
+ </div>
415
+ </div>
416
+ </section>
417
+ </div>
418
+
419
+ <script>
420
+ const healthEl = document.getElementById("health");
421
+ const outputEl = document.getElementById("output");
422
+ const taskEl = document.getElementById("taskId");
423
+ const scoreEl = document.getElementById("score");
424
+ const issuesEl = document.getElementById("issues");
425
+
426
+ async function loadHealth() {
427
+ try {
428
+ const response = await fetch("/health");
429
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
430
+ const data = await response.json();
431
+ healthEl.textContent = `API healthy: ${data.status}`;
432
+ healthEl.className = "status";
433
+ } catch (error) {
434
+ healthEl.textContent = `API check failed: ${error.message}`;
435
+ healthEl.className = "status error";
436
+ }
437
+ }
438
+
439
+ async function runTask(taskId) {
440
+ outputEl.textContent = "Loading...";
441
+ try {
442
+ const response = await fetch("/reset", {
443
+ method: "POST",
444
+ headers: { "Content-Type": "application/json" },
445
+ body: JSON.stringify({ task_id: taskId, seed: 7 }),
446
+ });
447
+ if (!response.ok) {
448
+ throw new Error(`HTTP ${response.status}`);
449
+ }
450
+ const payload = await response.json();
451
+ const observation = payload.observation || {};
452
+ taskEl.textContent = observation.task_id || taskId;
453
+ scoreEl.textContent = String(observation.quality_score ?? "-");
454
+ issuesEl.textContent = String((observation.validation_issues || []).length);
455
+ outputEl.textContent = JSON.stringify(
456
+ {
457
+ task_id: observation.task_id,
458
+ difficulty: observation.difficulty,
459
+ objective: observation.objective,
460
+ quality_score: observation.quality_score,
461
+ remaining_steps: observation.remaining_steps,
462
+ validation_issue_count: (observation.validation_issues || []).length,
463
+ available_operations: (observation.available_operations || []).map((item) => item.operation_id),
464
+ },
465
+ null,
466
+ 2
467
+ );
468
+ } catch (error) {
469
+ outputEl.textContent = `Request failed: ${error.message}`;
470
+ }
471
+ }
472
+
473
+ document.querySelectorAll("button[data-task]").forEach((button) => {
474
+ button.addEventListener("click", () => runTask(button.dataset.task));
475
+ });
476
+
477
+ loadHealth();
478
+ </script>
479
  </body>
480
  </html>
481
  """