parthpethia commited on
Commit
6031a66
·
1 Parent(s): a1dc936

Add interactive web dashboard for testing and evaluation

Browse files
Files changed (2) hide show
  1. app.py +3 -15
  2. templates/index.html +833 -0
app.py CHANGED
@@ -2,7 +2,7 @@
2
 
3
  import os
4
 
5
- from flask import Flask, request, jsonify
6
 
7
  from environment.env import EmailTriageEnv
8
  from environment.types import Action
@@ -20,20 +20,8 @@ def get_env(task_name: str = "spam_detection") -> EmailTriageEnv:
20
 
21
  @app.route("/", methods=["GET"])
22
  def index():
23
- """Root endpoint - Space status page"""
24
- return jsonify({
25
- "status": "ok",
26
- "message": "Email Triage OpenEnv API",
27
- "version": "1.0.0",
28
- "endpoints": {
29
- "health": "GET /health",
30
- "tasks": "GET /tasks",
31
- "reset": "POST /reset?task=spam_detection",
32
- "step": "POST /step?task=spam_detection",
33
- "state": "GET /state?task=spam_detection",
34
- "state-describe": "GET /state-describe?task=spam_detection"
35
- }
36
- }), 200
37
 
38
  @app.route("/health", methods=["GET"])
39
  def health():
 
2
 
3
  import os
4
 
5
+ from flask import Flask, request, jsonify, render_template
6
 
7
  from environment.env import EmailTriageEnv
8
  from environment.types import Action
 
20
 
21
  @app.route("/", methods=["GET"])
22
  def index():
23
+ """Root endpoint - Interactive dashboard"""
24
+ return render_template("index.html")
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  @app.route("/health", methods=["GET"])
27
  def health():
