ybkim95 commited on
Commit
074c1f1
·
verified ·
1 Parent(s): 47ceb5b

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +229 -112
app.py CHANGED
@@ -9,6 +9,8 @@ from __future__ import annotations
9
 
10
  import json
11
  import os
 
 
12
  from pathlib import Path
13
 
14
  import gradio as gr
@@ -46,11 +48,6 @@ MODEL_DISPLAY = {
46
  "gpt-5-nano": "GPT-5 Nano",
47
  }
48
 
49
- CONSISTENT_TEAM_HELPS = {
50
- "D8_csv_cleanup", "MULTI1_fullstack_fix", "O2_incident_rootcause",
51
- "P1_policy_config", "PIPE1_etl_fix", "TEST2_regression", "TEST5_mutation_resistant",
52
- }
53
-
54
  # ---------------------------------------------------------------------------
55
  # CSS
56
  # ---------------------------------------------------------------------------
@@ -62,7 +59,6 @@ CUSTOM_CSS = """
62
  --badge-radius: 4px;
63
  }
64
 
65
- /* Header */
66
  .tb-header {
67
  background: linear-gradient(135deg, #1e3a8a 0%, #1d4ed8 50%, #2563eb 100%);
68
  border-radius: 12px; padding: 28px 32px 24px; margin-bottom: 8px;
@@ -75,7 +71,6 @@ CUSTOM_CSS = """
75
  margin: 0; font-size: 1rem; color: #ffffff !important; opacity: 0.92;
76
  }
77
 
