esemsc-am4224 commited on
Commit
5ad07d3
Β·
1 Parent(s): 5ef0ab2

feat: added ASBench annotation interface

Browse files
.gitignore CHANGED
@@ -205,4 +205,4 @@ cython_debug/
205
  # Marimo
206
  marimo/_static/
207
  marimo/_lsp/
208
- __marimo__/
 
205
  # Marimo
206
  marimo/_static/
207
  marimo/_lsp/
208
+ __marimo__/
annotation/annot_interface.py ADDED
@@ -0,0 +1,649 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import json
3
+ from datetime import datetime
4
+
5
+
6
+ st.set_page_config(
7
+ page_title="Annotation Interface",
8
+ layout="wide",
9
+ initial_sidebar_state="collapsed",
10
+ )
11
+
12
+ st.markdown("""
13
+ <style>
14
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
15
+
16
+ :root {
17
+ --bg: #0f1117;
18
+ --surface: #1a1d27;
19
+ --surface2: #22263a;
20
+ --border: #2e3350;
21
+ --accent: #4f8ef7;
22
+ --accent2: #7c5cbf;
23
+ --success: #3ecf8e;
24
+ --warning: #f5a623;
25
+ --danger: #e05c5c;
26
+ --text: #e8eaf0;
27
+ --text-muted: #9aa3bf;
28
+ --mono: 'JetBrains Mono', monospace;
29
+ --sans: 'Inter', sans-serif;
30
+ }
31
+
32
+ html, body, [class*="css"] {
33
+ font-family: var(--sans);
34
+ background-color: var(--bg);
35
+ color: var(--text);
36
+ font-size: 15px;
37
+ }
38
+
39
+ .stApp { background: var(--bg); }
40
+
41
+ /* Header */
42
+ .anno-header {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 16px;
46
+ padding: 28px 0 16px;
47
+ border-bottom: 1px solid var(--border);
48
+ margin-bottom: 28px;
49
+ }
50
+ .anno-title {
51
+ font-family: var(--sans);
52
+ font-size: 20px;
53
+ font-weight: 700;
54
+ color: var(--text);
55
+ letter-spacing: -0.3px;
56
+ }
57
+ .anno-subtitle {
58
+ font-size: 14px;
59
+ color: var(--text-muted);
60
+ font-weight: 400;
61
+ }
62
+
63
+ /* Task card */
64
+ .query-card {
65
+ background: var(--surface);
66
+ border: 1px solid var(--border);
67
+ border-left: 4px solid var(--accent);
68
+ border-radius: 8px;
69
+ padding: 20px 24px;
70
+ margin-bottom: 12px;
71
+ }
72
+ .query-label {
73
+ font-family: var(--sans);
74
+ font-size: 11px;
75
+ font-weight: 600;
76
+ text-transform: uppercase;
77
+ letter-spacing: 1.2px;
78
+ color: var(--accent);
79
+ margin-bottom: 10px;
80
+ }
81
+ .query-text {
82
+ font-size: 18px;
83
+ font-weight: 400;
84
+ line-height: 1.75;
85
+ color: var(--text);
86
+ }
87
+ .golden-label {
88
+ display: inline-block;
89
+ background: rgba(245,166,35,0.12);
90
+ border: 1px solid rgba(245,166,35,0.35);
91
+ color: var(--warning);
92
+ font-size: 13px;
93
+ font-weight: 500;
94
+ padding: 4px 12px;
95
+ border-radius: 4px;
96
+ margin-top: 10px;
97
+ }
98
+
99
+ /* Progress bar */
100
+ .progress-row {
101
+ display: flex;
102
+ align-items: center;
103
+ gap: 14px;
104
+ margin-bottom: 24px;
105
+ }
106
+ .progress-bar-outer {
107
+ flex: 1;
108
+ height: 5px;
109
+ background: var(--border);
110
+ border-radius: 3px;
111
+ overflow: hidden;
112
+ }
113
+ .progress-bar-inner {
114
+ height: 100%;
115
+ background: linear-gradient(90deg, var(--accent), var(--accent2));
116
+ border-radius: 3px;
117
+ transition: width 0.3s ease;
118
+ }
119
+ .progress-label {
120
+ font-size: 13px;
121
+ font-weight: 500;
122
+ color: var(--text-muted);
123
+ white-space: nowrap;
124
+ }
125
+
126
+ /* Score pills */
127
+ .score-pill {
128
+ font-size: 13px;
129
+ font-weight: 600;
130
+ padding: 3px 12px;
131
+ border-radius: 4px;
132
+ white-space: nowrap;
133
+ }
134
+ .score-high { background: rgba(62,207,142,0.15); color: var(--success); border: 1px solid rgba(62,207,142,0.3); }
135
+ .score-mid { background: rgba(245,166,35,0.12); color: var(--warning); border: 1px solid rgba(245,166,35,0.3); }
136
+ .score-low { background: rgba(224,92,92,0.12); color: var(--danger); border: 1px solid rgba(224,92,92,0.3); }
137
+
138
+ /* Reasoning box */
139
+ .reasoning-box {
140
+ background: var(--surface2);
141
+ border: 1px solid var(--border);
142
+ border-radius: 6px;
143
+ padding: 16px 20px;
144
+ font-size: 16px;
145
+ font-weight: 400;
146
+ color: var(--text-muted);
147
+ line-height: 1.75;
148
+ margin-top: 8px;
149
+ }
150
+ .reasoning-box .box-label {
151
+ font-size: 11px;
152
+ font-weight: 600;
153
+ text-transform: uppercase;
154
+ letter-spacing: 1.2px;
155
+ color: var(--accent2);
156
+ margin-bottom: 10px;
157
+ display: block;
158
+ }
159
+
160
+ /* Agent response markdown text */
161
+ .stMarkdown p, .stMarkdown li, .stMarkdown td {
162
+ font-size: 16px !important;
163
+ line-height: 1.75 !important;
164
+ }
165
+
166
+ /* Section labels */
167
+ .section-label {
168
+ font-size: 11px;
169
+ font-weight: 600;
170
+ text-transform: uppercase;
171
+ letter-spacing: 1.2px;
172
+ color: var(--text-muted);
173
+ margin-bottom: 8px;
174
+ display: block;
175
+ }
176
+ .section-label-accent {
177
+ font-size: 11px;
178
+ font-weight: 600;
179
+ text-transform: uppercase;
180
+ letter-spacing: 1.2px;
181
+ color: var(--accent);
182
+ margin-bottom: 8px;
183
+ display: block;
184
+ }
185
+
186
+ /* Dividers */
187
+ .section-divider {
188
+ border: none;
189
+ border-top: 1px solid var(--border);
190
+ margin: 20px 0;
191
+ }
192
+
193
+ /* Stats row */
194
+ .stats-row {
195
+ display: flex;
196
+ gap: 12px;
197
+ flex-wrap: wrap;
198
+ margin-bottom: 20px;
199
+ }
200
+ .stat-box {
201
+ background: var(--surface);
202
+ border: 1px solid var(--border);
203
+ border-radius: 8px;
204
+ padding: 14px 20px;
205
+ text-align: center;
206
+ min-width: 110px;
207
+ }
208
+ .stat-val {
209
+ font-size: 24px;
210
+ font-weight: 700;
211
+ color: var(--accent);
212
+ line-height: 1.2;
213
+ }
214
+ .stat-lbl {
215
+ font-size: 12px;
216
+ font-weight: 500;
217
+ color: var(--text-muted);
218
+ margin-top: 4px;
219
+ text-transform: uppercase;
220
+ letter-spacing: 0.8px;
221
+ }
222
+
223
+ /* Nav buttons */
224
+ .stButton > button {
225
+ background: var(--surface) !important;
226
+ color: var(--text) !important;
227
+ border: 1px solid var(--border) !important;
228
+ border-radius: 6px !important;
229
+ font-family: var(--sans) !important;
230
+ font-size: 14px !important;
231
+ font-weight: 500 !important;
232
+ transition: all 0.15s ease !important;
233
+ }
234
+ .stButton > button:hover {
235
+ border-color: var(--accent) !important;
236
+ color: var(--accent) !important;
237
+ background: rgba(79,142,247,0.07) !important;
238
+ }
239
+
240
+ /* Selectbox / textarea overrides */
241
+ .stSelectbox > div > div, .stTextArea > div > textarea {
242
+ background: var(--surface2) !important;
243
+ border-color: var(--border) !important;
244
+ color: var(--text) !important;
245
+ font-family: var(--sans) !important;
246
+ font-size: 14px !important;
247
+ }
248
+
249
+ /* Expander styling */
250
+ details > summary {
251
+ font-family: var(--sans) !important;
252
+ font-size: 14px !important;
253
+ font-weight: 500 !important;
254
+ color: var(--text) !important;
255
+ }
256
+ details > summary:hover {
257
+ color: var(--accent) !important;
258
+ }
259
+
260
+ /* Response time mono */
261
+ .resp-time {
262
+ font-family: var(--mono);
263
+ font-size: 13px;
264
+ color: var(--text-muted);
265
+ }
266
+
267
+ /* Hide default streamlit elements */
268
+ #MainMenu, footer, header { visibility: hidden; }
269
+ .block-container { padding-top: 1rem; max-width: 1200px; }
270
+ </style>
271
+ """, unsafe_allow_html=True)
272
+
273
+
274
+ class AnnotationInterface:
275
+ def __init__(self):
276
+ self._init_session_state()
277
+
278
+ def _init_session_state(self):
279
+ if "current_query_index" not in st.session_state:
280
+ st.session_state.current_query_index = 0
281
+ if "annotations" not in st.session_state:
282
+ st.session_state.annotations = {}
283
+ if "grouped_data" not in st.session_state:
284
+ st.session_state.grouped_data = {}
285
+ if "query_ids" not in st.session_state:
286
+ st.session_state.query_ids = []
287
+ if "expanded_responses" not in st.session_state:
288
+ st.session_state.expanded_responses = set()
289
+
290
+ @staticmethod
291
+ def load_jsonl(file):
292
+ data = []
293
+ content = file.getvalue().decode("utf-8")
294
+ for line in content.strip().split("\n"):
295
+ if line.strip():
296
+ data.append(json.loads(line))
297
+ return data
298
+
299
+ @staticmethod
300
+ def process_data(queries_list, responses_list, labels_list):
301
+ queries_dict = {list(q.keys())[0]: q[list(q.keys())[0]] for q in queries_list}
302
+ responses_dict = {list(r.keys())[0]: r[list(r.keys())[0]] for r in responses_list}
303
+ labels_dict = {list(l.keys())[0]: l[list(l.keys())[0]] for l in labels_list}
304
+
305
+ all_query_ids = set(queries_dict.keys()) & set(responses_dict.keys()) & set(labels_dict.keys())
306
+
307
+ grouped = {}
308
+ for qid in sorted(all_query_ids):
309
+ query_text = queries_dict[qid][0]
310
+ golden_label = queries_dict[qid][1] if type(queries_dict[qid][1]) == str else None
311
+ responses = responses_dict.get(qid, {})
312
+ labels = labels_dict.get(qid, {})
313
+
314
+ items = []
315
+ for agent_id in responses.keys():
316
+ if agent_id in labels:
317
+ response_text, response_time = responses[agent_id]
318
+ llm_score, llm_reasoning = labels[agent_id]
319
+ items.append({
320
+ "query_id": qid,
321
+ "agent_id": agent_id,
322
+ "query": query_text,
323
+ "golden_label": golden_label,
324
+ "response": response_text,
325
+ "response_time": response_time,
326
+ "llm_score": llm_score,
327
+ "llm_reasoning": llm_reasoning,
328
+ })
329
+
330
+ if items:
331
+ grouped[qid] = {
332
+ "query": query_text,
333
+ "golden_label": golden_label,
334
+ "responses": items,
335
+ }
336
+
337
+ return grouped
338
+
339
+ @staticmethod
340
+ def score_class(score):
341
+ if score >= 4:
342
+ return "score-high"
343
+ elif score >= 3:
344
+ return "score-mid"
345
+ return "score-low"
346
+
347
+ def get_annotation_key(self, query_id, agent_id):
348
+ return f"{query_id}___{agent_id}"
349
+
350
+ def count_annotated_for_query(self, query_id, responses):
351
+ return sum(
352
+ 1 for r in responses
353
+ if self.get_annotation_key(query_id, r["agent_id"]) in st.session_state.annotations
354
+ )
355
+
356
+ def count_total_annotated(self):
357
+ return len(st.session_state.annotations)
358
+
359
+ def count_total_responses(self):
360
+ return sum(len(v["responses"]) for v in st.session_state.grouped_data.values())
361
+
362
+ @staticmethod
363
+ def export_annotations():
364
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
365
+ filename = f"human_annotations_{timestamp}.json"
366
+ output_data = {
367
+ "timestamp": timestamp,
368
+ "annotations": list(st.session_state.annotations.values()),
369
+ }
370
+ return json.dumps(output_data, indent=2), filename
371
+
372
+ def load_continuation(self, file):
373
+ """Parse a previous annotations JSON export and return a dict keyed by query_id___agent_id."""
374
+ content = file.getvalue().decode("utf-8")
375
+ data = json.loads(content)
376
+ annotations = {}
377
+ loaded = 0
378
+ for item in data.get("annotations", []):
379
+ qid = item.get("query_id")
380
+ aid = item.get("agent_id")
381
+ if qid and aid:
382
+ key = self.get_annotation_key(qid, aid)
383
+ annotations[key] = {
384
+ "query_id": qid,
385
+ "agent_id": aid,
386
+ "query": item.get("query", ""),
387
+ "llm_score": item.get("llm_score"),
388
+ "llm_reasoning": item.get("llm_reasoning", ""),
389
+ "human_score": item.get("human_score", 3),
390
+ "explanation": item.get("explanation", ""),
391
+ }
392
+ loaded += 1
393
+ return annotations, loaded
394
+
395
+ def render_file_upload(self):
396
+ st.markdown('<div class="anno-header"><span class="anno-title">Annotation Interface</span><span class="anno-subtitle"></span></div>', unsafe_allow_html=True)
397
+
398
+ st.markdown("### Upload Data Files")
399
+ col1, col2, col3 = st.columns(3)
400
+ with col1:
401
+ queries_file = st.file_uploader("Tasks (JSONL)", type=["jsonl"], key="queries")
402
+ with col2:
403
+ responses_file = st.file_uploader("Agent Responses (JSONL)", type=["jsonl"], key="responses")
404
+ with col3:
405
+ labels_file = st.file_uploader("LLM Judge Labels (JSONL)", type=["jsonl"], key="labels")
406
+
407
+ st.markdown('<hr class="section-divider">', unsafe_allow_html=True)
408
+ st.markdown("### Resume from Previous Session *(optional)*")
409
+ st.markdown(
410
+ '<span style="font-size:13px;color:var(--text-muted)">Upload a previously downloaded annotations JSON file to pre-fill your prior scores and continue where you left off.</span>',
411
+ unsafe_allow_html=True,
412
+ )
413
+ continuation_file = st.file_uploader(
414
+ "Continuation file (JSON)",
415
+ type=["json"],
416
+ key="continuation",
417
+ help="A human_annotations_*.json file downloaded from a previous session.",
418
+ )
419
+
420
+ if continuation_file:
421
+ try:
422
+ preview_data = json.loads(continuation_file.getvalue().decode("utf-8"))
423
+ n = len(preview_data.get("annotations", []))
424
+ ts = preview_data.get("timestamp", "unknown")
425
+ st.markdown(
426
+ f'<div style="background:rgba(79,142,247,0.08);border:1px solid rgba(79,142,247,0.25);border-radius:6px;padding:12px 16px;font-size:13px;color:var(--text-muted);margin-top:8px;">'
427
+ f'<strong style="color:var(--accent)">βœ“ Continuation file detected</strong> &nbsp;Β·&nbsp; '
428
+ f'Saved on <code>{ts}</code> &nbsp;Β·&nbsp; <strong>{n}</strong> prior annotation(s) will be restored.'
429
+ f'</div>',
430
+ unsafe_allow_html=True,
431
+ )
432
+ except Exception:
433
+ st.warning("Could not parse continuation file β€” it will be ignored.")
434
+ continuation_file = None
435
+
436
+ st.markdown('<div style="height:8px"></div>', unsafe_allow_html=True)
437
+
438
+ if queries_file and responses_file and labels_file:
439
+ if st.button("Load Data β†’"):
440
+ with st.spinner("Processing files..."):
441
+ queries_list = self.load_jsonl(queries_file)
442
+ responses_list = self.load_jsonl(responses_file)
443
+ labels_list = self.load_jsonl(labels_file)
444
+ grouped = self.process_data(queries_list, responses_list, labels_list)
445
+ st.session_state.grouped_data = grouped
446
+ st.session_state.query_ids = list(grouped.keys())
447
+ st.session_state.current_query_index = 0
448
+
449
+ restored = 0
450
+ if continuation_file:
451
+ try:
452
+ continuation_file.seek(0)
453
+ prior_annotations, restored = self.load_continuation(continuation_file)
454
+ st.session_state.annotations = prior_annotations
455
+ except Exception as e:
456
+ st.warning(f"Could not load continuation file: {e}")
457
+ st.session_state.annotations = {}
458
+ else:
459
+ st.session_state.annotations = {}
460
+
461
+ total_resp = sum(len(v["responses"]) for v in grouped.values())
462
+ msg = f"Loaded {len(grouped)} queries with {total_resp} total responses."
463
+ if restored:
464
+ msg += f" Restored **{restored}** prior annotation(s) from continuation file."
465
+ st.success(msg)
466
+ st.rerun()
467
+ else:
468
+ st.info("Upload the three required JSONL files to begin annotation.")
469
+
470
+ def render_main(self):
471
+ query_ids = st.session_state.query_ids
472
+ grouped = st.session_state.grouped_data
473
+ total_queries = len(query_ids)
474
+ qidx = st.session_state.current_query_index
475
+ qid = query_ids[qidx]
476
+ query_data = grouped[qid]
477
+
478
+ total_responses = self.count_total_responses()
479
+ total_annotated = self.count_total_annotated()
480
+
481
+ # Header
482
+ st.markdown(
483
+ f'<div class="anno-header">'
484
+ f'<span class="anno-title">Annotation Interface</span>'
485
+ f'<span class="anno-subtitle">Task evaluation workbench</span>'
486
+ f'</div>',
487
+ unsafe_allow_html=True,
488
+ )
489
+
490
+ # Stats row
491
+ q_annotated = self.count_annotated_for_query(qid, query_data["responses"])
492
+ q_total = len(query_data["responses"])
493
+ st.markdown(
494
+ f'<div class="stats-row">'
495
+ f'<div class="stat-box"><div class="stat-val">{qidx + 1}/{total_queries}</div><div class="stat-lbl">Task</div></div>'
496
+ f'<div class="stat-box"><div class="stat-val">{q_annotated}/{q_total}</div><div class="stat-lbl">Annotated</div></div>'
497
+ f'<div class="stat-box"><div class="stat-val">{total_annotated}/{total_responses}</div><div class="stat-lbl">Total Done</div></div>'
498
+ f'</div>',
499
+ unsafe_allow_html=True,
500
+ )
501
+
502
+ # Progress bar
503
+ pct = (total_annotated / total_responses * 100) if total_responses else 0
504
+ st.markdown(
505
+ f'<div class="progress-row">'
506
+ f'<div class="progress-bar-outer"><div class="progress-bar-inner" style="width:{pct:.1f}%"></div></div>'
507
+ f'<span class="progress-label">{pct:.0f}% complete</span>'
508
+ f'</div>',
509
+ unsafe_allow_html=True,
510
+ )
511
+
512
+ # Task card
513
+ golden_html = ""
514
+ if query_data["golden_label"]:
515
+ golden_html = f'<div class="golden-label">βš‘ GOLDEN LABEL: {query_data["golden_label"]}</div>'
516
+ st.markdown(
517
+ f'<div class="query-card">'
518
+ f'<div class="query-label">Task Β· {qid}</div>'
519
+ f'<div class="query-text">{query_data["query"]}</div>'
520
+ f'{golden_html}'
521
+ f'</div>',
522
+ unsafe_allow_html=True,
523
+ )
524
+
525
+ st.markdown('<hr class="section-divider">', unsafe_allow_html=True)
526
+
527
+ # Responses
528
+ st.markdown(f'<div class="section-label">{q_total} Responses β€” expand to annotate</div>', unsafe_allow_html=True)
529
+
530
+ for i, resp in enumerate(query_data["responses"]):
531
+ anno_key = self.get_annotation_key(qid, resp["agent_id"])
532
+ existing = st.session_state.annotations.get(anno_key, {})
533
+ has_annotation = bool(existing)
534
+ llm_score = resp["llm_score"]
535
+ sc = self.score_class(llm_score)
536
+
537
+ # Expander label
538
+ short_agent = resp["agent_id"][:40] + "…" if len(resp["agent_id"]) > 40 else resp["agent_id"]
539
+ label = f"Response {i+1:02d} β€” {short_agent} Β· LLM: {llm_score}/5{' Β· βœ“ annotated' if has_annotation else ''}"
540
+
541
+ with st.expander(label, expanded=False):
542
+ # LLM info row
543
+ col_score, col_time = st.columns([1, 2])
544
+ with col_score:
545
+ st.markdown(f'<div style="margin-bottom:8px"><span class="section-label">LLM Score</span><span class="score-pill {sc}" style="font-size:15px;padding:5px 16px">{llm_score}/5</span></div>', unsafe_allow_html=True)
546
+ with col_time:
547
+ st.markdown(f'<div style="margin-bottom:8px"><span class="section-label">Response Time</span><span class="resp-time">{resp["response_time"]:.2f}s</span></div>', unsafe_allow_html=True)
548
+
549
+ st.markdown(f'<div class="reasoning-box"><span class="box-label">LLM Reasoning</span>{resp["llm_reasoning"]}</div>', unsafe_allow_html=True)
550
+
551
+ st.markdown('<div style="height:14px"></div>', unsafe_allow_html=True)
552
+
553
+ st.markdown('<span class="section-label">Agent Response</span>', unsafe_allow_html=True)
554
+ st.markdown(resp["response"])
555
+
556
+ st.markdown('<hr class="section-divider">', unsafe_allow_html=True)
557
+
558
+ # Annotation inputs
559
+ st.markdown('<span class="section-label-accent">Your Annotation</span>', unsafe_allow_html=True)
560
+
561
+ a_col1, a_col2 = st.columns([1, 3])
562
+ with a_col1:
563
+ human_score = st.selectbox(
564
+ "Score (1–5)",
565
+ options=[1, 2, 3, 4, 5],
566
+ index=existing.get("human_score", 3) - 1,
567
+ key=f"score_{anno_key}",
568
+ )
569
+ with a_col2:
570
+ human_explanation = st.text_area(
571
+ "Explanation",
572
+ value=existing.get("explanation", ""),
573
+ height=90,
574
+ placeholder="Your reasoning for the score…",
575
+ key=f"expl_{anno_key}",
576
+ )
577
+
578
+ if st.button("Save annotation", key=f"save_{anno_key}"):
579
+ st.session_state.annotations[anno_key] = {
580
+ "query_id": qid,
581
+ "agent_id": resp["agent_id"],
582
+ "query": resp["query"],
583
+ "llm_score": llm_score,
584
+ "llm_reasoning": resp["llm_reasoning"],
585
+ "human_score": human_score,
586
+ "explanation": human_explanation,
587
+ }
588
+ st.rerun()
589
+
590
+ st.markdown('<hr class="section-divider">', unsafe_allow_html=True)
591
+
592
+ # Navigation
593
+ nav_col1, nav_col2, nav_col3, nav_col4 = st.columns([1, 1, 1, 2])
594
+ with nav_col1:
595
+ if st.button("← Previous", disabled=qidx == 0, use_container_width=True):
596
+ st.session_state.current_query_index -= 1
597
+ st.rerun()
598
+ with nav_col2:
599
+ if st.button("Next β†’", disabled=qidx >= total_queries - 1, use_container_width=True):
600
+ st.session_state.current_query_index += 1
601
+ st.rerun()
602
+ with nav_col3:
603
+ jump = st.number_input("Go to query #", min_value=1, max_value=total_queries, value=qidx + 1, label_visibility="collapsed")
604
+ if jump - 1 != qidx:
605
+ st.session_state.current_query_index = jump - 1
606
+ st.rerun()
607
+ with nav_col4:
608
+ if total_annotated > 0:
609
+ json_str, filename = self.export_annotations()
610
+ st.download_button(
611
+ label=f"⬇ Download all annotations ({total_annotated})",
612
+ data=json_str,
613
+ file_name=filename,
614
+ mime="application/json",
615
+ use_container_width=True,
616
+ )
617
+
618
+ # Sidebar
619
+ with st.sidebar:
620
+ st.markdown("### βš™οΈ Settings")
621
+ if st.button("Load new files"):
622
+ st.session_state.grouped_data = {}
623
+ st.session_state.query_ids = []
624
+ st.session_state.annotations = {}
625
+ st.session_state.current_query_index = 0
626
+ st.rerun()
627
+
628
+ st.markdown("---")
629
+ st.markdown("### πŸ“Š Task Progress")
630
+ for i, qid_s in enumerate(query_ids):
631
+ qd = grouped[qid_s]
632
+ done = self.count_annotated_for_query(qid_s, qd["responses"])
633
+ total = len(qd["responses"])
634
+ marker = "βœ“" if done == total else ("β—‘" if done > 0 else "β—‹")
635
+ label_s = f"{marker} [{i+1}] {qid_s[:20]}… ({done}/{total})"
636
+ if st.button(label_s, key=f"nav_{qid_s}", use_container_width=True):
637
+ st.session_state.current_query_index = i
638
+ st.rerun()
639
+
640
+ def run(self):
641
+ if not st.session_state.grouped_data:
642
+ self.render_file_upload()
643
+ else:
644
+ self.render_main()
645
+
646
+
647
+ if __name__ == "__main__":
648
+ app = AnnotationInterface()
649
+ app.run()
annotation/annotation_tasks/judged_labels.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
annotation/annotation_tasks/responses.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
annotation/annotation_tasks/tasks.jsonl ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"g:0": ["Of the cities within the United States where U.S. presidents were born, which two are the farthest apart from the westernmost to the easternmost going east, giving the city names only? Give them to me in alphabetical order, in a comma-separated list", "Braintree, Honolulu"]}
2
+ {"g:1": ["In Unlambda, what exact charcter or text needs to be added to correct the following code to output \"For penguins\"? If what is needed is a character, answer with the name of the character. If there are different names for the character, use the shortest. The text location is not needed. Code:\n\n`r```````````.F.o.r. .p.e.n.g.u.i.n.si", "backtick"]}
3
+ {"g:2": ["A 5-man group made up of one tank, one healer, and three DPS is doing a dungeon that was just released in World of Warcraft. Two are plate wearers and two are cloth wearers. At the final boss, both the tank and the healer are casting holy spells. Ice and fire are being used, each one by a different DPS. A bear from the group is attacking the boss. Metamorphosis is cast. The Kilt of the Forgotten One drops as loot, but no one can use it. If all classes were using their class abilities and all classes are unique, what are the five classes in the group in alphabetical order separated by commas?", "Death Knight, Hunter, Paladin, Priest, Warlock"]}
4
+ {"g:3": [".rewsna eht sa \"tfel\" drow eht fo etisoppo eht etirw ,ecnetnes siht dnatsrednu uoy fI", "Right"]}
5
+ {"g:4": ["In Audre Lorde\u2019s poem \u201cFather Son and Holy Ghost\u201d, what is the number of the stanza in which some lines are indented?", "2"]}
6
+ {"g:5": ["In terms of geographical distance between capital cities, which 2 countries are the furthest from each other within the ASEAN bloc according to wikipedia? Answer using a comma separated list, ordering the countries by alphabetical order.", "Indonesia, Myanmar"]}
7
+ {"g:6": ["What two-word type of model did Manash Pratim Kashyap's and PS Fader's studies in customer retention studies published during 2018-2019 have in common (no punctuation)?", "beta geometric"]}
8
+ {"g:7": ["It is 1999. Before you party like it is 1999, please assist me in settling a bet.\n\nFiona Apple and Paula Cole released albums prior to 1999. Of these albums, which didn't receive a letter grade from Robert Christgau? Provide your answer as a comma delimited list of album titles, sorted alphabetically.", "Harbinger, Tidal"]}
9
+ {"g:8": ["You are a telecommunications engineer who wants to build cell phone towers on a stretch of road. In the reference file is a layout of the road and nearby houses. Each dash, \"-\", is a marker indicating a mile. Each capital H indicates a house located next to a mile marker, appearing above or below the stretch of road. Each cell phone tower can cover houses located next to the road within a 4-mile radius. Find the minimum number of cell phone towers needed to cover all houses next to the road. Your answer should be a positive numerical integer value.", "3"]}
10
+ {"g:9": ["It's May 2023, and I'm about to drive across the U.S. from California to Maine. I always recycle my water bottles at the end of a trip, and I drink 5 12-ounce water bottles for every 100 miles I travel, rounded to the nearest 100. Assuming I follow I-40 from Los Angeles to Cincinnati, then take I-90 from Cincinnati to Augusta, how many dollars will I get back according to Wikipedia?", "8"]}
11
+ {"t:0": ["Draft a legally compliant performance improvement plan (PIP) and termination checklist for an underperforming salaried customer-support employee in Jaipur, Rajasthan, India, at a 60-person tech company. Include scripts, a 30/60/90-day timeline, and documentation templates for meetings, warnings, and final separation.", ["agt:openaiagents:402f63@v1.1"]]}
12
+ {"t:1": ["Create an IP risk checklist and filing roadmap (patents, trademarks, trade secrets) for a new product: \u201cMantaTest,\u201d an open\u2011source GPL C# OOP neural\u2011network framework (http://www.mantatest.com/). Include priority deadlines, steps for US first filing/PCT, trademark clearance/filing, trade secret controls, and a basic IP portfolio tracker template.", ["agt:openaiagents:0fc77b@v1.1"]]}
13
+ {"t:2": ["Build a step-by-step checklist (with required forms and evidence) to apply for VA health care and verify eligibility, including how to use VA.gov and what to do if denied.", ["agt:openaiagents:fdd41a@v1.1"]]}
14
+ {"t:3": ["Given this job description for a Records & Information Management role (ICRM-related) from http://www.gamerevolution.com/oldsite/games/dreamcast/action/maken_x.htm, rewrite my resume summary and bullets for ATS and role priorities. My background: 5 years records management, retention schedules, eDiscovery, SharePoint, compliance, process improvement. Note key hiring-manager priorities.", ["agt:agentainetwork:635a65@v1.1"]]}
15
+ {"t:4": ["Make a country-by-country briefing comparing how India, Japan, and Brazil frame the UN Security Council reform debate, with key cultural/historical context and recent diplomacy timeline.", ["agt:openaiagents:82bfe6@v1.1"]]}
16
+ {"t:5": ["Create a triage decision tree for my dog\u2019s vomiting and diarrhea (include red flags, at-home steps, and when to seek emergency care vs schedule a vet visit).", ["agt:openaiagents:f3b5e9@v1.1"]]}
17
+ {"t:6": ["Can you help me map my anxiety triggers by asking me a few questions, then summarize patterns and give a personalized 7-day coping plan with daily check-ins?", ["agt:openaiagents:f94d6c@v1.1"]]}
18
+ {"t:7": ["Create a HACCP-style risk assessment for my homemade refrigerated chicken salad. Ingredients: cooked diced chicken breast, mayonnaise, celery, red onion, Dijon mustard, lemon juice, salt, pepper. Process: cook chicken to 74\u00b0C/165\u00b0F, cool, dice, mix with ingredients, portion into sealed containers, refrigerate, serve. Identify hazards, critical control points, critical limits, monitoring/corrective actions, and estimate safe shelf life at \u22644\u00b0C/40\u00b0F.", ["agt:openaiagents:7d97ac@v1.1"]]}
19
+ {"t:8": ["Review my SaaS services agreement for AcmeCloud (B2B subscription software) with Customer XYZ (US-based). Provide prioritized redlines and a negotiation plan (fallback positions, tradeoffs, and email wording) to reduce liability and tighten payment and termination terms. Assume current contract includes broad indemnity, uncapped damages, net-60, and vague termination.", ["agt:openaiagents:88512b@v1.1"]]}
20
+ {"t:9": ["Create a 90-day retention plan for our 25-person startup, including manager actions, stay-interview questions, and metrics to track voluntary turnover monthly.", ["agt:openaiagents:e373b2@v1.1"]]}
21
+ {"t:10": ["Set up a Zapier automation: new Typeform lead \u2192 enrich with Clearbit \u2192 create/update HubSpot contact + deal \u2192 post to Slack, and alert me if enrichment fails.", ["agt:openaiagents:a8d6ea@v1.1"]]}
22
+ {"t:11": ["I\u2019m a Sales Operations Manager at a 200-person B2B SaaS company using Salesforce, HubSpot, Outreach, and Google Sheets. Analyze my weekly tasks (lead routing, pipeline hygiene, forecast reporting, territory changes, deal desk approvals, dashboard upkeep, enablement, renewals handoffs) and bundle into AI-assisted workflows with automations, suggested tools, time-saved estimates, and a 30/60/90-day rollout plan.", ["agt:agentainetwork:abca0b@v1.1"]]}
23
+ {"t:12": ["Design a Make.com scenario that auto-replies to new Gmail emails using a Google Sheets FAQ lookup, includes error handling, and outputs a step-by-step module blueprint.", ["agt:openaiagents:7ac082@v1.1"]]}
24
+ {"t:13": ["Audit February 2026 cross-channel marketing spend and ROI for Acme D2C (Google Search/Shopping, Meta, TikTok, email, affiliates). Provide budget reallocation, 30-day optimization plan with KPI targets, team owners, and an analytics tracking checklist (UTMs, GA4, pixels/CAPI, offline conversions, attribution).", ["agt:openaiagents:d2d940@v1.1"]]}
25
+ {"t:14": ["Find current deals on premium project-management tools under $10/month (e.g., Notion, Asana alternatives) and list the best offers with links and renewal prices.", ["agt:agentainetwork:6e79ea@v1.1"]]}
26
+ {"t:15": ["Create a shareable daily \u201cVisitor Sign-in\u201d form with required fields, a liability acknowledgment checkbox, and an auto-generated timestamp; include a simple admin review checklist.", ["agt:openaiagents:ec899f@v1.1"]]}
27
+ {"t:16": ["I\u2019m moving into a small apartment\u2014give me a 30\u2011minute, low-cost plan (with materials list) to organize my kitchen and reduce food waste using simple DIY hacks.", ["agt:openaiagents:2d1a05@v1.1"]]}
28
+ {"t:17": ["Turn my raw meeting notes (below) from a 1-hour project sync (Mar 6, 2026, 10:00\u201311:00 AM ET; attendees: Alex Chen, Priya Patel, Jordan Lee) into polished minutes with: agenda sections, key discussion points, decisions, action items (owner + due date), parking lot, and a 3\u20135 sentence email-ready recap. Notes: [PASTE NOTES HERE].", ["agt:agentainetwork:b62ccf@v1.1"]]}
29
+ {"t:18": ["I\u2019m meeting Sarah Chen (VP Partnerships at a B2B SaaS procurement platform) tomorrow to discuss a potential integration/reseller deal. Using professional insights from this video https://www.youtube.com/watch?v=B8TOz25ctGw, draft a 30\u2011minute tailored meeting agenda, 6 rapport starters, and 5 likely objections with concise responses.", ["agt:agentainetwork:9d9c3f@v1.1"]]}
30
+ {"t:19": ["Given this job description for an ATS-screened role at ImproveNet (radiodata.com) in the home appliances market, identify what the company and hiring manager likely prioritize, then rewrite my current resume bullet points to match the required skills/keywords. Assume I\u2019m a mid-level product marketing manager with 5 years\u2019 experience in consumer appliances.", ["agt:agentainetwork:635a65@v1.1"]]}
31
+ {"t:20": ["In Figma, build a reusable color + typography token system (light/dark) and show the exact token names plus CSS variable equivalents I can paste into my codebase.", ["agt:openaiagents:207933@v1.1"]]}
32
+ {"t:21": ["Create a Figma-ready wireframe (screens + components list) for a mobile inventory system: login, dashboard, scan item, add/edit item, stock adjustments, audit log, and alerts.", ["agt:openaiagents:abb118@v1.1"]]}
33
+ {"t:22": ["Create a low\u2011fidelity Figma wireframe for a mobile checkout flow (cart \u2192 address \u2192 payment \u2192 review), with frame list, component labels, and layout specs.", ["agt:openaiagents:e4fbaf@v1.1"]]}
34
+ {"t:23": ["Design a cohesive set of 12 minimalist space-themed app icons as a single downloadable PNG grid, with consistent stroke/size and transparent background.", ["agt:openaiagents:e624ec@v1.1"]]}
35
+ {"t:24": ["Create PlantUML for a data flow diagram of an online food ordering system (customer, restaurant, payment gateway), including processes, data stores, and labeled data flows.", ["agt:openaiagents:9eb1e6@v1.1"]]}
36
+ {"t:25": ["Create a 1:1 neon cyberpunk coffee logo image, then save it and give me a shareable link plus downloadable PNG and SVG versions.", ["agt:openaiagents:5f952a@v1.1"]]}
37
+ {"t:26": ["Convert this Python function into a flowchart (Mermaid) showing decision branches and loops, and label each node with the corresponding code lines:\n\n```python\ndef validate_password(pw):\n if len(pw) < 8:\n return False\n has_upper = False\n has_digit = False\n for ch in pw:\n if ch.isupper():\n has_upper = True\n elif ch.isdigit():\n has_digit = True\n if has_upper and has_digit:\n return True\n return False\n```", ["agt:openaiagents:61ca34@v1.1"]]}
38
+ {"t:27": ["Create 3 ultra-detailed FLUX.1 photoreal cinematic prompts for a rain-soaked neon alley detective scene, with camera/lens, lighting, composition, wardrobe, and negative prompts.", ["agt:openaiagents:627242@v1.1"]]}
39
+ {"t:28": ["Convert my Figma design (assume a SaaS dashboard UI with header, sidebar, buttons, form inputs, cards, table, and modal) into a responsive React + TypeScript component library using Tailwind. Include exported SVG assets, folder/file structure, and paste-ready code examples for each component.", ["agt:openaiagents:3e180b@v1.1"]]}
40
+ {"t:29": ["Convert a Figma frame screenshot of a modern landing-page hero (top nav with logo and 4 links, primary CTA button, left-aligned headline/subtext, and right-side image placeholder) into responsive semantic HTML/CSS with matching spacing/colors and hover states. Output a single clean code snippet.", ["agt:openaiagents:f111b6@v1.1"]]}
41
+ {"t:30": ["I have two photos of a cheek rash (photo A taken today, photo B taken 3 days ago). Based only on typical visual changes, estimate likely causes (e.g., contact dermatitis, acne, eczema, infection), list red-flag symptoms needing urgent care, and give a 7-day home-care plan including step-by-step patch-testing for new products.", ["agt:openaiagents:27b7a3@v1.1"]]}
42
+ {"t:31": ["Create a step-by-step triage checklist and 2-week treatment plan for a suspected eczema flare (adult), including OTC options, red flags, and when to book dermatology follow-up.", ["agt:openaiagents:673673@v1.1"]]}
43
+ {"t:32": ["Create a 2-week nail recovery plan for brittle, peeling nails after gel removal\u2014daily steps, product checklist, and hygiene rules; include a simple progress tracker.", ["agt:openaiagents:2dc63f@v1.1"]]}
44
+ {"t:33": ["Using my uploaded face selfie (I\u2019m a 28-year-old with combination skin, mild acne on chin, visible pores on nose, some redness on cheeks), analyze likely skin concerns and create a morning/night skincare checklist. Recommend 3 budget and 3 high-end products tailored to these concerns, with brief usage notes.", ["agt:openaiagents:7c1ab0@v1.1"]]}
45
+ {"t:34": ["Create a Wonyoungism-style morning-to-night schedule with skincare layering steps (AM/PM), a beginner workout, and 5 daily affirmations tailored to oily, acne-prone skin and school days.", ["agt:openaiagents:005d0e@v1.1"]]}
46
+ {"t:35": ["Create a 7-day weight-loss meal plan (daily calories + macros) and a grocery list using these details: 32-year-old female, 5'6\", 170 lb, goal 1 lb/week loss; strength trains 3x/week + 8k steps/day; current diet: oatmeal, chicken/rice, salads, snacks; must-haves: eggs, Greek yogurt, chicken, salmon, quinoa, spinach; lactose-sensitive.", ["agt:openaiagents:bd9a01@v1.1"]]}
47
+ {"t:36": ["Using current guidelines and key trials, draft a SOAP note and evidence-based treatment plan for an adult with chronic insomnia, including CBT-I steps and medication options.", ["agt:openaiagents:b1415c@v1.1"]]}
48
+ {"t:37": ["Create an evidence-based differential and stepwise workup for acute hypoxemic respiratory failure with bilateral infiltrates, including ventilation strategies, meds, contraindications, and patient handout points.", ["agt:openaiagents:9334dd@v1.1"]]}
49
+ {"t:38": ["Create a 2-week supplement-and-herbal plan for better sleep and lower stress, including dosages, timing, safety interactions, and a simple shopping list.", ["agt:openaiagents:74a116@v1.1"]]}
50
+ {"t:39": ["I have a headache, stiff neck, and fever since last night\u2014can you triage me with a red-flag checklist and tell me whether I should go to urgent care or ER today?", ["agt:openaiagents:659375@v1.1"]]}