templates/index.html ADDED
@@ -0,0 +1,833 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Email Triage OpenEnv - Interactive Dashboard</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
+ min-height: 100vh;
19
+ padding: 20px;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1400px;
24
+ margin: 0 auto;
25
+ }
26
+
27
+ header {
28
+ text-align: center;
29
+ color: white;
30
+ margin-bottom: 30px;
31
+ }
32
+
33
+ header h1 {
34
+ font-size: 2.5em;
35
+ margin-bottom: 10px;
36
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
37
+ }
38
+
39
+ header p {
40
+ font-size: 1.1em;
41
+ opacity: 0.9;
42
+ }
43
+
44
+ .main-layout {
45
+ display: grid;
46
+ grid-template-columns: 1fr 2fr 1fr;
47
+ gap: 20px;
48
+ margin-bottom: 20px;
49
+ }
50
+
51
+ .panel {
52
+ background: white;
53
+ border-radius: 12px;
54
+ padding: 20px;
55
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
56
+ display: flex;
57
+ flex-direction: column;
58
+ }
59
+
60
+ .panel h2 {
61
+ color: #333;
62
+ font-size: 1.3em;
63
+ margin-bottom: 15px;
64
+ padding-bottom: 10px;
65
+ border-bottom: 2px solid #667eea;
66
+ }
67
+
68
+ .panel h3 {
69
+ color: #555;
70
+ font-size: 1em;
71
+ margin-top: 15px;
72
+ margin-bottom: 10px;
73
+ }
74
+
75
+ .task-selector {
76
+ display: flex;
77
+ flex-direction: column;
78
+ gap: 10px;
79
+ }
80
+
81
+ .task-btn {
82
+ padding: 12px;
83
+ border: 2px solid #ddd;
84
+ border-radius: 8px;
85
+ background: white;
86
+ cursor: pointer;
87
+ font-size: 0.95em;
88
+ transition: all 0.3s;
89
+ text-align: left;
90
+ }
91
+
92
+ .task-btn:hover {
93
+ border-color: #667eea;
94
+ background: #f0f4ff;
95
+ }
96
+
97
+ .task-btn.active {
98
+ background: #667eea;
99
+ color: white;
100
+ border-color: #667eea;
101
+ }
102
+
103
+ .task-btn-title {
104
+ font-weight: bold;
105
+ display: block;
106
+ margin-bottom: 5px;
107
+ }
108
+
109
+ .task-btn-desc {
110
+ font-size: 0.85em;
111
+ opacity: 0.8;
112
+ }
113
+
114
+ .email-display {
115
+ background: #f9f9f9;
116
+ border-left: 4px solid #667eea;
117
+ padding: 15px;
118
+ border-radius: 8px;
119
+ margin-bottom: 15px;
120
+ min-height: 200px;
121
+ }
122
+
123
+ .email-header {
124
+ display: grid;
125
+ grid-template-columns: 1fr 1fr;
126
+ gap: 15px;
127
+ margin-bottom: 15px;
128
+ font-size: 0.9em;
129
+ }
130
+
131
+ .email-field {
132
+ display: flex;
133
+ flex-direction: column;
134
+ }
135
+
136
+ .email-label {
137
+ color: #666;
138
+ font-weight: bold;
139
+ font-size: 0.85em;
140
+ margin-bottom: 5px;
141
+ }
142
+
143
+ .email-value {
144
+ color: #333;
145
+ padding: 8px;
146
+ background: white;
147
+ border-radius: 4px;
148
+ }
149
+
150
+ .email-subject {
151
+ font-weight: bold;
152
+ margin-bottom: 10px;
153
+ color: #333;
154
+ }
155
+
156
+ .email-body {
157
+ background: white;
158
+ padding: 12px;
159
+ border-radius: 4px;
160
+ line-height: 1.5;
161
+ color: #555;
162
+ max-height: 300px;
163
+ overflow-y: auto;
164
+ }
165
+
166
+ .form-group {
167
+ margin-bottom: 15px;
168
+ }
169
+
170
+ .form-group label {
171
+ display: block;
172
+ margin-bottom: 8px;
173
+ color: #333;
174
+ font-weight: bold;
175
+ font-size: 0.95em;
176
+ }
177
+
178
+ select,
179
+ input {
180
+ width: 100%;
181
+ padding: 10px;
182
+ border: 1px solid #ddd;
183
+ border-radius: 6px;
184
+ font-size: 0.95em;
185
+ font-family: inherit;
186
+ }
187
+
188
+ select:focus,
189
+ input:focus {
190
+ outline: none;
191
+ border-color: #667eea;
192
+ box-shadow: 0 0 5px rgba(102, 126, 234, 0.3);
193
+ }
194
+
195
+ .button-group {
196
+ display: grid;
197
+ grid-template-columns: 1fr 1fr;
198
+ gap: 10px;
199
+ margin-top: 15px;
200
+ }
201
+
202
+ button {
203
+ padding: 12px;
204
+ border: none;
205
+ border-radius: 6px;
206
+ font-size: 0.95em;
207
+ font-weight: bold;
208
+ cursor: pointer;
209
+ transition: all 0.3s;
210
+ }
211
+
212
+ .btn-primary {
213
+ background: #667eea;
214
+ color: white;
215
+ }
216
+
217
+ .btn-primary:hover:not(:disabled) {
218
+ background: #5568d3;
219
+ transform: translateY(-2px);
220
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
221
+ }
222
+
223
+ .btn-primary:disabled {
224
+ background: #ccc;
225
+ cursor: not-allowed;
226
+ }
227
+
228
+ .btn-secondary {
229
+ background: #f0f0f0;
230
+ color: #333;
231
+ border: 1px solid #ddd;
232
+ }
233
+
234
+ .btn-secondary:hover:not(:disabled) {
235
+ background: #e0e0e0;
236
+ }
237
+
238
+ .btn-secondary:disabled {
239
+ opacity: 0.5;
240
+ cursor: not-allowed;
241
+ }
242
+
243
+ .stats-panel {
244
+ display: grid;
245
+ grid-template-columns: 1fr;
246
+ gap: 10px;
247
+ }
248
+
249
+ .stat-box {
250
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
251
+ color: white;
252
+ padding: 15px;
253
+ border-radius: 8px;
254
+ text-align: center;
255
+ }
256
+
257
+ .stat-label {
258
+ font-size: 0.9em;
259
+ opacity: 0.9;
260
+ margin-bottom: 5px;
261
+ }
262
+
263
+ .stat-value {
264
+ font-size: 1.8em;
265
+ font-weight: bold;
266
+ }
267
+
268
+ .results-area {
269
+ background: #f0f8ff;
270
+ border: 2px solid #667eea;
271
+ padding: 15px;
272
+ border-radius: 8px;
273
+ margin-top: 15px;
274
+ min-height: 100px;
275
+ }
276
+
277
+ .result-item {
278
+ padding: 10px;
279
+ margin-bottom: 8px;
280
+ background: white;
281
+ border-left: 4px solid #667eea;
282
+ border-radius: 4px;
283
+ }
284
+
285
+ .result-label {
286
+ font-weight: bold;
287
+ color: #667eea;
288
+ font-size: 0.9em;
289
+ }
290
+
291
+ .result-value {
292
+ color: #333;
293
+ margin-top: 5px;
294
+ }
295
+
296
+ .reward-high {
297
+ color: #28a745;
298
+ font-weight: bold;
299
+ }
300
+
301
+ .reward-low {
302
+ color: #dc3545;
303
+ font-weight: bold;
304
+ }
305
+
306
+ .progress-bar {
307
+ width: 100%;
308
+ height: 8px;
309
+ background: #e0e0e0;
310
+ border-radius: 4px;
311
+ overflow: hidden;
312
+ margin-top: 10px;
313
+ }
314
+
315
+ .progress-fill {
316
+ height: 100%;
317
+ background: linear-gradient(90deg, #667eea, #764ba2);
318
+ transition: width 0.3s;
319
+ }
320
+
321
+ .status-message {
322
+ padding: 12px;
323
+ border-radius: 6px;
324
+ margin-bottom: 15px;
325
+ font-size: 0.95em;
326
+ }
327
+
328
+ .status-info {
329
+ background: #e7f3ff;
330
+ color: #00396b;
331
+ border: 1px solid #667eea;
332
+ }
333
+
334
+ .status-success {
335
+ background: #d4edda;
336
+ color: #155724;
337
+ border: 1px solid #28a745;
338
+ }
339
+
340
+ .status-error {
341
+ background: #f8d7da;
342
+ color: #721c24;
343
+ border: 1px solid #dc3545;
344
+ }
345
+
346
+ .status-warning {
347
+ background: #fff3cd;
348
+ color: #856404;
349
+ border: 1px solid #ffc107;
350
+ }
351
+
352
+ .history-table {
353
+ width: 100%;
354
+ border-collapse: collapse;
355
+ margin-top: 10px;
356
+ font-size: 0.85em;
357
+ }
358
+
359
+ .history-table th {
360
+ background: #667eea;
361
+ color: white;
362
+ padding: 8px;
363
+ text-align: left;
364
+ }
365
+
366
+ .history-table td {
367
+ padding: 8px;
368
+ border-bottom: 1px solid #ddd;
369
+ }
370
+
371
+ .history-table tr:hover {
372
+ background: #f5f5f5;
373
+ }
374
+
375
+ .task-complete {
376
+ background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
377
+ color: white;
378
+ padding: 20px;
379
+ border-radius: 8px;
380
+ text-align: center;
381
+ }
382
+
383
+ .task-complete h3 {
384
+ font-size: 1.5em;
385
+ margin-bottom: 10px;
386
+ color: white;
387
+ }
388
+
389
+ .idle-message {
390
+ text-align: center;
391
+ padding: 30px;
392
+ color: #999;
393
+ }
394
+
395
+ @media (max-width: 1200px) {
396
+ .main-layout {
397
+ grid-template-columns: 1fr;
398
+ }
399
+
400
+ header h1 {
401
+ font-size: 1.8em;
402
+ }
403
+ }
404
+
405
+ .loader {
406
+ display: inline-block;
407
+ width: 20px;
408
+ height: 20px;
409
+ border: 3px solid #f3f3f3;
410
+ border-top: 3px solid #667eea;
411
+ border-radius: 50%;
412
+ animation: spin 1s linear infinite;
413
+ margin-right: 10px;
414
+ }
415
+
416
+ @keyframes spin {
417
+ 0% {
418
+ transform: rotate(0deg);
419
+ }
420
+
421
+ 100% {
422
+ transform: rotate(360deg);
423
+ }
424
+ }
425
+ </style>
426
+ </head>
427
+
428
+ <body>
429
+ <div class="container">
430
+ <header>
431
+ <h1>📧 Email Triage OpenEnv</h1>
432
+ <p>Interactive Dashboard - Test & Evaluate Email Classification Tasks</p>
433
+ </header>
434
+
435
+ <div id="statusMessage"></div>
436
+
437
+ <div class="main-layout">
438
+ <!-- Left Panel: Task Selection & Controls -->
439
+ <div class="panel">
440
+ <h2>📋 Tasks</h2>
441
+
442
+ <div class="task-selector">
443
+ <button class="task-btn active" onclick="selectTask('spam_detection')">
444
+ <span class="task-btn-title">Task 1: Spam Detection</span>
445
+ <span class="task-btn-desc">Easy - 10 emails</span>
446
+ </button>
447
+ <button class="task-btn" onclick="selectTask('multi_class_routing')">
448
+ <span class="task-btn-title">Task 2: Multi-Class Routing</span>
449
+ <span class="task-btn-desc">Medium - 12 emails</span>
450
+ </button>
451
+ <button class="task-btn" onclick="selectTask('context_aware_triage')">
452
+ <span class="task-btn-title">Task 3: Context-Aware Triage</span>
453
+ <span class="task-btn-desc">Hard - 20 emails</span>
454
+ </button>
455
+ </div>
456
+
457
+ <h3 style="margin-top: 25px;">⚙️ Controls</h3>
458
+ <button class="btn-primary" style="width: 100%; margin-bottom: 10px;" onclick="resetTask()">
459
+ 🔄 Reset Task
460
+ </button>
461
+ <button class="btn-secondary" style="width: 100%;" onclick="loadTaskInfo()">
462
+ ℹ️ Task Info
463
+ </button>
464
+
465
+ <h3 style="margin-top: 25px;">📊 Statistics</h3>
466
+ <div class="stats-panel">
467
+ <div class="stat-box">
468
+ <div class="stat-label">Current Step</div>
469
+ <div class="stat-value" id="statStep">0</div>
470
+ </div>
471
+ <div class="stat-box">
472
+ <div class="stat-label">Total Reward</div>
473
+ <div class="stat-value" id="statReward">0.00</div>
474
+ </div>
475
+ <div class="stat-box">
476
+ <div class="stat-label">Average Reward</div>
477
+ <div class="stat-value" id="statAvg">0.00</div>
478
+ </div>
479
+ <div class="stat-box">
480
+ <div class="stat-label">Final Score</div>
481
+ <div class="stat-value" id="statScore">-</div>
482
+ </div>
483
+ </div>
484
+ </div>
485
+
486
+ <!-- Middle Panel: Email Display & Classification Form -->
487
+ <div class="panel">
488
+ <h2>✉️ Email Classification</h2>
489
+
490
+ <div id="emailContainer">
491
+ <div class="idle-message">
492
+ <p>Click "Reset Task" to start</p>
493
+ </div>
494
+ </div>
495
+
496
+ <div id="formContainer" style="display: none;">
497
+ <div class="form-group">
498
+ <label for="classification">📌 Classification:</label>
499
+ <select id="classification" onchange="updatePriorities()">
500
+ <option value="">-- Select Classification --</option>
501
+ <option value="spam">🚫 Spam</option>
502
+ <option value="normal">📄 Normal</option>
503
+ <option value="urgent">⚡ Urgent</option>
504
+ <option value="billing">💳 Billing</option>
505
+ </select>
506
+ </div>
507
+
508
+ <div class="form-group">
509
+ <label for="team">🏢 Route to Team:</label>
510
+ <select id="team">
511
+ <option value="none">🚫 None</option>
512
+ <option value="support">🆘 Support</option>
513
+ <option value="sales">💼 Sales</option>
514
+ <option value="billing">💰 Billing</option>
515
+ </select>
516
+ </div>
517
+
518
+ <div class="form-group">
519
+ <label for="priority">⭐ Priority (0-3):</label>
520
+ <select id="priority">
521
+ <option value="0">0 - Low</option>
522
+ <option value="1" selected>1 - Medium</option>
523
+ <option value="2">2 - High</option>
524
+ <option value="3">3 - Critical</option>
525
+ </select>
526
+ </div>
527
+
528
+ <div class="button-group">
529
+ <button class="btn-primary" onclick="submitAction()">✓ Submit</button>
530
+ <button class="btn-secondary" onclick="resetTask()">⟲ Reset</button>
531
+ </div>
532
+
533
+ <div id="resultArea" class="results-area" style="display: none;">
534
+ <div id="resultContent"></div>
535
+ <div class="progress-bar">
536
+ <div class="progress-fill" id="progressFill"></div>
537
+ </div>
538
+ </div>
539
+ </div>
540
+ </div>
541
+
542
+ <!-- Right Panel: History & Results -->
543
+ <div class="panel">
544
+ <h2>📝 History & Results</h2>
545
+
546
+ <div id="historyContainer">
547
+ <div class="idle-message">
548
+ <p>History will appear here</p>
549
+ </div>
550
+ </div>
551
+
552
+ <div id="completeMessage" style="display: none;">
553
+ <div class="task-complete">
554
+ <h3>✓ Task Complete!</h3>
555
+ <p>Final Score: <span id="finalScore">0.00</span></p>
556
+ <p>Steps Taken: <span id="finalSteps">0</span></p>
557
+ <p style="margin-top: 15px; font-size: 0.9em;">Click "Reset Task" to try again or select another
558
+ task</p>
559
+ </div>
560
+ </div>
561
+ </div>
562
+ </div>
563
+ </div>
564
+
565
+ <script>
566
+ let currentTask = 'spam_detection';
567
+ let currentState = null;
568
+ let history = [];
569
+ let totalReward = 0;
570
+ let taskDone = false;
571
+
572
+ const taskDescriptions = {
573
+ 'spam_detection': {
574
+ title: 'Spam Detection (Easy)',
575
+ desc: 'Classify 10 emails as spam or legitimate. High accuracy expected.',
576
+ steps: 10,
577
+ categories: ['spam', 'normal']
578
+ },
579
+ 'multi_class_routing': {
580
+ title: 'Multi-Class Routing (Medium)',
581
+ desc: 'Classify 12 emails into 4 categories and route to appropriate teams.',
582
+ steps: 12,
583
+ categories: ['spam', 'normal', 'urgent', 'billing']
584
+ },
585
+ 'context_aware_triage': {
586
+ title: 'Context-Aware Triage (Hard)',
587
+ desc: 'Handle 20 emails with VIP flags, SLAs, and complex context.',
588
+ steps: 20,
589
+ categories: ['spam', 'normal', 'urgent', 'billing']
590
+ }
591
+ };
592
+
593
+ function showStatus(message, type = 'info') {
594
+ const statusEl = document.getElementById('statusMessage');
595
+ statusEl.className = `status-message status-${type}`;
596
+ statusEl.innerHTML = message;
597
+ statusEl.style.display = 'block';
598
+ setTimeout(() => {
599
+ statusEl.style.display = 'none';
600
+ }, 5000);
601
+ }
602
+
603
+ async function selectTask(taskName) {
604
+ currentTask = taskName;
605
+ document.querySelectorAll('.task-btn').forEach(btn => btn.classList.remove('active'));
606
+ event.target.closest('.task-btn').classList.add('active');
607
+ loadTaskInfo();
608
+ showStatus(`Selected: ${taskDescriptions[taskName].title}`, 'info');
609
+ }
610
+
611
+ function loadTaskInfo() {
612
+ const task = taskDescriptions[currentTask];
613
+ showStatus(`<strong>${task.title}</strong><br>${task.desc}`, 'info');
614
+ }
615
+
616
+ async function resetTask() {
617
+ try {
618
+ showStatus('🔄 Resetting task...', 'info');
619
+ const response = await fetch(`/reset?task=${currentTask}`, {
620
+ method: 'POST'
621
+ });
622
+ const data = await response.json();
623
+
624
+ currentState = data.observation;
625
+ history = [];
626
+ totalReward = 0;
627
+ taskDone = false;
628
+
629
+ document.getElementById('statStep').textContent = '0';
630
+ document.getElementById('statReward').textContent = '0.00';
631
+ document.getElementById('statAvg').textContent = '0.00';
632
+ document.getElementById('statScore').textContent = '-';
633
+ document.getElementById('completeMessage').style.display = 'none';
634
+ document.getElementById('formContainer').style.display = 'block';
635
+
636
+ displayEmail();
637
+ updateHistory();
638
+ showStatus(`✓ Task reset! Ready to classify emails.`, 'success');
639
+ } catch (error) {
640
+ showStatus(`Error resetting task: ${error.message}`, 'error');
641
+ }
642
+ }
643
+
644
+ function displayEmail() {
645
+ const email = currentState.current_email;
646
+ const emailHTML = `
647
+ <div class="email-display">
648
+ <div class="email-subject">Subject: ${email.subject}</div>
649
+ <div class="email-header">
650
+ <div class="email-field">
651
+ <span class="email-label">From:</span>
652
+ <span class="email-value">${email.sender_domain}</span>
653
+ </div>
654
+ <div class="email-field">
655
+ <span class="email-label">VIP Sender:</span>
656
+ <span class="email-value">${email.is_vip_sender ? '✓ Yes' : '✗ No'}</span>
657
+ </div>
658
+ </div>
659
+ <div class="email-header" style="margin-bottom: 15px;">
660
+ <div class="email-field">
661
+ <span class="email-label">SLA Hours:</span>
662
+ <span class="email-value">${email.sla_hours || 'N/A'}</span>
663
+ </div>
664
+ <div class="email-field">
665
+ <span class="email-label">Timestamp:</span>
666
+ <span class="email-value">${new Date(email.timestamp).toLocaleString()}</span>
667
+ </div>
668
+ </div>
669
+ <div class="email-body">${email.body.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>
670
+ </div>
671
+ `;
672
+ document.getElementById('emailContainer').innerHTML = emailHTML;
673
+ }
674
+
675
+ function updatePriorities() {
676
+ const classification = document.getElementById('classification').value;
677
+ updateTeamOptions();
678
+ }
679
+
680
+ function updateTeamOptions() {
681
+ const classification = document.getElementById('classification').value;
682
+ const teamSelect = document.getElementById('team');
683
+
684
+ if (classification === 'spam') {
685
+ teamSelect.value = 'none';
686
+ }
687
+ }
688
+
689
+ async function submitAction() {
690
+ const classification = document.getElementById('classification').value;
691
+ const team = document.getElementById('team').value;
692
+ const priority = parseInt(document.getElementById('priority').value);
693
+
694
+ if (!classification) {
695
+ showStatus('Please select a classification', 'warning');
696
+ return;
697
+ }
698
+
699
+ try {
700
+ const response = await fetch(`/step?task=${currentTask}`, {
701
+ method: 'POST',
702
+ headers: {
703
+ 'Content-Type': 'application/json'
704
+ },
705
+ body: JSON.stringify({
706
+ classification: classification,
707
+ team: team,
708
+ priority: priority
709
+ })
710
+ });
711
+
712
+ const data = await response.json();
713
+ currentState = data.observation;
714
+ totalReward += data.reward.value;
715
+ taskDone = data.done;
716
+
717
+ const stepNum = history.length + 1;
718
+ history.push({
719
+ step: stepNum,
720
+ email_subject: currentState.current_email.subject.substring(0, 30),
721
+ classification: classification,
722
+ team: team,
723
+ priority: priority,
724
+ reward: data.reward.value,
725
+ done: taskDone
726
+ });
727
+
728
+ updateStats();
729
+ displayResults(data);
730
+ updateHistory();
731
+
732
+ if (taskDone) {
733
+ showTaskComplete();
734
+ } else {
735
+ setTimeout(() => {
736
+ displayEmail();
737
+ document.getElementById('classification').value = '';
738
+ document.getElementById('team').value = 'none';
739
+ document.getElementById('priority').value = '1';
740
+ document.getElementById('resultArea').style.display = 'none';
741
+ }, 1500);
742
+ }
743
+
744
+ showStatus(`✓ Step ${stepNum} submitted!`, 'success');
745
+ } catch (error) {
746
+ showStatus(`Error: ${error.message}`, 'error');
747
+ }
748
+ }
749
+
750
+ function updateStats() {
751
+ const stepNum = history.length;
752
+ const avgReward = stepNum > 0 ? (totalReward / stepNum) : 0;
753
+
754
+ document.getElementById('statStep').textContent = stepNum.toString();
755
+ document.getElementById('statReward').textContent = totalReward.toFixed(2);
756
+ document.getElementById('statAvg').textContent = avgReward.toFixed(2);
757
+
758
+ const progressPercent = (stepNum / taskDescriptions[currentTask].steps) * 100;
759
+ document.getElementById('progressFill').style.width = progressPercent + '%';
760
+ }
761
+
762
+ function displayResults(data) {
763
+ const resultHTML = `
764
+ <div class="result-item">
765
+ <span class="result-label">Reward:</span>
766
+ <span class="result-value ${data.reward.value >= 0.7 ? 'reward-high' : 'reward-low'}">
767
+ ${data.reward.value.toFixed(3)}
768
+ </span>
769
+ </div>
770
+ <div class="result-item">
771
+ <span class="result-label">Breakdown:</span>
772
+ <span class="result-value">${JSON.stringify(data.reward.breakdown).replace(/[{}]/g, '')}</span>
773
+ </div>
774
+ <div class="result-item">
775
+ <span class="result-label">Status:</span>
776
+ <span class="result-value">${data.done ? '✓ Task Complete' : '▶ In Progress'}</span>
777
+ </div>
778
+ `;
779
+ document.getElementById('resultContent').innerHTML = resultHTML;
780
+ document.getElementById('resultArea').style.display = 'block';
781
+ }
782
+
783
+ function updateHistory() {
784
+ if (history.length === 0) {
785
+ document.getElementById('historyContainer').innerHTML = '<div class="idle-message"><p>No actions yet</p></div>';
786
+ return;
787
+ }
788
+
789
+ const tableHTML = `
790
+ <table class="history-table">
791
+ <thead>
792
+ <tr>
793
+ <th>Step</th>
794
+ <th>Classification</th>
795
+ <th>Team</th>
796
+ <th>Priority</th>
797
+ <th>Reward</th>
798
+ </tr>
799
+ </thead>
800
+ <tbody>
801
+ ${history.map(h => `
802
+ <tr>
803
+ <td>${h.step}</td>
804
+ <td>${h.classification}</td>
805
+ <td>${h.team}</td>
806
+ <td>${h.priority}</td>
807
+ <td>${h.reward.toFixed(3)}</td>
808
+ </tr>
809
+ `).join('')}
810
+ </tbody>
811
+ </table>
812
+ `;
813
+ document.getElementById('historyContainer').innerHTML = tableHTML;
814
+ }
815
+
816
+ function showTaskComplete() {
817
+ const finalScore = (totalReward / history.length).toFixed(3);
818
+ document.getElementById('finalScore').textContent = finalScore;
819
+ document.getElementById('finalSteps').textContent = history.length;
820
+ document.getElementById('completeMessage').style.display = 'block';
821
+ document.getElementById('statScore').textContent = finalScore;
822
+ document.getElementById('formContainer').style.display = 'none';
823
+ showStatus(`🎉 Task complete! Final score: ${finalScore}`, 'success');
824
+ }
825
+
826
+ // Initialize on load
827
+ window.addEventListener('load', () => {
828
+ loadTaskInfo();
829
+ });
830
+ </script>
831
+ </body>
832
+
833
+ </html>