78
- /* Stat cards */
79
  .stat-grid {
80
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
81
  gap: 12px; margin-bottom: 16px;
@@ -88,19 +83,16 @@ CUSTOM_CSS = """
88
  .stat-card .stat-value { font-size: 1.5rem; font-weight: 700; color: var(--bench-blue); line-height: 1; }
89
  .stat-card .stat-label { font-size: 0.78rem; color: var(--bench-gray); margin-top: 4px; }
90
 
91
- /* Classification badges */
92
  .badge-HIGH-TNI { background:#dbeafe; color:#1e40af; padding:2px 8px; border-radius:var(--badge-radius); font-size:0.78rem; font-weight:600; }
93
  .badge-TEAM-HELPS { background:#dcfce7; color:#166534; padding:2px 8px; border-radius:var(--badge-radius); font-size:0.78rem; font-weight:600; }
94
  .badge-NEUTRAL { background:#f3f4f6; color:#374151; padding:2px 8px; border-radius:var(--badge-radius); font-size:0.78rem; font-weight:600; }
95
  .badge-TEAM-HURTS { background:#fee2e2; color:#991b1b; padding:2px 8px; border-radius:var(--badge-radius); font-size:0.78rem; font-weight:600; }
96
 
97
- /* Section headings */
98
  .section-title {
99
  font-size: 1.05rem; font-weight: 600; margin: 20px 0 10px;
100
  padding-bottom: 6px; border-bottom: 2px solid var(--border-color-primary, #e5e7eb);
101
  }
102
 
103
- /* About cards */
104
  .about-card {
105
  background: var(--background-fill-primary, #fff);
106
  border: 1px solid var(--border-color-primary, #e5e7eb);
@@ -110,12 +102,10 @@ CUSTOM_CSS = """
110
  .about-card p, .about-card li { font-size: 0.9rem; line-height: 1.65; }
111
  .about-card ul { padding-left: 20px; margin: 8px 0; }
112
 
113
- /* Role pills */
114
  .role-planner { background:#ede9fe; color:#5b21b6; padding:3px 10px; border-radius:20px; font-size:0.82rem; font-weight:600; }
115
  .role-executor { background:#fef3c7; color:#92400e; padding:3px 10px; border-radius:20px; font-size:0.82rem; font-weight:600; }
116
  .role-verifier { background:#d1fae5; color:#065f46; padding:3px 10px; border-radius:20px; font-size:0.82rem; font-weight:600; }
117
 
118
- /* Submit section */
119
  .submit-info {
120
  background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 10px;
121
  padding: 18px 22px; margin-bottom: 16px; font-size: 0.9rem; line-height: 1.7;
@@ -123,8 +113,13 @@ CUSTOM_CSS = """
123
  .submit-info code {
124
  background: #dbeafe; padding: 2px 6px; border-radius: 4px; font-size: 0.82rem;
125
  }
 
 
 
 
 
 
126
 
127
- /* Tables */
128
  .gr-dataframe table { width: 100%; font-size: 0.875rem; }
129
  .gr-dataframe th { font-weight: 600; }
130
  """
@@ -306,83 +301,172 @@ def build_category_summary_df(track: str = "Full") -> pd.DataFrame:
306
 
307
 
308
  # ---------------------------------------------------------------------------
309
- # Submission handler
310
  # ---------------------------------------------------------------------------
311
 
312
- EXPECTED_TASKS = {t["task_id"] for t in _load()["per_task"]} if False else set()
313
 
314
 
315
- def _get_expected_tasks() -> set[str]:
316
  return {t["task_id"] for t in _load()["per_task"]}
317
 
318
 
319
- def validate_submission(file, model_name: str, team_name: str, framework: str,
320
- description: str) -> str:
 
 
 
 
 
321
  if not file:
322
- return "Please upload a results JSON file."
323
  if not model_name or not model_name.strip():
324
  return "Please provide a model name."
 
 
325
 
326
- try:
327
- with open(file.name) as f:
328
- data = json.load(f)
329
- except (json.JSONDecodeError, Exception) as e:
330
- return f"Invalid JSON: {e}"
331
-
332
- # Validate structure
333
- if not isinstance(data, dict):
334
- return "JSON root must be an object with 'model', 'results' keys."
335
-
336
- results = data.get("results")
337
- if not isinstance(results, list):
338
- return "Missing 'results' array. See format specification below."
339
-
340
- expected = _get_expected_tasks()
341
- submitted_tasks = set()
342
- errors = []
343
-
344
- for i, r in enumerate(results):
345
- if not isinstance(r, dict):
346
- errors.append(f"results[{i}]: not an object")
347
- continue
348
- tid = r.get("task_id", "")
349
- if not tid:
350
- errors.append(f"results[{i}]: missing task_id")
351
- continue
352
- submitted_tasks.add(tid)
353
 
354
- for cond in ["oracle", "restricted", "full", "team_no_plan", "team_no_verify"]:
355
- val = r.get(cond)
356
- if val is not None and not isinstance(val, (int, float)):
357
- errors.append(f"results[{i}] ({tid}): {cond} must be a number")
358
 
359
- if errors:
360
- return "Validation errors:\n" + "\n".join(errors[:20])
 
361
 
362
- missing = expected - submitted_tasks
363
- extra = submitted_tasks - expected
 
364
 
365
- # Save submission
366
- safe_name = "".join(c if c.isalnum() or c in "-_" else "_" for c in model_name.strip())
367
- out_path = SUBMISSIONS_DIR / f"{safe_name}.json"
368
- submission = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  "model": model_name.strip(),
370
- "team": team_name.strip() if team_name else "",
371
  "framework": framework.strip() if framework else "",
 
372
  "description": description.strip() if description else "",
373
- "task_count": len(submitted_tasks),
374
- "data": data,
 
 
 
375
  }
376
- with open(out_path, "w") as f:
377
- json.dump(submission, f, indent=2)
378
 
379
- parts = [f"Submission received for **{model_name.strip()}** ({len(submitted_tasks)} tasks)."]
380
- if missing:
381
- parts.append(f"\n{len(missing)} tasks missing (partial submission OK).")
382
- if extra:
383
- parts.append(f"\n{len(extra)} unrecognized task IDs (ignored).")
384
- parts.append("\nThank you! Your submission will be reviewed and added to the leaderboard.")
385
- return "\n".join(parts)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
 
387
 
388
  # ---------------------------------------------------------------------------
@@ -594,47 +678,71 @@ def build_app() -> gr.Blocks:
594
  with gr.Tab("Submit"):
595
  gr.HTML("""
596
  <div class="submit-info">
597
- <h3 style="margin:0 0 10px;">Submit Your Results</h3>
598
- <p>Run the TeamBench evaluation locally and upload your results to appear on the leaderboard.</p>
599
-
600
- <strong>Step 1: Install &amp; Run</strong>
601
- <pre style="background:#dbeafe; padding:10px 14px; border-radius:6px; margin:8px 0; font-size:0.82rem; overflow-x:auto;">pip install teambench
602
- teambench run --model your-model-name --output results.json</pre>
603
-
604
- <strong>Step 2: Upload</strong>
605
- <p>Upload the generated <code>results.json</code> file below.</p>
606
-
607
- <strong>Expected JSON format:</strong>
608
- <pre style="background:#dbeafe; padding:10px 14px; border-radius:6px; margin:8px 0; font-size:0.82rem; overflow-x:auto;">{
609
- "model": "your-model-name",
610
- "framework": "langgraph",
611
- "results": [
612
- {
613
- "task_id": "S1_hidden_spec",
614
- "oracle": 1.0,
615
- "restricted": 0.0,
616
- "full": 0.75,
617
- "team_no_plan": 0.5,
618
- "team_no_verify": 0.75
619
- },
620
- ...
621
- ]
622
- }</pre>
623
 
624
- <p>All 5 conditions (<code>oracle</code>, <code>restricted</code>, <code>full</code>,
625
- <code>team_no_plan</code>, <code>team_no_verify</code>) are expected per task.
626
- Partial submissions (subset of 155 tasks) are accepted.</p>
 
 
 
627
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
  """)
629
 
630
  with gr.Row():
631
  model_input = gr.Textbox(
632
- label="Model Name",
633
  placeholder="e.g. gpt-5, claude-opus-4, gemini-3-pro",
634
  scale=2,
635
  )
636
  team_input = gr.Textbox(
637
- label="Team / Organization",
638
  placeholder="e.g. OpenAI, Google DeepMind, your-lab-name",
639
  scale=2,
640
  )
@@ -649,24 +757,33 @@ teambench run --model your-model-name --output results.json</pre>
649
  placeholder="your@email.com",
650
  scale=2,
651
  )
652
-
653
- description_input = gr.Textbox(
654
- label="Description (optional)",
655
- placeholder="Brief description of your agent setup, tools used, etc.",
656
- lines=2,
657
- )
 
 
 
 
 
 
 
658
 
659
  file_input = gr.File(
660
- label="Upload results.json",
661
- file_types=[".json"],
662
  )
663
 
664
- submit_btn = gr.Button("Submit Results", variant="primary")
665
  result_output = gr.Markdown("")
666
 
667
  submit_btn.click(
668
- fn=validate_submission,
669
- inputs=[file_input, model_input, team_input, framework_input, description_input],
 
 
670
  outputs=result_output,
671
  )
672
 
 
9
 
10
  import json
11
  import os
12
+ import zipfile
13
+ import tempfile
14
  from pathlib import Path
15
 
16
  import gradio as gr
 
48
  "gpt-5-nano": "GPT-5 Nano",
49
  }
50
 
 
 
 
 
 
51
  # ---------------------------------------------------------------------------
52
  # CSS
53
  # ---------------------------------------------------------------------------
 
59
  --badge-radius: 4px;
60
  }
61
 
 
62
  .tb-header {
63
  background: linear-gradient(135deg, #1e3a8a 0%, #1d4ed8 50%, #2563eb 100%);
64
  border-radius: 12px; padding: 28px 32px 24px; margin-bottom: 8px;
 
71
  margin: 0; font-size: 1rem; color: #ffffff !important; opacity: 0.92;
72
  }
73
 
 
74
  .stat-grid {
75
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
76
  gap: 12px; margin-bottom: 16px;
 
83
  .stat-card .stat-value { font-size: 1.5rem; font-weight: 700; color: var(--bench-blue); line-height: 1; }
84
  .stat-card .stat-label { font-size: 0.78rem; color: var(--bench-gray); margin-top: 4px; }
85
 
 
86
  .badge-HIGH-TNI { background:#dbeafe; color:#1e40af; padding:2px 8px; border-radius:var(--badge-radius); font-size:0.78rem; font-weight:600; }
87
  .badge-TEAM-HELPS { background:#dcfce7; color:#166534; padding:2px 8px; border-radius:var(--badge-radius); font-size:0.78rem; font-weight:600; }
88
  .badge-NEUTRAL { background:#f3f4f6; color:#374151; padding:2px 8px; border-radius:var(--badge-radius); font-size:0.78rem; font-weight:600; }
89
  .badge-TEAM-HURTS { background:#fee2e2; color:#991b1b; padding:2px 8px; border-radius:var(--badge-radius); font-size:0.78rem; font-weight:600; }
90
 
 
91
  .section-title {
92
  font-size: 1.05rem; font-weight: 600; margin: 20px 0 10px;
93
  padding-bottom: 6px; border-bottom: 2px solid var(--border-color-primary, #e5e7eb);
94
  }
95
 
 
96
  .about-card {
97
  background: var(--background-fill-primary, #fff);
98
  border: 1px solid var(--border-color-primary, #e5e7eb);
 
102
  .about-card p, .about-card li { font-size: 0.9rem; line-height: 1.65; }
103
  .about-card ul { padding-left: 20px; margin: 8px 0; }
104
 
 
105
  .role-planner { background:#ede9fe; color:#5b21b6; padding:3px 10px; border-radius:20px; font-size:0.82rem; font-weight:600; }
106
  .role-executor { background:#fef3c7; color:#92400e; padding:3px 10px; border-radius:20px; font-size:0.82rem; font-weight:600; }
107
  .role-verifier { background:#d1fae5; color:#065f46; padding:3px 10px; border-radius:20px; font-size:0.82rem; font-weight:600; }
108
 
 
109
  .submit-info {
110
  background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 10px;
111
  padding: 18px 22px; margin-bottom: 16px; font-size: 0.9rem; line-height: 1.7;
 
113
  .submit-info code {
114
  background: #dbeafe; padding: 2px 6px; border-radius: 4px; font-size: 0.82rem;
115
  }
116
+ .submit-step {
117
+ background: var(--background-fill-primary, #fff);
118
+ border: 1px solid var(--border-color-primary, #e5e7eb);
119
+ border-radius: 10px; padding: 16px 20px; margin-bottom: 12px;
120
+ }
121
+ .submit-step h4 { margin: 0 0 8px; font-size: 0.95rem; }
122
 
 
123
  .gr-dataframe table { width: 100%; font-size: 0.875rem; }
124
  .gr-dataframe th { font-weight: 600; }
125
  """
 
301
 
302
 
303
  # ---------------------------------------------------------------------------
304
+ # Submission handler — workspace artifact zip (server-graded)
305
  # ---------------------------------------------------------------------------
306
 
307
+ MAX_UPLOAD_MB = 100
308
 
309
 
310
+ def _get_valid_task_ids() -> set[str]:
311
  return {t["task_id"] for t in _load()["per_task"]}
312
 
313
 
314
+ def validate_and_accept_submission(
315
+ file, model_name: str, team_name: str, framework: str,
316
+ contact: str, description: str, seed: str,
317
+ ) -> str:
318
+ """Validate a workspace artifact zip and queue it for server-side grading."""
319
+
320
+ # --- basic field validation ---
321
  if not file:
322
+ return "Please upload a workspace artifact zip file."
323
  if not model_name or not model_name.strip():
324
  return "Please provide a model name."
325
+ if not team_name or not team_name.strip():
326
+ return "Please provide a team or organization name."
327
 
328
+ file_path = file.name if hasattr(file, "name") else str(file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
 
330
+ # --- file size check ---
331
+ file_size_mb = os.path.getsize(file_path) / (1024 * 1024)
332
+ if file_size_mb > MAX_UPLOAD_MB:
333
+ return f"File too large ({file_size_mb:.1f} MB). Maximum is {MAX_UPLOAD_MB} MB."
334
 
335
+ # --- zip validation ---
336
+ if not zipfile.is_zipfile(file_path):
337
+ return "Uploaded file is not a valid zip archive."
338
 
339
+ valid_tasks = _get_valid_task_ids()
340
+ found_tasks: dict[str, list[str]] = {} # task_id -> list of files
341
+ has_meta = False
342
 
343
+ try:
344
+ with zipfile.ZipFile(file_path, "r") as zf:
345
+ names = zf.namelist()
346
+
347
+ # Security: reject paths with .. or absolute paths
348
+ for name in names:
349
+ if ".." in name or name.startswith("/"):
350
+ return f"Rejected: unsafe path in zip: `{name}`"
351
+
352
+ # Check for meta.json
353
+ for name in names:
354
+ if name.endswith("meta.json") and name.count("/") <= 1:
355
+ has_meta = True
356
+
357
+ # Find workspace directories. Accept multiple layouts:
358
+ # tasks/{TASK_ID}/workspace/...
359
+ # tasks/{TASK_ID}/{condition}/workspace/...
360
+ # {TASK_ID}/workspace/...
361
+ # {TASK_ID}/{condition}/workspace/...
362
+ valid_upper = {t.upper(): t for t in valid_tasks}
363
+
364
+ for name in names:
365
+ parts = name.split("/")
366
+ if "workspace" not in parts:
367
+ continue
368
+ ws_idx = parts.index("workspace")
369
+ # Try task_id at ws_idx-1 or ws_idx-2
370
+ for offset in [1, 2]:
371
+ candidate_idx = ws_idx - offset
372
+ if candidate_idx < 0:
373
+ continue
374
+ candidate = parts[candidate_idx]
375
+ matched = (
376
+ candidate if candidate in valid_tasks
377
+ else valid_upper.get(candidate.upper())
378
+ )
379
+ if matched:
380
+ if matched not in found_tasks:
381
+ found_tasks[matched] = []
382
+ found_tasks[matched].append(name)
383
+ break
384
+
385
+ except zipfile.BadZipFile:
386
+ return "Corrupted zip file. Please re-create and upload again."
387
+
388
+ if not found_tasks:
389
+ return (
390
+ "No valid task workspaces found in the zip.\n\n"
391
+ "Expected structure:\n"
392
+ "```\n"
393
+ "submission.zip/\n"
394
+ " meta.json\n"
395
+ " tasks/\n"
396
+ " S1_hidden_spec/\n"
397
+ " workspace/\n"
398
+ " <files your agent produced>\n"
399
+ " D1_schema_drift/\n"
400
+ " workspace/\n"
401
+ " ...\n"
402
+ "```"
403
+ )
404
+
405
+ # --- save submission ---
406
+ safe_model = "".join(c if c.isalnum() or c in "-_" else "_" for c in model_name.strip())
407
+ safe_team = "".join(c if c.isalnum() or c in "-_" else "_" for c in team_name.strip())
408
+ submission_id = f"{safe_team}__{safe_model}"
409
+
410
+ # Save metadata
411
+ meta = {
412
+ "submission_id": submission_id,
413
  "model": model_name.strip(),
414
+ "team": team_name.strip(),
415
  "framework": framework.strip() if framework else "",
416
+ "contact": contact.strip() if contact else "",
417
  "description": description.strip() if description else "",
418
+ "seed": int(seed) if seed and seed.isdigit() else 0,
419
+ "tasks_submitted": sorted(found_tasks.keys()),
420
+ "task_count": len(found_tasks),
421
+ "file_count": sum(len(v) for v in found_tasks.values()),
422
+ "status": "pending_grading",
423
  }
 
 
424
 
425
+ meta_path = SUBMISSIONS_DIR / f"{submission_id}.json"
426
+ with open(meta_path, "w") as f:
427
+ json.dump(meta, f, indent=2)
428
+
429
+ # Save the zip itself
430
+ import shutil
431
+ zip_path = SUBMISSIONS_DIR / f"{submission_id}.zip"
432
+ shutil.copy2(file_path, zip_path)
433
+
434
+ # --- build response ---
435
+ missing = valid_tasks - set(found_tasks.keys())
436
+ conditions = ["oracle", "restricted", "full", "team_no_plan", "team_no_verify"]
437
+
438
+ response_parts = [
439
+ f"### Submission Received",
440
+ f"",
441
+ f"**Model**: {model_name.strip()}",
442
+ f"**Team**: {team_name.strip()}",
443
+ f"**Tasks**: {len(found_tasks)} / {len(valid_tasks)}",
444
+ f"**Files**: {sum(len(v) for v in found_tasks.values())} workspace artifacts",
445
+ f"**Seed**: {meta['seed']}",
446
+ ]
447
+
448
+ if framework and framework.strip():
449
+ response_parts.append(f"**Framework**: {framework.strip()}")
450
+
451
+ response_parts.append("")
452
+
453
+ if len(found_tasks) < len(valid_tasks):
454
+ response_parts.append(
455
+ f"*{len(missing)} tasks not included (partial submission accepted).*"
456
+ )
457
+
458
+ response_parts += [
459
+ "",
460
+ "**Status: Queued for server-side grading.**",
461
+ "",
462
+ "Your workspace artifacts will be graded against our hidden test suites. "
463
+ "Scores are computed server-side — not self-reported — to ensure benchmark integrity.",
464
+ "",
465
+ "Results will appear on the leaderboard once grading completes (typically within 24-48 hours). "
466
+ "You will be contacted at the provided email if there are any issues.",
467
+ ]
468
+
469
+ return "\n".join(response_parts)
470
 
471
 
472
  # ---------------------------------------------------------------------------
 
678
  with gr.Tab("Submit"):
679
  gr.HTML("""
680
  <div class="submit-info">
681
+ <h3 style="margin:0 0 12px;">Submit Your Results</h3>
682
+ <p>TeamBench uses <strong>server-side grading</strong> to ensure benchmark integrity.
683
+ You submit workspace artifacts (the files your agent produced) &mdash;
684
+ not self-reported scores. We grade them against hidden test suites.</p>
685
+ </div>
686
+ """)
687
+
688
+ gr.HTML("""
689
+ <div class="submit-step">
690
+ <h4>Step 1: Install and Run</h4>
691
+ <pre style="background:#f3f4f6; padding:10px 14px; border-radius:6px; font-size:0.82rem; overflow-x:auto; margin:8px 0;">pip install teambench
692
+ teambench run --model your-model --seed 0 --output submission/</pre>
693
+ <p style="font-size:0.85rem; color:#6b7280; margin:4px 0 0;">
694
+ This runs your agent on all tasks and saves the workspace artifacts (files your agent wrote).
695
+ You must run all 5 conditions: <code>oracle</code>, <code>restricted</code>, <code>full</code>,
696
+ <code>team_no_plan</code>, <code>team_no_verify</code>.
697
+ </p>
698
+ </div>
699
+
700
+ <div class="submit-step">
701
+ <h4>Step 2: Package</h4>
702
+ <pre style="background:#f3f4f6; padding:10px 14px; border-radius:6px; font-size:0.82rem; overflow-x:auto; margin:8px 0;">teambench package submission/ --output submission.zip</pre>
703
+ <p style="font-size:0.85rem; color:#6b7280; margin:4px 0 0;">
704
+ Creates a zip with the required structure. Partial submissions (subset of 155 tasks) are accepted.
705
+ </p>
706
+ </div>
707
 
708
+ <div class="submit-step">
709
+ <h4>Step 3: Upload</h4>
710
+ <p style="font-size:0.85rem; color:#6b7280;">
711
+ Fill in the fields below and upload your <code>submission.zip</code>.
712
+ Results appear on the leaderboard after server-side grading (24-48 hours).
713
+ </p>
714
  </div>
715
+ """)
716
+
717
+ gr.HTML("""
718
+ <details style="margin-bottom:16px;">
719
+ <summary style="cursor:pointer; font-weight:600; font-size:0.9rem; padding:8px 0;">
720
+ Expected zip structure
721
+ </summary>
722
+ <pre style="background:#f3f4f6; padding:12px 16px; border-radius:6px; font-size:0.82rem; margin-top:8px;">submission.zip/
723
+ meta.json # auto-generated by teambench package
724
+ tasks/
725
+ S1_hidden_spec/
726
+ oracle/workspace/ # workspace after oracle condition
727
+ restricted/workspace/ # workspace after restricted condition
728
+ full/workspace/ # workspace after full team condition
729
+ team_no_plan/workspace/ # workspace after no-plan condition
730
+ team_no_verify/workspace/ # workspace after no-verify condition
731
+ D1_schema_drift/
732
+ oracle/workspace/
733
+ ...
734
+ ...</pre>
735
+ </details>
736
  """)
737
 
738
  with gr.Row():
739
  model_input = gr.Textbox(
740
+ label="Model Name *",
741
  placeholder="e.g. gpt-5, claude-opus-4, gemini-3-pro",
742
  scale=2,
743
  )
744
  team_input = gr.Textbox(
745
+ label="Team / Organization *",
746
  placeholder="e.g. OpenAI, Google DeepMind, your-lab-name",
747
  scale=2,
748
  )
 
757
  placeholder="your@email.com",
758
  scale=2,
759
  )
760
+ with gr.Row():
761
+ description_input = gr.Textbox(
762
+ label="Description (optional)",
763
+ placeholder="Brief description of your agent setup, tools used, etc.",
764
+ lines=2,
765
+ scale=3,
766
+ )
767
+ seed_input = gr.Textbox(
768
+ label="Seed",
769
+ value="0",
770
+ placeholder="0",
771
+ scale=1,
772
+ )
773
 
774
  file_input = gr.File(
775
+ label="Upload submission.zip (workspace artifacts, max 100 MB)",
776
+ file_types=[".zip"],
777
  )
778
 
779
+ submit_btn = gr.Button("Submit for Grading", variant="primary")
780
  result_output = gr.Markdown("")
781
 
782
  submit_btn.click(
783
+ fn=validate_and_accept_submission,
784
+ inputs=[file_input, model_input, team_input,
785
+ framework_input, contact_input,
786
+ description_input, seed_input],
787
  outputs=result_output,
788
  )
789