uuuhjb commited on
Commit
bc450de
·
1 Parent(s): af63b1d

update submission

Browse files
Files changed (4) hide show
  1. app.py +204 -47
  2. content.py +5 -4
  3. data/model.jsonl +36 -8
  4. submission.py +426 -252
app.py CHANGED
@@ -536,6 +536,144 @@ def create_capability_subplots(data_dict, title="Capability Performance", top_n=
536
  return fig
537
 
538
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  def create_summary_table(capability_dict, domain_dict, verified_dict, type_name="Agent"):
540
  """
541
  Create summary table showing rank, average accuracy and F1 scores.
@@ -675,19 +813,38 @@ def build_app():
675
  if not any(len(category_data) > 0 for category_data in model_domain_filtered.values()):
676
  model_domain_filtered = {}
677
 
678
- with gr.Blocks(css=CSS, title="AMA-Bench Leaderboard", theme=gr.themes.Soft()) as demo:
 
 
 
 
 
 
 
 
 
 
 
679
 
680
  # Header
681
- gr.HTML("""
682
- <div style="text-align: center; padding: 10px 20px; margin-bottom: 20px;">
683
- <h1 style="margin: 0; font-size: 48px; font-weight: 700; color: #1a1a2e;">
684
- 🤖 AMA-Bench: Leaderboard
685
- </h1>
686
- <p style="font-size: 18px; color: #666; margin-top: 10px;">
687
- Agent Memory Assessment Benchmark - Performance Visualization
688
- </p>
 
 
 
 
 
 
 
689
  </div>
690
- """)
 
691
 
692
  # Welcome Banner
693
  gr.HTML("""
@@ -749,13 +906,11 @@ def build_app():
749
 
750
  with gr.Accordion("📊 Summary Statistics", open=True):
751
  gr.Markdown("""
752
- **Verification Status:** Icons next to agent names indicate verification status
753
- • **✓** = Officially verified by LLM-as-Judge
754
- • **○** = User-submitted, pending official verification
755
  """)
756
  agent_domain_table = gr.Dataframe(
757
- value=create_summary_table(AGENT_CAPABILITY, AGENT_DOMAIN, AGENT_VERIFIED, "Agent"),
758
- label="Average Domain Scores"
759
  )
760
 
761
  # Update chart when slider changes
@@ -796,13 +951,11 @@ def build_app():
796
 
797
  with gr.Accordion("📊 Summary Statistics", open=True):
798
  gr.Markdown("""
799
- **Verification Status:** Icons next to agent names indicate verification status
800
- • **✓** = Officially verified by LLM-as-Judge
801
- • **○** = User-submitted, pending official verification
802
  """)
803
  agent_capability_table = gr.Dataframe(
804
- value=create_summary_table(AGENT_CAPABILITY, AGENT_DOMAIN, AGENT_VERIFIED, "Agent"),
805
- label="Average Capability Scores"
806
  )
807
 
808
  # Update chart when slider changes
@@ -853,13 +1006,11 @@ def build_app():
853
 
854
  with gr.Accordion("📊 Summary Statistics", open=True):
855
  gr.Markdown("""
856
- **Verification Status:** Icons next to model names indicate verification status
857
- • **✓** = Officially verified by LLM-as-Judge
858
- • **○** = User-submitted, pending official verification
859
  """)
860
  model_domain_table = gr.Dataframe(
861
- value=create_summary_table(MODEL_CAPABILITY, model_domain_filtered, MODEL_VERIFIED, "Model"),
862
- label="Average Domain Scores"
863
  )
864
 
865
  # Update chart when slider changes
@@ -900,13 +1051,11 @@ def build_app():
900
 
901
  with gr.Accordion("📊 Summary Statistics", open=True):
902
  gr.Markdown("""
903
- **Verification Status:** Icons next to model names indicate verification status
904
- • **✓** = Officially verified by LLM-as-Judge
905
- • **○** = User-submitted, pending official verification
906
  """)
907
  model_capability_table = gr.Dataframe(
908
- value=create_summary_table(MODEL_CAPABILITY, MODEL_DOMAIN, MODEL_VERIFIED, "Model"),
909
- label="Average Capability Scores"
910
  )
911
 
912
  # Update chart when slider changes
@@ -971,48 +1120,56 @@ def build_app():
971
  )
972
 
973
  gr.Markdown("""
974
- **📋 New Submission Format:**
975
 
976
- Your JSONL file should contain one submission per episode with the following fields:
977
 
978
  ```json
979
  {
980
  "episode_id": "trajectory_id",
981
- "answer_list": ["xxxx", "xxxx", "xxx"],
 
982
  "llm_as_judge_score_list": [true, false, true]
983
  }
984
  ```
985
 
986
  **Field Descriptions:**
987
- - `episode_id` (required): The episode identifier
988
- - `answer_list` (required): List of your model/agent's answers (one per question in the episode)
989
- - `llm_as_judge_score_list` (optional): List of boolean values (true/false) indicating correctness
990
- - If provided, these are your self-evaluated scores
991
- - If not provided, scoring will be done during weekly official evaluation
992
- - Official scores (`verified=true`) are computed by our LLM-as-Judge system
993
 
994
  **Important Notes:**
995
- - `answer_list` and `llm_as_judge_score_list` must have the same length
996
- - All submissions start as `verified=false` and become `verified=true` after official evaluation
 
997
  """)
998
 
999
  with gr.Row():
1000
  submit_button = gr.Button("Submit", variant="primary", size="lg")
1001
 
1002
- submission_result = gr.Markdown()
1003
 
1004
  submit_button.click(
1005
- add_new_submission,
1006
- [
 
 
 
 
1007
  model_name_textbox,
1008
  submission_type,
1009
  url_textbox,
1010
  file_upload,
1011
  organisation,
1012
  mail,
1013
- model_family_textbox
1014
  ],
1015
- submission_result,
 
 
 
 
1016
  )
1017
 
1018
  # ============================================================
@@ -1078,4 +1235,4 @@ Results are reported as **Accuracy** and **F1 Score**:
1078
 
1079
  if __name__ == "__main__":
1080
  demo_app = build_app()
1081
- demo_app.launch(debug=True, show_error=True)
 
536
  return fig
537
 
538
 
539
+ def _rank_prefix(i):
540
+ medals = ["🥇", "🥈", "🥉"]
541
+ return f"{medals[i]} {i+1}" if i < 3 else str(i + 1)
542
+
543
+
544
+ def _fmt(v):
545
+ return f"{v * 100:.2f}%"
546
+
547
+
548
+ def _build_rows_sorted(items, verified_dict, score_fn, type_name):
549
+ """
550
+ Build rows for verified entries only (verified=True).
551
+ Unverified submissions are excluded from the leaderboard display.
552
+ """
553
+ rows = []
554
+ for item in sorted(items):
555
+ if not verified_dict.get(item, False):
556
+ continue
557
+ row = score_fn(item, True, type_name)
558
+ rows.append(row)
559
+
560
+ rows.sort(key=lambda r: r["_sort"], reverse=True)
561
+ for i, r in enumerate(rows):
562
+ r["Rank"] = _rank_prefix(i)
563
+
564
+ return rows
565
+
566
+
567
+ def create_capability_table(capability_dict, domain_dict, verified_dict, type_name="Agent"):
568
+ """
569
+ Summary table grouped by capability (A/B/C/D).
570
+ verified=True → ranked by official score
571
+ verified=False → appended unranked, scores marked with * (self-reported)
572
+ """
573
+ items = set()
574
+ for d in domain_dict.values():
575
+ items.update(d.keys())
576
+ if not items:
577
+ return pd.DataFrame()
578
+
579
+ cap_cols = {
580
+ "Recall": "Recall (A)",
581
+ "Causal Inference": "Causal Inf. (B)",
582
+ "State Updating": "State Upd. (C)",
583
+ "State Abstraction": "State Abs. (D)",
584
+ }
585
+ cap_weights = {}
586
+ if QA_DISTRIBUTION:
587
+ pt = QA_DISTRIBUTION.get("overall_distribution", {}).get("problem_types", {})
588
+ letter_to_cap = {"A": "Recall", "B": "Causal Inference",
589
+ "C": "State Updating", "D": "State Abstraction"}
590
+ for letter, info in pt.items():
591
+ cap_weights[letter_to_cap.get(letter, "")] = info.get("ratio", 0.0)
592
+
593
+ def score_fn(item, is_verified, type_name):
594
+ model_family = ""
595
+ for cd in capability_dict.values():
596
+ if item in cd and isinstance(cd[item], dict):
597
+ model_family = cd[item].get("model_family", "")
598
+ if model_family:
599
+ break
600
+
601
+ cap_scores = {}
602
+ for cap_name in cap_cols:
603
+ d = capability_dict.get(cap_name, {}).get(item, {})
604
+ cap_scores[cap_name] = d.get("accuracy", 0.0) if isinstance(d, dict) else 0.0
605
+
606
+ w_sum = sum(cap_scores[c] * cap_weights.get(c, 0.0) for c in cap_cols)
607
+ w_tot = sum(cap_weights.get(c, 0.0) for c in cap_cols)
608
+ avg = w_sum / w_tot if w_tot > 0 else sum(cap_scores.values()) / len(cap_scores)
609
+
610
+ row = {
611
+ type_name: f"{item} {'✓' if is_verified else '○'}",
612
+ "Model Family": model_family,
613
+ "Avg Score": _fmt(avg),
614
+ "_sort": avg,
615
+ }
616
+ for cap_name, col_label in cap_cols.items():
617
+ row[col_label] = _fmt(cap_scores[cap_name])
618
+ return row
619
+
620
+ rows = _build_rows_sorted(items, verified_dict, score_fn, type_name)
621
+ return pd.DataFrame([
622
+ {"Rank": r["Rank"], **{k: v for k, v in r.items() if k not in ("Rank", "_sort")}}
623
+ for r in rows
624
+ ])
625
+
626
+
627
+ def create_domain_table(capability_dict, domain_dict, verified_dict, type_name="Agent"):
628
+ """
629
+ Summary table grouped by domain.
630
+ verified=True → ranked by official score
631
+ verified=False → appended unranked, scores marked with * (self-reported)
632
+ """
633
+ items = set()
634
+ for d in domain_dict.values():
635
+ items.update(d.keys())
636
+ if not items:
637
+ return pd.DataFrame()
638
+
639
+ domain_order = ["TEXT2SQL", "SOFTWARE", "WEB", "GAME", "EMBODIED_AI", "OPENWORLD_QA"]
640
+ domain_weights = {}
641
+ if QA_DISTRIBUTION:
642
+ for dom, info in QA_DISTRIBUTION.get("domain_distribution", {}).items():
643
+ domain_weights[dom] = info.get("qa_ratio", 0.0)
644
+
645
+ def score_fn(item, is_verified, type_name):
646
+ model_family = ""
647
+ for cd in capability_dict.values():
648
+ if item in cd and isinstance(cd[item], dict):
649
+ model_family = cd[item].get("model_family", "")
650
+ if model_family:
651
+ break
652
+
653
+ dom_scores = {}
654
+ for dom in domain_order:
655
+ d = domain_dict.get(dom, {}).get(item, {})
656
+ dom_scores[dom] = d.get("accuracy", 0.0) if isinstance(d, dict) else 0.0
657
+
658
+ w_sum = sum(dom_scores[d] * domain_weights.get(d, 0.0) for d in domain_order)
659
+ w_tot = sum(domain_weights.get(d, 0.0) for d in domain_order)
660
+ avg = w_sum / w_tot if w_tot > 0 else sum(dom_scores.values()) / len(dom_scores)
661
+
662
+ row = {
663
+ type_name: f"{item} {'✓' if is_verified else '○'}",
664
+ "Model Family": model_family,
665
+ "Avg Score": _fmt(avg),
666
+ "_sort": avg,
667
+ }
668
+ for dom in domain_order:
669
+ row[dom] = _fmt(dom_scores[dom])
670
+ return row
671
+
672
+ rows = _build_rows_sorted(items, verified_dict, score_fn, type_name)
673
+ return pd.DataFrame([{"Rank": r["Rank"], **{k: v for k, v in r.items() if k != "Rank" and k != "_sort"}}
674
+ for r in rows])
675
+
676
+
677
  def create_summary_table(capability_dict, domain_dict, verified_dict, type_name="Agent"):
678
  """
679
  Create summary table showing rank, average accuracy and F1 scores.
 
813
  if not any(len(category_data) > 0 for category_data in model_domain_filtered.values()):
814
  model_domain_filtered = {}
815
 
816
+ import base64, pathlib
817
+ _logo_path = pathlib.Path("assets/ama_logo.jpg")
818
+ if _logo_path.exists():
819
+ _logo_b64 = base64.b64encode(_logo_path.read_bytes()).decode()
820
+ _logo_tag = (
821
+ '<img src="data:image/jpeg;base64,' + _logo_b64 + '"'
822
+ ' alt="AMA-Bench" style="height:80px;object-fit:contain;flex-shrink:0;">'
823
+ )
824
+ else:
825
+ _logo_tag = "🤖 "
826
+
827
+ with gr.Blocks(title="AMA-Bench Leaderboard") as demo:
828
 
829
  # Header
830
+ gr.HTML(
831
+ """
832
+ <div style="display:flex; align-items:center; justify-content:center;
833
+ gap:24px; padding:20px 20px 10px; margin-bottom:20px;">
834
+ """
835
+ + _logo_tag
836
+ + """
837
+ <div style="text-align:left;">
838
+ <h1 style="margin:0; font-size:48px; font-weight:700; color:#1a1a2e; line-height:1.1;">
839
+ AMA-Bench: Leaderboard
840
+ </h1>
841
+ <p style="font-size:18px; color:#666; margin:8px 0 0;">
842
+ Agent Memory Assessment Benchmark - Performance Visualization
843
+ </p>
844
+ </div>
845
  </div>
846
+ """
847
+ )
848
 
849
  # Welcome Banner
850
  gr.HTML("""
 
906
 
907
  with gr.Accordion("📊 Summary Statistics", open=True):
908
  gr.Markdown("""
909
+ **Verification Status:** Only officially verified entries (✓) are shown. User-submitted results (○) will appear after weekly LLM-as-Judge evaluation.
 
 
910
  """)
911
  agent_domain_table = gr.Dataframe(
912
+ value=create_domain_table(AGENT_CAPABILITY, AGENT_DOMAIN, AGENT_VERIFIED, "Agent"),
913
+ label="Scores by Domain"
914
  )
915
 
916
  # Update chart when slider changes
 
951
 
952
  with gr.Accordion("📊 Summary Statistics", open=True):
953
  gr.Markdown("""
954
+ **Verification Status:** Only officially verified entries (✓) are shown. User-submitted results (○) will appear after weekly LLM-as-Judge evaluation.
 
 
955
  """)
956
  agent_capability_table = gr.Dataframe(
957
+ value=create_capability_table(AGENT_CAPABILITY, AGENT_DOMAIN, AGENT_VERIFIED, "Agent"),
958
+ label="Scores by Capability"
959
  )
960
 
961
  # Update chart when slider changes
 
1006
 
1007
  with gr.Accordion("📊 Summary Statistics", open=True):
1008
  gr.Markdown("""
1009
+ **Verification Status:** Only officially verified entries (✓) are shown. User-submitted results (○) will appear after weekly LLM-as-Judge evaluation.
 
 
1010
  """)
1011
  model_domain_table = gr.Dataframe(
1012
+ value=create_domain_table(MODEL_CAPABILITY, model_domain_filtered, MODEL_VERIFIED, "Model"),
1013
+ label="Scores by Domain"
1014
  )
1015
 
1016
  # Update chart when slider changes
 
1051
 
1052
  with gr.Accordion("📊 Summary Statistics", open=True):
1053
  gr.Markdown("""
1054
+ **Verification Status:** Only officially verified entries (✓) are shown. User-submitted results (○) will appear after weekly LLM-as-Judge evaluation.
 
 
1055
  """)
1056
  model_capability_table = gr.Dataframe(
1057
+ value=create_capability_table(MODEL_CAPABILITY, MODEL_DOMAIN, MODEL_VERIFIED, "Model"),
1058
+ label="Scores by Capability"
1059
  )
1060
 
1061
  # Update chart when slider changes
 
1120
  )
1121
 
1122
  gr.Markdown("""
1123
+ **📋 Submission Format:**
1124
 
1125
+ Your JSONL file should contain one line per episode:
1126
 
1127
  ```json
1128
  {
1129
  "episode_id": "trajectory_id",
1130
+ "question_uuid_list": ["uuid-1", "uuid-2", "uuid-3"],
1131
+ "answer_list": ["The agent moved right.", "..."],
1132
  "llm_as_judge_score_list": [true, false, true]
1133
  }
1134
  ```
1135
 
1136
  **Field Descriptions:**
1137
+ - `episode_id` *(required)*: The episode identifier — used to automatically look up the domain
1138
+ - `question_uuid_list` *(required)*: UUIDs of the benchmark questions in the same order as `answer_list` — used to look up each question's capability (A/B/C/D).
1139
+ - `answer_list` *(required)*: Your model/agent's answers, one per question
1140
+ - `llm_as_judge_score_list` *(required)*: `true`/`false` per answer — your self-evaluated correctness scores used for leaderboard ranking.
 
 
1141
 
1142
  **Important Notes:**
1143
+ - `question_uuid_list`, `answer_list`, and `llm_as_judge_score_list` must all be the same length
1144
+ - Domain is resolved automatically from `episode_id`; capability (A/B/C/D) is resolved from `question_uuid_list` no need to supply them manually
1145
+ - All submissions start as `verified=false` and become `verified=true` after official LLM-as-Judge evaluation
1146
  """)
1147
 
1148
  with gr.Row():
1149
  submit_button = gr.Button("Submit", variant="primary", size="lg")
1150
 
1151
+ submission_result = gr.HTML()
1152
 
1153
  submit_button.click(
1154
+ fn=lambda: gr.update(interactive=False, value="⏳ Submitting..."),
1155
+ inputs=[],
1156
+ outputs=[submit_button],
1157
+ ).then(
1158
+ fn=add_new_submission,
1159
+ inputs=[
1160
  model_name_textbox,
1161
  submission_type,
1162
  url_textbox,
1163
  file_upload,
1164
  organisation,
1165
  mail,
1166
+ model_family_textbox,
1167
  ],
1168
+ outputs=[submission_result],
1169
+ ).then(
1170
+ fn=lambda: gr.update(interactive=True, value="Submit"),
1171
+ inputs=[],
1172
+ outputs=[submit_button],
1173
  )
1174
 
1175
  # ============================================================
 
1235
 
1236
  if __name__ == "__main__":
1237
  demo_app = build_app()
1238
+ demo_app.launch(debug=True, show_error=True, theme=gr.themes.Soft())
content.py CHANGED
@@ -16,12 +16,14 @@ Results can be submitted for evaluation. Each submission should contain answers
16
 
17
  We expect submissions to be JSON Lines files with the following format:
18
  ```
19
- {"episode_id": "traj_id_1", "answer_list": ["(A)", "(B)(C)", "(D)"], "reasoning_trace": "optional"}
20
  ```
21
 
22
  **Required fields:**
23
  - `episode_id`: The episode identifier
24
- - `answer_list`: Your model's answer list for the questions in the episode (a list of strings, e.g., ["(A)", "(B)(C)", "(D)"])
 
 
25
  - `reasoning_trace`: (Optional) The reasoning process or explanation for the answers
26
  """
27
 
@@ -52,5 +54,4 @@ def model_hyperlink(link, model_name):
52
  """Create a hyperlink to the model information."""
53
  if not link or link.strip() == "":
54
  return model_name
55
- return f'<a target="_blank" href="{link}" style="color: var(--link-text-color); text-decoration: underline;text-decoration-style: dotted;">{model_name}</a>'
56
-
 
16
 
17
  We expect submissions to be JSON Lines files with the following format:
18
  ```
19
+ {"episode_id": "trajectory_id", "question_uuid_list": ["uuid-1", "uuid-2", "uuid-3"], "answer_list": ["The agent moved right.", "..."], "llm_as_judge_score_list": [true, false, true]}
20
  ```
21
 
22
  **Required fields:**
23
  - `episode_id`: The episode identifier
24
+ - `question_uuid_list`: List of question UUIDs corresponding to each answer (e.g., `["uuid-1", "uuid-2"]`)
25
+ - `answer_list`: Your model's answers, in the same order as `question_uuid_list`
26
+ - `llm_as_judge_score_list`: Boolean scores for each answer (e.g., `[true, false, true]`)
27
  - `reasoning_trace`: (Optional) The reasoning process or explanation for the answers
28
  """
29
 
 
54
  """Create a hyperlink to the model information."""
55
  if not link or link.strip() == "":
56
  return model_name
57
+ return f'<a target="_blank" href="{link}" style="color: var(--link-text-color); text-decoration: underline;text-decoration-style: dotted;">{model_name}</a>'
 
data/model.jsonl CHANGED
@@ -1,8 +1,36 @@
1
- {"model": "Claude Haiku 3.5", "Date": "2026-03-03", "verified": true, "Score": {"GAME": [{"A": 0.5}, {"B": 0.458}, {"C": 0.564}, {"D": 0.583}], "EMBODIED_AI": [{"A": 0.3934}, {"B": 0.4667}, {"C": 0.34}, {"D": 0.0}], "WEB": [{"A": 0.4711}, {"B": 0.5889}, {"C": 0.5222}, {"D": 0.5932}], "TEXT2SQL": [{"A": 0.6233}, {"B": 0.1961}, {"C": 0.4328}, {"D": 0.1569}], "OPENWORLD_QA": [{"A": 0.6596}, {"B": 0.7333}, {"C": 0.5625}, {"D": 0.5}], "SOFTWARE": [{"A": 0.26}, {"B": 0.4366}, {"C": 0.1739}, {"D": 0.1324}]}}
2
- {"model": "GPT-5 mini", "Date": "2026-03-03", "verified": true, "Score": {"GAME": [{"A": 0.5}, {"B": 0.514}, {"C": 0.4872}, {"D": 0.5}], "EMBODIED_AI": [{"A": 0.6557}, {"B": 0.5667}, {"C": 0.4133}, {"D": 0.0169}], "WEB": [{"A": 0.8}, {"B": 0.8925}, {"C": 0.8387}, {"D": 0.7869}], "TEXT2SQL": [{"A": 0.8924}, {"B": 0.7778}, {"C": 0.8731}, {"D": 0.7941}], "OPENWORLD_QA": [{"A": 0.7347}, {"B": 0.8105}, {"C": 0.757}, {"D": 0.85}], "SOFTWARE": [{"A": 0.4811}, {"B": 0.6933}, {"C": 0.4521}, {"D": 0.6667}]}}
3
- {"model": "gpt 5.2", "Date": "2026-03-03", "verified": true, "Score": {"GAME": [{"A": 0.8362}, {"B": 0.8194}, {"C": 0.7564}, {"D": 0.8333}], "EMBODIED_AI": [{"A": 0.9508}, {"B": 0.8}, {"C": 0.5067}, {"D": 0.0}], "WEB": [{"A": 0.744}, {"B": 0.8925}, {"C": 0.7204}, {"D": 0.7705}], "TEXT2SQL": [{"A": 0.9058}, {"B": 0.8627}, {"C": 0.8806}, {"D": 0.6765}], "OPENWORLD_QA": [{"A": 0.6939}, {"B": 0.7158}, {"C": 0.5794}, {"D": 0.65}], "SOFTWARE": [{"A": 0.4623}, {"B": 0.7067}, {"C": 0.3699}, {"D": 0.625}]}}
4
- {"model": "Gemini 2.5 flash", "Date": "2026-03-03", "verified": true, "Score": {"GAME": [{"A": 0.4224}, {"B": 0.1667}, {"C": 0.1795}, {"D": 0.1333}], "EMBODIED_AI": [{"A": 0.541}, {"B": 0.6333}, {"C": 0.4}, {"D": 0.0169}], "WEB": [{"A": 0.664}, {"B": 0.6237}, {"C": 0.6344}, {"D": 0.4918}], "TEXT2SQL": [{"A": 0.688}, {"B": 0.6344}, {"C": 0.6559}, {"D": 0.5246}], "OPENWORLD_QA": [{"A": 0.77}, {"B": 0.619}, {"C": 0.721}, {"D": 0.758}], "SOFTWARE": [{"A": 0.3726}, {"B": 0.32}, {"C": 0.3288}, {"D": 0.5139}]}}
5
- {"model": "Qwen2.5-14B-Instruct-1M", "Date": "2026-03-03", "verified": true, "Score": {"GAME": [{"A": 0.4957}, {"B": 0.4444}, {"C": 0.5769}, {"D": 0.4833}], "EMBODIED_AI": [{"A": 0.6842}, {"B": 0.4211}, {"C": 0.2211}, {"D": 0.0}], "WEB": [{"A": 0.488}, {"B": 0.6882}, {"C": 0.5376}, {"D": 0.4918}], "TEXT2SQL": [{"A": 0.6319}, {"B": 0.2339}, {"C": 0.5526}, {"D": 0.119}], "OPENWORLD_QA": [{"A": 0.48}, {"B": 0.4091}, {"C": 0.6087}, {"D": 0.5}], "SOFTWARE": [{"A": 0.5189}, {"B": 0.3867}, {"C": 0.3014}, {"D": 0.5417}]}}
6
- {"model": "Qwen3-32B", "Date": "2026-03-03", "verified": true, "Score": {"GAME": [{"A": 0.5431}, {"B": 0.4861}, {"C": 0.5128}, {"D": 0.6}], "EMBODIED_AI": [{"A": 0.7966}, {"B": 0.6437}, {"C": 0.4345}, {"D": 0.0526}], "WEB": [{"A": 0.504}, {"B": 0.6667}, {"C": 0.5054}, {"D": 0.541}], "TEXT2SQL": [{"A": 0.7309}, {"B": 0.3203}, {"C": 0.5672}, {"D": 0.2059}], "OPENWORLD_QA": [{"A": 0.4894}, {"B": 0.5978}, {"C": 0.4904}, {"D": 0.4138}], "SOFTWARE": [{"A": 0.5581}, {"B": 0.5254}, {"C": 0.3898}, {"D": 0.4655}]}}
7
- {"model": "Qwen3-14B", "Date": "2026-03-03", "verified": true, "Score": {"GAME": [{"A": 0.5162}, {"B": 0.5}, {"C": 0.5556}, {"D": 0.6}], "EMBODIED_AI": [{"A": 0.6557}, {"B": 0.5}, {"C": 0.3133}, {"D": 0.0508}], "WEB": [{"A": 0.512}, {"B": 0.5591}, {"C": 0.5484}, {"D": 0.5246}], "TEXT2SQL": [{"A": 0.6502}, {"B": 0.2288}, {"C": 0.4851}, {"D": 0.1471}], "OPENWORLD_QA": [{"A": 0.5306}, {"B": 0.5895}, {"C": 0.4393}, {"D": 0.3667}], "SOFTWARE": [{"A": 0.4953}, {"B": 0.4267}, {"C": 0.3425}, {"D": 0.3194}]}}
8
- {"model": "Qwen3-8B", "Date": "2026-03-03", "verified": true, "Score": {"GAME": [{"A": 0.5294}, {"B": 0.4111}, {"C": 0.4333}, {"D": 0.5}], "EMBODIED_AI": [{"A": 0.541}, {"B": 0.3333}, {"C": 0.2867}, {"D": 0.0169}], "WEB": [{"A": 0.424}, {"B": 0.4839}, {"C": 0.4409}, {"D": 0.3934}], "TEXT2SQL": [{"A": 0.5605}, {"B": 0.1895}, {"C": 0.4478}, {"D": 0.1471}], "OPENWORLD_QA": [{"A": 0.4796}, {"B": 0.5263}, {"C": 0.4579}, {"D": 0.4333}], "SOFTWARE": [{"A": 0.4481}, {"B": 0.44}, {"C": 0.3014}, {"D": 0.3472}]}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"model": "Claude Haiku 3.5", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5}, {"B": 0.458}, {"C": 0.564}, {"D": 0.583}], "EMBODIED_AI": [{"A": 0.3934}, {"B": 0.4667}, {"C": 0.34}, {"D": 0.0}], "WEB": [{"A": 0.4711}, {"B": 0.5889}, {"C": 0.5222}, {"D": 0.5932}], "TEXT2SQL": [{"A": 0.6233}, {"B": 0.1961}, {"C": 0.4328}, {"D": 0.1569}], "OPENWORLD_QA": [{"A": 0.6596}, {"B": 0.7333}, {"C": 0.5625}, {"D": 0.5}], "SOFTWARE": [{"A": 0.26}, {"B": 0.4366}, {"C": 0.1739}, {"D": 0.1324}]}}
2
+ {"model": "OpenAI GPT-5.1 mini", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5}, {"B": 0.514}, {"C": 0.4872}, {"D": 0.5}], "EMBODIED_AI": [{"A": 0.6557}, {"B": 0.5667}, {"C": 0.4133}, {"D": 0.0169}], "WEB": [{"A": 0.8}, {"B": 0.8925}, {"C": 0.8387}, {"D": 0.7869}], "TEXT2SQL": [{"A": 0.8924}, {"B": 0.7778}, {"C": 0.8731}, {"D": 0.7941}], "OPENWORLD_QA": [{"A": 0.7347}, {"B": 0.8105}, {"C": 0.757}, {"D": 0.85}], "SOFTWARE": [{"A": 0.4811}, {"B": 0.6933}, {"C": 0.4521}, {"D": 0.6667}]}}
3
+ {"model": "gpt 5.2", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.8362}, {"B": 0.8194}, {"C": 0.7564}, {"D": 0.8333}], "EMBODIED_AI": [{"A": 0.9508}, {"B": 0.8}, {"C": 0.5067}, {"D": 0.0}], "WEB": [{"A": 0.744}, {"B": 0.8925}, {"C": 0.7204}, {"D": 0.7705}], "TEXT2SQL": [{"A": 0.9058}, {"B": 0.8627}, {"C": 0.8806}, {"D": 0.6765}], "OPENWORLD_QA": [{"A": 0.6939}, {"B": 0.7158}, {"C": 0.5794}, {"D": 0.65}], "SOFTWARE": [{"A": 0.4623}, {"B": 0.7067}, {"C": 0.3699}, {"D": 0.625}]}}
4
+ {"model": "Gemini 2.5 flash", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4224}, {"B": 0.1667}, {"C": 0.1795}, {"D": 0.1333}], "EMBODIED_AI": [{"A": 0.541}, {"B": 0.6333}, {"C": 0.4}, {"D": 0.0169}], "WEB": [{"A": 0.664}, {"B": 0.6237}, {"C": 0.6344}, {"D": 0.4918}], "TEXT2SQL": [{"A": 0.688}, {"B": 0.6344}, {"C": 0.6559}, {"D": 0.5246}], "OPENWORLD_QA": [{"A": 0.77}, {"B": 0.619}, {"C": 0.721}, {"D": 0.758}], "SOFTWARE": [{"A": 0.3726}, {"B": 0.32}, {"C": 0.3288}, {"D": 0.5139}]}}
5
+ {"model": "Qwen2.5-14B-Instruct-1M", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4957}, {"B": 0.4444}, {"C": 0.5769}, {"D": 0.4833}], "EMBODIED_AI": [{"A": 0.6842}, {"B": 0.4211}, {"C": 0.2211}, {"D": 0.0}], "WEB": [{"A": 0.488}, {"B": 0.6882}, {"C": 0.5376}, {"D": 0.4918}], "TEXT2SQL": [{"A": 0.6319}, {"B": 0.2339}, {"C": 0.5526}, {"D": 0.119}], "OPENWORLD_QA": [{"A": 0.48}, {"B": 0.4091}, {"C": 0.6087}, {"D": 0.5}], "SOFTWARE": [{"A": 0.5189}, {"B": 0.3867}, {"C": 0.3014}, {"D": 0.5417}]}}
6
+ {"model": "Qwen3-32B", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5431}, {"B": 0.4861}, {"C": 0.5128}, {"D": 0.6}], "EMBODIED_AI": [{"A": 0.7966}, {"B": 0.6437}, {"C": 0.4345}, {"D": 0.0526}], "WEB": [{"A": 0.504}, {"B": 0.6667}, {"C": 0.5054}, {"D": 0.541}], "TEXT2SQL": [{"A": 0.7309}, {"B": 0.3203}, {"C": 0.5672}, {"D": 0.2059}], "OPENWORLD_QA": [{"A": 0.4894}, {"B": 0.5978}, {"C": 0.4904}, {"D": 0.4138}], "SOFTWARE": [{"A": 0.5581}, {"B": 0.5254}, {"C": 0.3898}, {"D": 0.4655}]}}
7
+ {"model": "Qwen3-14B", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5162}, {"B": 0.5}, {"C": 0.5556}, {"D": 0.6}], "EMBODIED_AI": [{"A": 0.6557}, {"B": 0.5}, {"C": 0.3133}, {"D": 0.0508}], "WEB": [{"A": 0.512}, {"B": 0.5591}, {"C": 0.5484}, {"D": 0.5246}], "TEXT2SQL": [{"A": 0.6502}, {"B": 0.2288}, {"C": 0.4851}, {"D": 0.1471}], "OPENWORLD_QA": [{"A": 0.5306}, {"B": 0.5895}, {"C": 0.4393}, {"D": 0.3667}], "SOFTWARE": [{"A": 0.4953}, {"B": 0.4267}, {"C": 0.3425}, {"D": 0.3194}]}}
8
+ {"model": "Qwen3-8B", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5294}, {"B": 0.4111}, {"C": 0.4333}, {"D": 0.5}], "EMBODIED_AI": [{"A": 0.541}, {"B": 0.3333}, {"C": 0.2867}, {"D": 0.0169}], "WEB": [{"A": 0.424}, {"B": 0.4839}, {"C": 0.4409}, {"D": 0.3934}], "TEXT2SQL": [{"A": 0.5605}, {"B": 0.1895}, {"C": 0.4478}, {"D": 0.1471}], "OPENWORLD_QA": [{"A": 0.4796}, {"B": 0.5263}, {"C": 0.4579}, {"D": 0.4333}], "SOFTWARE": [{"A": 0.4481}, {"B": 0.44}, {"C": 0.3014}, {"D": 0.3472}]}}
9
+ {"model": "BM25 (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.3365}, {"B": 0.3333}, {"C": 0.4638}, {"D": 0.2963}], "EMBODIED_AI": [{"A": 0.2295}, {"B": 0.3111}, {"C": 0.0933}, {"D": 0.0008}], "WEB": [{"A": 0.272}, {"B": 0.3548}, {"C": 0.2366}, {"D": 0.1311}], "TEXT2SQL": [{"A": 0.3857}, {"B": 0.4183}, {"C": 0.3582}, {"D": 0.1078}], "OPENWORLD_QA": [{"A": 0.2347}, {"B": 0.3474}, {"C": 0.3178}, {"D": 0.2167}], "SOFTWARE": [{"A": 0.467}, {"B": 0.76}, {"C": 0.5616}, {"D": 0.7778}]}}
10
+ {"model": "Qwen3-Embedding-4B (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4914}, {"B": 0.5278}, {"C": 0.5769}, {"D": 0.4667}], "EMBODIED_AI": [{"A": 0.4262}, {"B": 0.2444}, {"C": 0.1467}, {"D": 0.0}], "WEB": [{"A": 0.312}, {"B": 0.4194}, {"C": 0.2043}, {"D": 0.2131}], "TEXT2SQL": [{"A": 0.5785}, {"B": 0.5556}, {"C": 0.306}, {"D": 0.2255}], "OPENWORLD_QA": [{"A": 0.4388}, {"B": 0.4632}, {"C": 0.4112}, {"D": 0.2833}], "SOFTWARE": [{"A": 0.5849}, {"B": 0.7067}, {"C": 0.4795}, {"D": 0.625}]}}
11
+ {"model": "GRAPHRAG (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5347}, {"B": 0.5484}, {"C": 0.5588}, {"D": 0.5962}], "EMBODIED_AI": [{"A": 0.0426}, {"B": 0.3913}, {"C": 0.1565}, {"D": 0.0}], "WEB": [{"A": 0.314}, {"B": 0.4778}, {"C": 0.4333}, {"D": 0.4576}], "TEXT2SQL": [{"A": 0.2646}, {"B": 0.2353}, {"C": 0.2687}, {"D": 0.098}], "OPENWORLD_QA": [{"A": 0.3474}, {"B": 0.4348}, {"C": 0.233}, {"D": 0.2586}], "SOFTWARE": [{"A": 0.3585}, {"B": 0.36}, {"C": 0.2603}, {"D": 0.4167}]}}
12
+ {"model": "Hipporag2 (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5137}, {"B": 0.6029}, {"C": 0.6806}, {"D": 0.625}], "EMBODIED_AI": [{"A": 0.3607}, {"B": 0.2111}, {"C": 0.1333}, {"D": 0.0}], "WEB": [{"A": 0.344}, {"B": 0.4731}, {"C": 0.4086}, {"D": 0.2787}], "TEXT2SQL": [{"A": 0.6233}, {"B": 0.5882}, {"C": 0.4627}, {"D": 0.1765}], "OPENWORLD_QA": [{"A": 0.4595}, {"B": 0.5135}, {"C": 0.5122}, {"D": 0.3478}], "SOFTWARE": [{"A": 0.3471}, {"B": 0.5902}, {"C": 0.4237}, {"D": 0.7586}]}}
13
+ {"model": "Memagent (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.3103}, {"B": 0.3056}, {"C": 0.3718}, {"D": 0.2833}], "EMBODIED_AI": [{"A": 0.0656}, {"B": 0.2889}, {"C": 0.0533}, {"D": 0.0169}], "WEB": [{"A": 0.256}, {"B": 0.3118}, {"C": 0.2258}, {"D": 0.2623}], "TEXT2SQL": [{"A": 0.2518}, {"B": 0.2975}, {"C": 0.272}, {"D": 0.1602}], "OPENWORLD_QA": [{"A": 0.1939}, {"B": 0.1895}, {"C": 0.1495}, {"D": 0.1}], "SOFTWARE": [{"A": 0.4292}, {"B": 0.6267}, {"C": 0.6027}, {"D": 0.5}]}}
14
+ {"model": "Mem1 (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.2217}, {"B": 0.24}, {"C": 0.1918}, {"D": 0.25}], "EMBODIED_AI": [{"A": 0.0164}, {"B": 0.1111}, {"C": 0.0067}, {"D": 0.0}], "WEB": [{"A": 0.12}, {"B": 0.1075}, {"C": 0.1828}, {"D": 0.1148}], "TEXT2SQL": [{"A": 0.0762}, {"B": 0.0784}, {"C": 0.0746}, {"D": 0.0294}], "OPENWORLD_QA": [{"A": 0.1224}, {"B": 0.2}, {"C": 0.0935}, {"D": 0.0667}], "SOFTWARE": [{"A": 0.1698}, {"B": 0.16}, {"C": 0.1918}, {"D": 0.2222}]}}
15
+ {"model": "Amem (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.3793}, {"B": 0.4028}, {"C": 0.5}, {"D": 0.4167}], "EMBODIED_AI": [{"A": 0.082}, {"B": 0.4}, {"C": 0.22}, {"D": 0.0339}], "WEB": [{"A": 0.36}, {"B": 0.4624}, {"C": 0.3011}, {"D": 0.4426}], "TEXT2SQL": [{"A": 0.4036}, {"B": 0.3333}, {"C": 0.3134}, {"D": 0.2059}], "OPENWORLD_QA": [{"A": 0.2755}, {"B": 0.3579}, {"C": 0.2243}, {"D": 0.3167}], "SOFTWARE": [{"A": 0.283}, {"B": 0.2667}, {"C": 0.2877}, {"D": 0.3472}]}}
16
+ {"model": "Mem0 (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4259}, {"B": 0.3788}, {"C": 0.473}, {"D": 0.2857}], "EMBODIED_AI": [{"A": 0.0164}, {"B": 0.3444}, {"C": 0.0667}, {"D": 0.0169}], "WEB": [{"A": 0.256}, {"B": 0.3656}, {"C": 0.2473}, {"D": 0.2131}], "TEXT2SQL": [{"A": 0.16}, {"B": 0.1512}, {"C": 0.1139}, {"D": 0.0517}], "OPENWORLD_QA": [{"A": 0.1735}, {"B": 0.1895}, {"C": 0.1682}, {"D": 0.1167}], "SOFTWARE": [{"A": 0.1953}, {"B": 0.234}, {"C": 0.2444}, {"D": 0.2727}]}}
17
+ {"model": "Memorag (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5405}, {"B": 0.6}, {"C": 0.64}, {"D": 0.45}], "EMBODIED_AI": [{"A": 0.1639}, {"B": 0.1111}, {"C": 0.0667}, {"D": 0.0}], "WEB": [{"A": 0.368}, {"B": 0.4839}, {"C": 0.2473}, {"D": 0.3607}], "TEXT2SQL": [{"A": 0.7273}, {"B": 0.7333}, {"C": 0.6154}, {"D": 0.4}], "OPENWORLD_QA": [{"A": 0.4}, {"B": 0.5789}, {"C": 0.3333}, {"D": 0.3333}], "SOFTWARE": [{"A": 0.4495}, {"B": 0.6494}, {"C": 0.5067}, {"D": 0.5946}]}}
18
+ {"model": "Memgpt (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.3534}, {"B": 0.3472}, {"C": 0.4744}, {"D": 0.5667}], "EMBODIED_AI": [{"A": 0.1475}, {"B": 0.2444}, {"C": 0.04}, {"D": 0.0169}], "WEB": [{"A": 0.384}, {"B": 0.4624}, {"C": 0.2688}, {"D": 0.1967}], "TEXT2SQL": [{"A": 0.287}, {"B": 0.3595}, {"C": 0.1418}, {"D": 0.0392}], "OPENWORLD_QA": [{"A": 0.2615}, {"B": 0.5156}, {"C": 0.3099}, {"D": 0.175}], "SOFTWARE": [{"A": 0.5385}, {"B": 0.7333}, {"C": 0.5111}, {"D": 0.6136}]}}
19
+ {"model": "Mem-alpha (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4286}, {"B": 0.4783}, {"C": 0.4868}, {"D": 0.3621}], "EMBODIED_AI": [{"A": 0.0984}, {"B": 0.4222}, {"C": 0.1}, {"D": 0.0}], "WEB": [{"A": 0.3077}, {"B": 0.5172}, {"C": 0.2759}, {"D": 0.3509}], "TEXT2SQL": [{"A": 0.3318}, {"B": 0.3922}, {"C": 0.3806}, {"D": 0.098}], "OPENWORLD_QA": [{"A": 0.2674}, {"B": 0.2927}, {"C": 0.2065}, {"D": 0.1538}], "SOFTWARE": [{"A": 0.2594}, {"B": 0.4133}, {"C": 0.3288}, {"D": 0.3889}]}}
20
+ {"model": "Memorybank (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4359}, {"B": 0.4028}, {"C": 0.5}, {"D": 0.4167}], "EMBODIED_AI": [{"A": 0.1017}, {"B": 0.3908}, {"C": 0.131}, {"D": 0.0175}], "WEB": [{"A": 0.3393}, {"B": 0.5357}, {"C": 0.3571}, {"D": 0.375}], "TEXT2SQL": [{"A": 0.3139}, {"B": 0.3007}, {"C": 0.2612}, {"D": 0.0784}], "OPENWORLD_QA": [{"A": 0.2959}, {"B": 0.4421}, {"C": 0.2897}, {"D": 0.3667}], "SOFTWARE": [{"A": 0.4386}, {"B": 0.4545}, {"C": 0.2857}, {"D": 0.85}]}}
21
+ {"model": "Simple mem (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.2083}, {"B": 0.2353}, {"C": 0.4615}, {"D": 0.25}], "EMBODIED_AI": [{"A": 0.0}, {"B": 0.1149}, {"C": 0.069}, {"D": 0.0}], "WEB": [{"A": 0.156}, {"B": 0.2099}, {"C": 0.0988}, {"D": 0.0943}], "TEXT2SQL": [{"A": 0.354}, {"B": 0.1644}, {"C": 0.1802}, {"D": 0.0717}], "OPENWORLD_QA": [{"A": 0.1224}, {"B": 0.1684}, {"C": 0.1121}, {"D": 0.0833}], "SOFTWARE": [{"A": 0.2538}, {"B": 0.2444}, {"C": 0.1333}, {"D": 0.3409}]}}
22
+ {"model": "AMA-agent (Ours) (32B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.6471}, {"B": 0.7}, {"C": 0.6}, {"D": 0.6167}], "EMBODIED_AI": [{"A": 0.8033}, {"B": 0.5778}, {"C": 0.44}, {"D": 0.2169}], "WEB": [{"A": 0.53}, {"B": 0.68}, {"C": 0.4133}, {"D": 0.4}], "TEXT2SQL": [{"A": 0.7584}, {"B": 0.4615}, {"C": 0.6782}, {"D": 0.4118}], "OPENWORLD_QA": [{"A": 0.4482}, {"B": 0.5558}, {"C": 0.4612}, {"D": 0.39}], "SOFTWARE": [{"A": 0.6049}, {"B": 0.7268}, {"C": 0.4905}, {"D": 0.7778}]}}
23
+ {"model": "BM25 (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.3966}, {"B": 0.4583}, {"C": 0.4615}, {"D": 0.3333}], "EMBODIED_AI": [{"A": 0.3443}, {"B": 0.3222}, {"C": 0.0733}, {"D": 0.0169}], "WEB": [{"A": 0.224}, {"B": 0.4409}, {"C": 0.2688}, {"D": 0.1803}], "TEXT2SQL": [{"A": 0.3498}, {"B": 0.451}, {"C": 0.3731}, {"D": 0.1765}], "OPENWORLD_QA": [{"A": 0.2347}, {"B": 0.2842}, {"C": 0.3364}, {"D": 0.2167}], "SOFTWARE": [{"A": 0.4292}, {"B": 0.6667}, {"C": 0.4247}, {"D": 0.75}]}}
24
+ {"model": "Qwen3-Embedding-4B (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4397}, {"B": 0.3056}, {"C": 0.6026}, {"D": 0.35}], "EMBODIED_AI": [{"A": 0.4262}, {"B": 0.3111}, {"C": 0.1133}, {"D": 0.0}], "WEB": [{"A": 0.392}, {"B": 0.4301}, {"C": 0.2258}, {"D": 0.2459}], "TEXT2SQL": [{"A": 0.5291}, {"B": 0.451}, {"C": 0.3582}, {"D": 0.2059}], "OPENWORLD_QA": [{"A": 0.4184}, {"B": 0.3895}, {"C": 0.3925}, {"D": 0.25}], "SOFTWARE": [{"A": 0.5283}, {"B": 0.68}, {"C": 0.3699}, {"D": 0.5972}]}}
25
+ {"model": "GRAPHRAG (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4818}, {"B": 0.3182}, {"C": 0.4324}, {"D": 0.4464}], "EMBODIED_AI": [{"A": 0.0196}, {"B": 0.28}, {"C": 0.088}, {"D": 0.0}], "WEB": [{"A": 0.304}, {"B": 0.3656}, {"C": 0.3441}, {"D": 0.2787}], "TEXT2SQL": [{"A": 0.1502}, {"B": 0.1156}, {"C": 0.1769}, {"D": 0.0408}], "OPENWORLD_QA": [{"A": 0.2}, {"B": 0.2283}, {"C": 0.1748}, {"D": 0.2241}], "SOFTWARE": [{"A": 0.2784}, {"B": 0.2754}, {"C": 0.209}, {"D": 0.3333}]}}
26
+ {"model": "Hipporag2 (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4528}, {"B": 0.4343}, {"C": 0.4946}, {"D": 0.4429}], "EMBODIED_AI": [{"A": 0.1026}, {"B": 0.2807}, {"C": 0.1053}, {"D": 0.0}], "WEB": [{"A": 0.3191}, {"B": 0.3525}, {"C": 0.3908}, {"D": 0.2573}], "TEXT2SQL": [{"A": 0.4316}, {"B": 0.5217}, {"C": 0.534}, {"D": 0.2759}], "OPENWORLD_QA": [{"A": 0.4316}, {"B": 0.5217}, {"C": 0.534}, {"D": 0.2759}], "SOFTWARE": [{"A": 0.3497}, {"B": 0.5538}, {"C": 0.3871}, {"D": 0.5}]}}
27
+ {"model": "Memagent (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.1638}, {"B": 0.2917}, {"C": 0.3077}, {"D": 0.25}], "EMBODIED_AI": [{"A": 0.0164}, {"B": 0.1889}, {"C": 0.04}, {"D": 0.0169}], "WEB": [{"A": 0.2}, {"B": 0.2903}, {"C": 0.1828}, {"D": 0.1475}], "TEXT2SQL": [{"A": 0.2018}, {"B": 0.2484}, {"C": 0.1343}, {"D": 0.1078}], "OPENWORLD_QA": [{"A": 0.1735}, {"B": 0.1895}, {"C": 0.0935}, {"D": 0.0167}], "SOFTWARE": [{"A": 0.3396}, {"B": 0.5333}, {"C": 0.4795}, {"D": 0.3194}]}}
28
+ {"model": "Mem1 (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.1624}, {"B": 0.2222}, {"C": 0.2692}, {"D": 0.15}], "EMBODIED_AI": [{"A": 0.0328}, {"B": 0.2444}, {"C": 0.0}, {"D": 0.0169}], "WEB": [{"A": 0.24}, {"B": 0.2151}, {"C": 0.1828}, {"D": 0.0656}], "TEXT2SQL": [{"A": 0.1076}, {"B": 0.1242}, {"C": 0.1045}, {"D": 0.0392}], "OPENWORLD_QA": [{"A": 0.1224}, {"B": 0.1895}, {"C": 0.0841}, {"D": 0.15}], "SOFTWARE": [{"A": 0.1934}, {"B": 0.16}, {"C": 0.1233}, {"D": 0.2917}]}}
29
+ {"model": "Amem (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.431}, {"B": 0.5278}, {"C": 0.5641}, {"D": 0.4333}], "EMBODIED_AI": [{"A": 0.082}, {"B": 0.3667}, {"C": 0.1667}, {"D": 0.0847}], "WEB": [{"A": 0.336}, {"B": 0.4946}, {"C": 0.3333}, {"D": 0.3115}], "TEXT2SQL": [{"A": 0.3991}, {"B": 0.4575}, {"C": 0.3134}, {"D": 0.1667}], "OPENWORLD_QA": [{"A": 0.1327}, {"B": 0.3158}, {"C": 0.2056}, {"D": 0.2833}], "SOFTWARE": [{"A": 0.4198}, {"B": 0.5333}, {"C": 0.3973}, {"D": 0.5833}]}}
30
+ {"model": "Mem0 (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5741}, {"B": 0.5152}, {"C": 0.5946}, {"D": 0.5}], "EMBODIED_AI": [{"A": 0.0164}, {"B": 0.3889}, {"C": 0.02}, {"D": 0.0169}], "WEB": [{"A": 0.32}, {"B": 0.4086}, {"C": 0.2688}, {"D": 0.2623}], "TEXT2SQL": [{"A": 0.1928}, {"B": 0.2418}, {"C": 0.1493}, {"D": 0.1765}], "OPENWORLD_QA": [{"A": 0.1837}, {"B": 0.1895}, {"C": 0.1589}, {"D": 0.2167}], "SOFTWARE": [{"A": 0.3984}, {"B": 0.3191}, {"C": 0.2667}, {"D": 0.3864}]}}
31
+ {"model": "Memgpt (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.4538}, {"B": 0.4889}, {"C": 0.5444}, {"D": 0.4833}], "EMBODIED_AI": [{"A": 0.1803}, {"B": 0.1778}, {"C": 0.0333}, {"D": 0.0}], "WEB": [{"A": 0.36}, {"B": 0.4516}, {"C": 0.2903}, {"D": 0.2459}], "TEXT2SQL": [{"A": 0.2063}, {"B": 0.3007}, {"C": 0.1866}, {"D": 0.0196}], "OPENWORLD_QA": [{"A": 0.0918}, {"B": 0.1158}, {"C": 0.0841}, {"D": 0.1}], "SOFTWARE": [{"A": 0.4151}, {"B": 0.5333}, {"C": 0.3562}, {"D": 0.625}]}}
32
+ {"model": "Mem-alpha (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.292}, {"B": 0.3768}, {"C": 0.4211}, {"D": 0.2759}], "EMBODIED_AI": [{"A": 0.0984}, {"B": 0.2}, {"C": 0.0333}, {"D": 0.0}], "WEB": [{"A": 0.1736}, {"B": 0.2889}, {"C": 0.2}, {"D": 0.2881}], "TEXT2SQL": [{"A": 0.2242}, {"B": 0.3922}, {"C": 0.2537}, {"D": 0.0294}], "OPENWORLD_QA": [{"A": 0.2093}, {"B": 0.2439}, {"C": 0.2174}, {"D": 0.1346}], "SOFTWARE": [{"A": 0.217}, {"B": 0.32}, {"C": 0.1781}, {"D": 0.5139}]}}
33
+ {"model": "Memorag (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5}, {"B": 0.5278}, {"C": 0.641}, {"D": 0.55}], "EMBODIED_AI": [{"A": 0.1639}, {"B": 0.1333}, {"C": 0.02}, {"D": 0.0169}], "WEB": [{"A": 0.232}, {"B": 0.3763}, {"C": 0.1935}, {"D": 0.2295}], "TEXT2SQL": [{"A": 0.6637}, {"B": 0.6928}, {"C": 0.5896}, {"D": 0.3039}], "OPENWORLD_QA": [{"A": 0.2449}, {"B": 0.3789}, {"C": 0.3271}, {"D": 0.2333}], "SOFTWARE": [{"A": 0.4481}, {"B": 0.68}, {"C": 0.4384}, {"D": 0.6111}]}}
34
+ {"model": "Memorybank (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.2821}, {"B": 0.3056}, {"C": 0.3974}, {"D": 0.3167}], "EMBODIED_AI": [{"A": 0.0656}, {"B": 0.2889}, {"C": 0.06}, {"D": 0.0169}], "WEB": [{"A": 0.28}, {"B": 0.3871}, {"C": 0.2473}, {"D": 0.4426}], "TEXT2SQL": [{"A": 0.2735}, {"B": 0.2157}, {"C": 0.209}, {"D": 0.1078}], "OPENWORLD_QA": [{"A": 0.2857}, {"B": 0.3895}, {"C": 0.243}, {"D": 0.4167}], "SOFTWARE": [{"A": 0.25}, {"B": 0.3467}, {"C": 0.2192}, {"D": 0.625}]}}
35
+ {"model": "Simple mem (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.3613}, {"B": 0.1556}, {"C": 0.3556}, {"D": 0.3333}], "EMBODIED_AI": [{"A": 0.0328}, {"B": 0.2778}, {"C": 0.08}, {"D": 0.0}], "WEB": [{"A": 0.1538}, {"B": 0.3333}, {"C": 0.1379}, {"D": 0.193}], "TEXT2SQL": [{"A": 0.1302}, {"B": 0.069}, {"C": 0.0846}, {"D": 0.0102}], "OPENWORLD_QA": [{"A": 0.1939}, {"B": 0.3053}, {"C": 0.215}, {"D": 0.3}], "SOFTWARE": [{"A": 0.2028}, {"B": 0.2133}, {"C": 0.1507}, {"D": 0.2361}]}}
36
+ {"model": "AMA-agent (Ours) (8B)", "Date": "2026-03-03", "verified": true, "Score": {"Game": [{"A": 0.5798}, {"B": 0.5}, {"C": 0.4556}, {"D": 0.6167}], "EMBODIED_AI": [{"A": 0.7705}, {"B": 0.4}, {"C": 0.1667}, {"D": 0.0}], "WEB": [{"A": 0.52}, {"B": 0.5269}, {"C": 0.4086}, {"D": 0.3934}], "TEXT2SQL": [{"A": 0.713}, {"B": 0.3529}, {"C": 0.597}, {"D": 0.3137}], "OPENWORLD_QA": [{"A": 0.475}, {"B": 0.4875}, {"C": 0.4556}, {"D": 0.32}], "SOFTWARE": [{"A": 0.5534}, {"B": 0.6164}, {"C": 0.5352}, {"D": 0.6286}]}}
submission.py CHANGED
@@ -1,14 +1,28 @@
1
  """
2
  Submission handling module for AMA-Bench Leaderboard
3
- Manages model/agent submission processing and scoring
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  """
5
 
6
  import json
7
  import os
8
  import datetime
9
  from email.utils import parseaddr
 
 
10
 
11
- # Optional imports with fallbacks
12
  try:
13
  from content import format_error, format_warning, format_log
14
  except ImportError:
@@ -17,85 +31,80 @@ except ImportError:
17
  def format_log(msg): return f"✅ {msg}"
18
 
19
 
20
- def validate_submission_format(data):
21
- """
22
- Validate submission data format.
23
-
24
- Expected format:
25
- {
26
- "episode_id": str,
27
- "answer_list": list,
28
- "llm_as_judge_score_list": list # Optional, list of true/false
29
- }
30
 
31
- Args:
32
- data: Dictionary containing submission data
 
33
 
34
- Returns:
35
- (is_valid, error_msg): Tuple of validation result
36
  """
37
  if not isinstance(data, dict):
38
  return False, "Submission must be a JSON object"
39
 
40
- # Check required fields
41
  if "episode_id" not in data:
42
  return False, "Missing required field: episode_id"
43
-
44
- if "answer_list" not in data:
45
- return False, "Missing required field: answer_list"
46
-
47
- # Validate episode_id
48
  if not isinstance(data["episode_id"], str) or not data["episode_id"].strip():
49
  return False, "episode_id must be a non-empty string"
50
 
51
- # Validate answer_list
52
- if not isinstance(data["answer_list"], list):
53
- return False, "answer_list must be a list"
54
-
55
- if not data["answer_list"]:
56
- return False, "answer_list cannot be empty"
57
-
58
- # Validate llm_as_judge_score_list if present
59
- if "llm_as_judge_score_list" in data:
60
- score_list = data["llm_as_judge_score_list"]
61
- if not isinstance(score_list, list):
62
- return False, "llm_as_judge_score_list must be a list"
63
-
64
- if len(score_list) != len(data["answer_list"]):
65
- return False, "llm_as_judge_score_list length must match answer_list length"
66
-
67
- # Check that all values are boolean
68
- for score in score_list:
69
- if not isinstance(score, bool):
70
- return False, "All values in llm_as_judge_score_list must be true or false"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
  return True, ""
73
 
74
 
75
- def validate_submission_file(file_path):
76
- """
77
- Validate and load submission file.
 
 
 
78
 
79
- Args:
80
- file_path: Path to JSONL submission file
81
 
82
- Returns:
83
- (is_valid, error_msg, submissions): Tuple of validation result and submissions
84
- """
85
  try:
86
- if not os.path.exists(file_path):
87
- return False, "File not found", []
88
-
89
- if not file_path.endswith('.jsonl'):
90
- return False, "File must be in JSONL format (.jsonl)", []
91
-
92
- submissions = []
93
- with open(file_path, 'r', encoding='utf-8') as f:
94
  for line_num, line in enumerate(f, 1):
95
  line = line.strip()
96
  if not line:
97
  continue
98
-
99
  try:
100
  data = json.loads(line)
101
  except json.JSONDecodeError as e:
@@ -105,244 +114,409 @@ def validate_submission_file(file_path):
105
  if not is_valid:
106
  return False, f"Validation error on line {line_num}: {error_msg}", []
107
 
 
 
 
 
108
  submissions.append(data)
109
 
110
  if not submissions:
111
  return False, "File is empty or contains no valid submissions", []
112
-
113
  return True, "", submissions
114
 
115
  except Exception as e:
116
  return False, f"Error reading file: {e}", []
117
 
118
 
119
- def calculate_accuracy(submission, groundtruth_map):
 
 
 
 
 
120
  """
121
- Calculate accuracy for a submission against groundtruth.
122
 
123
- Args:
124
- submission: Submission data dictionary
125
- groundtruth_map: Dictionary mapping episode_id to groundtruth answers
 
 
 
 
 
126
 
127
- Returns:
128
- Dictionary with accuracy metrics by domain and capability
129
  """
130
- episode_id = submission["episode_id"]
131
- answer_list = submission["answer_list"]
132
-
133
- if episode_id not in groundtruth_map:
134
- return None
 
 
 
 
 
 
 
 
 
135
 
136
- groundtruth = groundtruth_map[episode_id]
137
- gt_answers = groundtruth.get("answer_list", [])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- if len(answer_list) != len(gt_answers):
140
- return None
141
 
142
- # Calculate per-question correctness
143
- correct_count = sum(1 for pred, gt in zip(answer_list, gt_answers) if pred == gt)
144
- total_count = len(answer_list)
145
 
146
- # Get domain and capability info from groundtruth
147
- domain = groundtruth.get("domain", "UNKNOWN")
148
- capabilities = groundtruth.get("capabilities", []) # List of A/B/C/D for each question
149
 
150
- # Calculate per-capability accuracy
151
- capability_scores = {"A": [], "B": [], "C": [], "D": []}
152
 
153
- for i, (pred, gt, cap) in enumerate(zip(answer_list, gt_answers, capabilities)):
154
- if cap in capability_scores:
155
- capability_scores[cap].append(1 if pred == gt else 0)
 
 
 
156
 
157
- result = {
158
- "episode_id": episode_id,
159
- "domain": domain,
160
- "total_correct": correct_count,
161
- "total_questions": total_count,
162
- "accuracy": correct_count / total_count if total_count > 0 else 0,
163
- "capability_scores": {}
164
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
- # Calculate accuracy per capability
167
- for cap, scores in capability_scores.items():
168
- if scores:
169
- result["capability_scores"][cap] = {
170
- "correct": sum(scores),
171
- "total": len(scores),
172
- "accuracy": sum(scores) / len(scores)
173
- }
174
-
175
- return result
176
-
177
 
178
- def process_submission(file_path, model_name, submission_type, organisation, verified=False):
179
- """
180
- Process a submission file and save results.
181
 
182
- Args:
183
- file_path: Path to submission JSONL file
184
- model_name: Name of model/agent
185
- submission_type: "model" or "agent"
186
- organisation: Organisation name
187
- verified: Whether this is an official verified submission
188
 
189
- Returns:
190
- Dictionary with submission results
191
- """
192
- # Validate file
193
- is_valid, error_msg, submissions = validate_submission_file(file_path)
194
- if not is_valid:
195
- return {"success": False, "error": error_msg}
196
-
197
- # Create submission directory
198
- submission_dir = f"submissions/{organisation}_{model_name}"
199
- os.makedirs(submission_dir, exist_ok=True)
200
-
201
- timestamp = datetime.datetime.today().strftime('%Y%m%d_%H%M%S')
202
-
203
- # Save submission file
204
- saved_file = f"{submission_dir}/submission_{timestamp}.jsonl"
205
- with open(saved_file, 'w', encoding='utf-8') as f:
206
- for sub in submissions:
207
- f.write(json.dumps(sub, ensure_ascii=False) + "\n")
208
-
209
- # Create metadata
210
- metadata = {
211
- "model": model_name if submission_type == "model" else None,
212
- "agent_name": model_name if submission_type == "agent" else None,
213
- "model_family": "",
214
- "submission_type": submission_type,
215
- "organisation": organisation,
216
- "Date": datetime.datetime.today().strftime('%Y-%m-%d'),
217
- "timestamp": timestamp,
218
- "verified": verified,
219
- "submission_count": len(submissions),
220
- "file_path": saved_file
221
- }
222
 
223
- metadata_file = f"{submission_dir}/metadata_{timestamp}.json"
224
- with open(metadata_file, 'w', encoding='utf-8') as f:
225
- json.dump(metadata, f, indent=2, ensure_ascii=False)
 
 
 
 
 
 
226
 
227
- return {
228
- "success": True,
229
- "submission_dir": submission_dir,
230
- "metadata_file": metadata_file,
231
- "saved_file": saved_file,
232
- "submission_count": len(submissions),
233
- "metadata": metadata
234
- }
235
 
236
 
237
- def add_new_submission(model, submission_type, url, file, organisation, mail, model_family=""):
238
- """
239
- Process and evaluate a new model/agent submission.
240
-
241
- Args:
242
- model: Model/agent name
243
- submission_type: "Model" or "Agent"
244
- url: URL to model/agent information
245
- file: Uploaded file object
246
- organisation: Organisation name
247
- mail: Contact email
248
- model_family: Model family (optional)
249
-
250
- Returns:
251
- String with formatted result message
252
- """
253
  try:
254
- # Validate inputs
255
  if file is None:
256
  return format_warning("Please attach a submission file.")
257
-
258
  _, parsed_mail = parseaddr(mail)
259
  if "@" not in parsed_mail:
260
  return format_warning("Please provide a valid email address.")
261
-
262
  if not model or not submission_type or not organisation:
263
  return format_warning("Please fill in all required fields.")
264
 
265
- print(f"Processing submission from {organisation}/{model}")
266
 
267
- # Validate file format
268
  is_valid, error_msg, submissions = validate_submission_file(file.name)
269
  if not is_valid:
270
  return format_error(error_msg)
271
 
272
- print(f" Validated {len(submissions)} episode submissions")
273
-
274
- # Process submission (without scoring for now - scoring will be done weekly)
275
- result = process_submission(
276
- file.name,
277
- model,
278
- submission_type.lower(),
279
- organisation,
280
- verified=False # User submissions start as unverified
281
- )
282
-
283
- if not result["success"]:
284
- return format_error(result["error"])
285
-
286
- # Format success message
287
- message = f"✅ **Submission received successfully!**\n\n"
288
- message += f"**{'Agent' if submission_type.lower() == 'agent' else 'Model'}:** {model}\n"
289
- message += f"**Organisation:** {organisation}\n"
290
- message += f"**Episodes Submitted:** {result['submission_count']}\n\n"
291
- message += f"**Submission ID:** {result['metadata']['timestamp']}\n\n"
292
- message += "📊 **Important Notes:**\n"
293
- message += "- Submissions are evaluated **once per week** using our LLM-as-Judge system\n"
294
- message += "- You can submit **once per week** per user\n"
295
- message += "- Official scores (verified=true) are computed by our evaluation system\n"
296
- message += "- You can also run your own evaluation if you have the groundtruth\n"
297
- message += "- Results will appear on the leaderboard after official evaluation\n\n"
298
- message += f"Your submission has been saved to: `{result['submission_dir']}`"
299
-
300
- return format_log(message)
301
-
302
- except Exception as e:
303
- import traceback
304
- traceback.print_exc()
305
- return format_error(f"An error occurred during submission: {str(e)}")
306
-
307
-
308
- def update_leaderboard_data(submission_metadata, scores):
309
- """
310
- Update leaderboard JSONL files with new submission results.
311
-
312
- Args:
313
- submission_metadata: Metadata from processed submission
314
- scores: Computed scores by domain and capability
315
-
316
- Returns:
317
- Boolean indicating success
318
- """
319
- try:
320
- submission_type = submission_metadata["submission_type"]
321
-
322
- # Determine which file to update
323
- if submission_type == "agent":
324
- data_file = "data/agent.jsonl"
325
- else:
326
- data_file = "data/model.jsonl"
327
-
328
- # Create new entry
329
- entry = {
330
- "model" if submission_type == "model" else "agent_name": submission_metadata.get("model") or submission_metadata.get("agent_name"),
331
- "model_family": submission_metadata.get("model_family", ""),
332
- "Date": submission_metadata["Date"],
333
- "verified": submission_metadata["verified"],
334
- "Score": scores
335
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
 
337
- # Append to file
338
- with open(data_file, 'a', encoding='utf-8') as f:
339
- f.write(json.dumps(entry, ensure_ascii=False) + "\n")
340
-
341
- print(f"✓ Updated {data_file}")
342
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
  except Exception as e:
345
- print(f"Error updating leaderboard data: {e}")
346
- import traceback
347
- traceback.print_exc()
348
- return False
 
1
  """
2
  Submission handling module for AMA-Bench Leaderboard
3
+
4
+ Submission format:
5
+ {
6
+ "episode_id": str,
7
+ "question_uuid_list": list[str], # required - UUIDs that map answers to groundtruth
8
+ "answer_list": list[str], # required - same length as question_uuid_list
9
+ "llm_as_judge_score_list": list[bool] # optional - same length as answer_list
10
+ }
11
+
12
+ Scoring logic:
13
+ - Uses llm_as_judge_score_list (true/false) from the submission
14
+ - Maps each question to its domain and capability (A/B/C/D) via groundtruth metadata
15
+ - Computes per-domain, per-capability accuracy
16
+ - Writes entry to data/agent.jsonl or data/model.jsonl (verified=False by default)
17
  """
18
 
19
  import json
20
  import os
21
  import datetime
22
  from email.utils import parseaddr
23
+ from collections import defaultdict
24
+ from typing import Dict, List, Tuple, Optional
25
 
 
26
  try:
27
  from content import format_error, format_warning, format_log
28
  except ImportError:
 
31
  def format_log(msg): return f"✅ {msg}"
32
 
33
 
34
+ # ---------------------------------------------------------------------------
35
+ # Validation
36
+ # ---------------------------------------------------------------------------
 
 
 
 
 
 
 
37
 
38
+ def validate_submission_format(data: dict) -> Tuple[bool, str]:
39
+ """
40
+ Validate a single submission record.
41
 
42
+ Required fields: episode_id, question_uuid_list, answer_list
43
+ Optional fields: llm_as_judge_score_list, reasoning_trace
44
  """
45
  if not isinstance(data, dict):
46
  return False, "Submission must be a JSON object"
47
 
48
+ # episode_id
49
  if "episode_id" not in data:
50
  return False, "Missing required field: episode_id"
 
 
 
 
 
51
  if not isinstance(data["episode_id"], str) or not data["episode_id"].strip():
52
  return False, "episode_id must be a non-empty string"
53
 
54
+ # answer_list
55
+ if "answer_list" not in data:
56
+ return False, "Missing required field: answer_list"
57
+ if not isinstance(data["answer_list"], list) or not data["answer_list"]:
58
+ return False, "answer_list must be a non-empty list"
59
+
60
+ # question_uuid_list
61
+ if "question_uuid_list" not in data:
62
+ return False, "Missing required field: question_uuid_list"
63
+ if not isinstance(data["question_uuid_list"], list):
64
+ return False, "question_uuid_list must be a list"
65
+ if len(data["question_uuid_list"]) != len(data["answer_list"]):
66
+ return False, (
67
+ f"question_uuid_list length ({len(data['question_uuid_list'])}) must match "
68
+ f"answer_list length ({len(data['answer_list'])})"
69
+ )
70
+ for i, q in enumerate(data["question_uuid_list"]):
71
+ if not isinstance(q, str) or not q.strip():
72
+ return False, f"question_uuid_list[{i}] must be a non-empty string"
73
+
74
+ # llm_as_judge_score_list (required)
75
+ if "llm_as_judge_score_list" not in data:
76
+ return False, "Missing required field: llm_as_judge_score_list"
77
+ score_list = data["llm_as_judge_score_list"]
78
+ if not isinstance(score_list, list):
79
+ return False, "llm_as_judge_score_list must be a list"
80
+ if len(score_list) != len(data["answer_list"]):
81
+ return False, (
82
+ f"llm_as_judge_score_list length ({len(score_list)}) must match "
83
+ f"answer_list length ({len(data['answer_list'])})"
84
+ )
85
+ for i, score in enumerate(score_list):
86
+ if not isinstance(score, bool):
87
+ return False, f"llm_as_judge_score_list[{i}] must be true or false (boolean)"
88
 
89
  return True, ""
90
 
91
 
92
+ def validate_submission_file(file_path: str) -> Tuple[bool, str, List[dict]]:
93
+ """Validate and load a JSONL submission file."""
94
+ if not os.path.exists(file_path):
95
+ return False, "File not found", []
96
+ if not file_path.endswith(".jsonl"):
97
+ return False, "File must be in JSONL format (.jsonl)", []
98
 
99
+ submissions = []
100
+ seen_ids = set()
101
 
 
 
 
102
  try:
103
+ with open(file_path, "r", encoding="utf-8") as f:
 
 
 
 
 
 
 
104
  for line_num, line in enumerate(f, 1):
105
  line = line.strip()
106
  if not line:
107
  continue
 
108
  try:
109
  data = json.loads(line)
110
  except json.JSONDecodeError as e:
 
114
  if not is_valid:
115
  return False, f"Validation error on line {line_num}: {error_msg}", []
116
 
117
+ episode_id = data["episode_id"]
118
+ if episode_id in seen_ids:
119
+ return False, f"Duplicate episode_id '{episode_id}' on line {line_num}", []
120
+ seen_ids.add(episode_id)
121
  submissions.append(data)
122
 
123
  if not submissions:
124
  return False, "File is empty or contains no valid submissions", []
 
125
  return True, "", submissions
126
 
127
  except Exception as e:
128
  return False, f"Error reading file: {e}", []
129
 
130
 
131
+ # ---------------------------------------------------------------------------
132
+ # Groundtruth loading
133
+ # ---------------------------------------------------------------------------
134
+
135
+ def load_groundtruth_metadata(dataset_name: str = "Pettingllms/AMA-bench",
136
+ token: str = None) -> Dict[str, dict]:
137
  """
138
+ Load groundtruth metadata. Returns a dict with two sub-dicts:
139
 
140
+ {
141
+ "episode_domain": {
142
+ "episode_id": "GAME" | "TEXT2SQL" | ...
143
+ },
144
+ "question_cap": {
145
+ "question_uuid": "A" | "B" | "C" | "D"
146
+ }
147
+ }
148
 
149
+ - episode_domain: episode_id -> domain (used even when question uuid doesn't match)
150
+ - question_cap: question_uuid -> capability letter
151
  """
152
+ episode_domain: Dict[str, str] = {}
153
+ question_cap: Dict[str, str] = {}
154
+
155
+ def _index_rows(rows):
156
+ for row in rows:
157
+ episode_id = str(row.get("episode_id", ""))
158
+ domain = row.get("domain", "UNKNOWN").upper()
159
+ episode_domain[episode_id] = domain
160
+ for qa in row.get("qa_pairs", []):
161
+ question_uuid = qa.get("question_uuid", "").strip()
162
+ if not question_uuid:
163
+ continue
164
+ cap_letter = _normalize_cap(qa.get("type", "A"))
165
+ question_cap[question_uuid] = cap_letter
166
 
167
+ # --- Try HuggingFace ---
168
+ try:
169
+ from datasets import load_dataset, VerificationMode
170
+ dataset = load_dataset(
171
+ dataset_name, split="test", token=token,
172
+ verification_mode=VerificationMode.NO_CHECKS,
173
+ )
174
+ _index_rows(dataset)
175
+ print(f"[groundtruth] Loaded {len(episode_domain)} episodes, "
176
+ f"{len(question_cap)} Q&A entries from HuggingFace (indexed by question_uuid).")
177
+ return {"episode_domain": episode_domain, "question_cap": question_cap}
178
+ except Exception as hf_err:
179
+ print(f"[groundtruth] HuggingFace failed ({hf_err}), trying local fallback…")
180
+
181
+ # --- Local fallback ---
182
+ for local_path in ["test/open_end_qa_set.jsonl", "data/open_end_qa_set.jsonl"]:
183
+ if not os.path.exists(local_path):
184
+ continue
185
+ try:
186
+ rows = []
187
+ with open(local_path, "r", encoding="utf-8") as f:
188
+ for line in f:
189
+ line = line.strip()
190
+ if line:
191
+ rows.append(json.loads(line))
192
+ _index_rows(rows)
193
+ print(f"[groundtruth] Loaded {len(episode_domain)} episodes, "
194
+ f"{len(question_cap)} Q&A entries from {local_path} (indexed by question_uuid).")
195
+ return {"episode_domain": episode_domain, "question_cap": question_cap}
196
+ except Exception as e:
197
+ print(f"[groundtruth] Error reading {local_path}: {e}")
198
+
199
+ print("[groundtruth] WARNING: No groundtruth metadata available.")
200
+ return {"episode_domain": {}, "question_cap": {}}
201
+
202
+
203
+ def _normalize_cap(cap: str) -> str:
204
+ """Normalize capability label to single letter A/B/C/D."""
205
+ mapping = {
206
+ "A": "A", "Recall": "A",
207
+ "B": "B", "Causal Inference": "B", "Causal": "B",
208
+ "C": "C", "State Updating": "C", "State": "C",
209
+ "D": "D", "State Abstraction": "D", "Abstraction": "D",
210
+ }
211
+ return mapping.get(cap.strip(), "A")
212
 
 
 
213
 
214
+ # ---------------------------------------------------------------------------
215
+ # Scoring
216
+ # ---------------------------------------------------------------------------
217
 
218
+ VALID_DOMAINS = {"TEXT2SQL", "SOFTWARE", "WEB", "GAME", "EMBODIED_AI", "OPENWORLD_QA"}
219
+ VALID_CAPS = ["A", "B", "C", "D"]
 
220
 
 
 
221
 
222
+ def compute_scores_from_submissions(
223
+ submissions: List[dict],
224
+ groundtruth_meta: Dict[str, dict],
225
+ ) -> Dict:
226
+ """
227
+ Compute per-domain, per-capability accuracy using llm_as_judge_score_list.
228
 
229
+ Each question is matched to groundtruth via question_uuid.
230
+ Score structure matches agent.jsonl / model.jsonl:
231
+ {
232
+ "TEXT2SQL": [{"A": 0.xx}, {"B": 0.xx}, {"C": 0.xx}, {"D": 0.xx}],
233
+ ...
 
 
234
  }
235
+ """
236
+ # domain -> capability -> [scores]
237
+ domain_cap_scores: Dict[str, Dict[str, List[float]]] = defaultdict(
238
+ lambda: defaultdict(list)
239
+ )
240
+
241
+ scored_questions = 0
242
+ skipped_episodes = 0 # no judge scores
243
+ unmatched_questions = 0 # question cap not found (domain still resolved via episode)
244
+
245
+ # Unpack the two indexes from groundtruth metadata
246
+ episode_domain: Dict[str, str] = groundtruth_meta.get("episode_domain", {})
247
+ question_cap: Dict[str, str] = groundtruth_meta.get("question_cap", {})
248
+
249
+ for sub in submissions:
250
+ episode_id = str(sub["episode_id"])
251
+ question_uuid_list = sub["question_uuid_list"]
252
+ judge_scores = sub.get("llm_as_judge_score_list")
253
+
254
+ # Resolve domain via episode_id
255
+ domain = episode_domain.get(episode_id, "UNKNOWN").upper()
256
+
257
+ for i, question_uuid in enumerate(question_uuid_list):
258
+ if i >= len(judge_scores):
259
+ break
260
+
261
+ # Resolve capability via question_uuid
262
+ cap = question_cap.get(question_uuid.strip())
263
+ if cap is None:
264
+ unmatched_questions += 1
265
+ continue
266
+
267
+ score = 1.0 if judge_scores[i] is True else 0.0
268
+ domain_cap_scores[domain][cap].append(score)
269
+ scored_questions += 1
270
+
271
+ # Build Score dict — always include all 6 known domains
272
+ score_dict: Dict[str, List[dict]] = {}
273
+ for domain in sorted(VALID_DOMAINS | set(domain_cap_scores.keys())):
274
+ cap_data = domain_cap_scores.get(domain, {})
275
+ score_dict[domain] = [
276
+ {cap: round(sum(cap_data[cap]) / len(cap_data[cap]), 4)
277
+ if cap_data.get(cap) else 0.0}
278
+ for cap in VALID_CAPS
279
+ ]
280
+
281
+ # Coverage warning
282
+ coverage_warning = None
283
+ parts = []
284
+ if skipped_episodes:
285
+ parts.append(f"{skipped_episodes} episode(s) had no llm_as_judge_score_list")
286
+ if unmatched_questions:
287
+ parts.append(f"{unmatched_questions} question(s) not matched in groundtruth")
288
+ if parts:
289
+ coverage_warning = "; ".join(parts)
290
 
291
+ return {
292
+ "Score": score_dict,
293
+ "scored_questions": scored_questions,
294
+ "skipped_episodes": skipped_episodes,
295
+ "unmatched_questions": unmatched_questions,
296
+ "coverage_warning": coverage_warning,
297
+ }
 
 
 
 
298
 
 
 
 
299
 
300
+ # ---------------------------------------------------------------------------
301
+ # Leaderboard update
302
+ # ---------------------------------------------------------------------------
 
 
 
303
 
304
+ def update_leaderboard_data(
305
+ model_or_agent_name: str,
306
+ model_family: str,
307
+ submission_type: str,
308
+ organisation: str,
309
+ score_dict: Dict,
310
+ verified: bool = False,
311
+ ) -> bool:
312
+ """Append a scored entry to data/agent.jsonl or data/model.jsonl."""
313
+ try:
314
+ os.makedirs("data", exist_ok=True)
315
+ data_file = "data/agent.jsonl" if submission_type == "agent" else "data/model.jsonl"
316
+ name_key = "agent_name" if submission_type == "agent" else "model"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
 
318
+ entry = {
319
+ name_key: model_or_agent_name,
320
+ "model_family": model_family,
321
+ "Date": datetime.datetime.today().strftime("%Y-%m-%d"),
322
+ "verified": verified,
323
+ "Score": score_dict,
324
+ }
325
+ with open(data_file, "a", encoding="utf-8") as f:
326
+ f.write(json.dumps(entry, ensure_ascii=False) + "\n")
327
 
328
+ print(f"[leaderboard] Appended to {data_file}: {model_or_agent_name}")
329
+ return True
330
+ except Exception as e:
331
+ print(f"[leaderboard] Error: {e}")
332
+ import traceback; traceback.print_exc()
333
+ return False
 
 
334
 
335
 
336
+ # ---------------------------------------------------------------------------
337
+ # Main entry point
338
+ # ---------------------------------------------------------------------------
339
+
340
+ def add_new_submission(
341
+ model: str,
342
+ submission_type: str,
343
+ url: str,
344
+ file,
345
+ organisation: str,
346
+ mail: str,
347
+ model_family: str = "",
348
+ ) -> str:
349
+ """Validate, score, and record a new submission."""
 
 
350
  try:
 
351
  if file is None:
352
  return format_warning("Please attach a submission file.")
 
353
  _, parsed_mail = parseaddr(mail)
354
  if "@" not in parsed_mail:
355
  return format_warning("Please provide a valid email address.")
 
356
  if not model or not submission_type or not organisation:
357
  return format_warning("Please fill in all required fields.")
358
 
359
+ print(f"[submission] Processing {organisation}/{model} ({submission_type})")
360
 
 
361
  is_valid, error_msg, submissions = validate_submission_file(file.name)
362
  if not is_valid:
363
  return format_error(error_msg)
364
 
365
+ print(f"[submission] Validated {len(submissions)} episode submissions")
366
+
367
+ groundtruth_meta = load_groundtruth_metadata()
368
+ score_result = compute_scores_from_submissions(submissions, groundtruth_meta)
369
+ score_dict = score_result["Score"]
370
+
371
+ # Save raw submission
372
+ submission_dir = f"submissions/{organisation}_{model}"
373
+ os.makedirs(submission_dir, exist_ok=True)
374
+ timestamp = datetime.datetime.today().strftime("%Y%m%d_%H%M%S")
375
+ saved_file = f"{submission_dir}/submission_{timestamp}.jsonl"
376
+ with open(saved_file, "w", encoding="utf-8") as f_out:
377
+ for sub in submissions:
378
+ f_out.write(json.dumps(sub, ensure_ascii=False) + "\n")
379
+
380
+ # Save metadata
381
+ metadata = {
382
+ "model" if submission_type.lower() == "model" else "agent_name": model,
383
+ "model_family": model_family,
384
+ "submission_type": submission_type.lower(),
385
+ "organisation": organisation,
386
+ "url": url,
387
+ "mail": parsed_mail,
388
+ "Date": datetime.datetime.today().strftime("%Y-%m-%d"),
389
+ "timestamp": timestamp,
390
+ "verified": False,
391
+ "submission_count": len(submissions),
392
+ "scored_questions": score_result["scored_questions"],
393
+ "skipped_episodes": score_result["skipped_episodes"],
394
+ "unmatched_questions": score_result["unmatched_questions"],
395
+ "file_path": saved_file,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  }
397
+ with open(f"{submission_dir}/metadata_{timestamp}.json", "w", encoding="utf-8") as f_meta:
398
+ json.dump(metadata, f_meta, indent=2, ensure_ascii=False)
399
+
400
+ # Update leaderboard
401
+ updated = update_leaderboard_data(
402
+ model_or_agent_name=model,
403
+ model_family=model_family,
404
+ submission_type=submission_type.lower(),
405
+ organisation=organisation,
406
+ score_dict=score_dict,
407
+ verified=False,
408
+ )
409
+ if not updated:
410
+ return format_error("Submission validated but failed to update leaderboard data.")
411
+
412
+ type_label = "Agent" if submission_type.lower() == "agent" else "Model"
413
+
414
+ # Compute per-domain averages and overall avg
415
+ domain_order = ["TEXT2SQL", "SOFTWARE", "WEB", "GAME", "EMBODIED_AI", "OPENWORLD_QA"]
416
+ domain_avgs = {}
417
+ for dom in domain_order:
418
+ caps = score_dict.get(dom, [])
419
+ vals = [list(c.values())[0] for c in caps if c]
420
+ domain_avgs[dom] = sum(vals) / len(vals) if vals else 0.0
421
+ overall_avg = sum(domain_avgs.values()) / len(domain_avgs) if domain_avgs else 0.0
422
+
423
+ # Build domain rows — all colors explicit to override Gradio dark theme
424
+ TD = 'style="padding:8px 12px;text-align:center;color:#2c3e50;background:#ffffff;"'
425
+ TD_NAME = 'style="padding:8px 12px;font-weight:600;color:#1a1a2e;background:#ffffff;"'
426
+ TD_AVG = 'style="padding:8px 12px;text-align:center;font-weight:700;color:#0e9e7a;background:#ffffff;"'
427
+
428
+ dom_rows_html = ""
429
+ for i, dom in enumerate(domain_order):
430
+ caps = score_dict.get(dom, [])
431
+ cap_cells = "".join(
432
+ f'<td {TD}>{list(c.values())[0]*100:.1f}%</td>'
433
+ for c in caps
434
+ )
435
+ avg = domain_avgs.get(dom, 0.0)
436
+ row_bg = "#f9fbff" if i % 2 == 0 else "#ffffff"
437
+ dom_rows_html += (
438
+ f'<tr style="border-bottom:1px solid #e8eef3;">'
439
+ f'<td style="padding:8px 12px;font-weight:600;color:#1a1a2e;background:{row_bg};">{dom}</td>'
440
+ f'<td style="padding:8px 12px;text-align:center;font-weight:700;color:#0e9e7a;background:{row_bg};">{avg*100:.2f}%</td>'
441
+ + "".join(
442
+ f'<td style="padding:8px 12px;text-align:center;color:#2c3e50;background:{row_bg};">{list(c.values())[0]*100:.1f}%</td>'
443
+ for c in caps
444
+ )
445
+ + '</tr>'
446
+ )
447
+
448
+ warning_html = (
449
+ '<div style="margin-top:12px;padding:10px 14px;background:#fff8e1;'
450
+ 'border-left:4px solid #f0ad4e;border-radius:6px;font-size:13px;color:#7d5a00;">'
451
+ f'&#x26A0;&#xFE0F; {score_result["coverage_warning"]}</div>'
452
+ if score_result["coverage_warning"] else ""
453
+ )
454
 
455
+ result_html = (
456
+ '<div style="border:1px solid #c8e6c9;border-radius:12px;overflow:hidden;'
457
+ 'margin-top:16px;font-family:-apple-system,BlinkMacSystemFont,sans-serif;'
458
+ 'background:#ffffff;color:#1a1a2e;">'
459
+
460
+ # Header
461
+ '<div style="background:linear-gradient(135deg,#1abc9c,#16a085);padding:16px 22px;'
462
+ 'display:flex;align-items:center;gap:12px;">'
463
+ '<span style="font-size:24px;">&#x2705;</span>'
464
+ '<span style="color:#ffffff;font-size:18px;font-weight:700;letter-spacing:0.3px;">'
465
+ 'Submission Received Successfully</span>'
466
+ '</div>'
467
+
468
+ # Meta row
469
+ '<div style="padding:18px 22px;background:#f0faf7;display:flex;flex-wrap:wrap;'
470
+ 'gap:28px;border-bottom:1px solid #d5eee8;">'
471
+ + "".join(
472
+ f'<div><div style="color:#6b8f85;font-size:11px;font-weight:600;'
473
+ f'letter-spacing:0.8px;text-transform:uppercase;">{label}</div>'
474
+ f'<div style="font-weight:700;font-size:15px;color:{color};margin-top:3px;">{value}</div></div>'
475
+ for label, value, color in [
476
+ (type_label, model, "#1a1a2e"),
477
+ ("Organisation", organisation, "#1a1a2e"),
478
+ ("Episodes", str(len(submissions)), "#1a1a2e"),
479
+ ("Questions Scored", str(score_result["scored_questions"]), "#1a1a2e"),
480
+ ("Overall Avg", f"{overall_avg*100:.2f}%", "#0e9e7a"),
481
+ ("Submission ID", timestamp, "#666"),
482
+ ]
483
+ )
484
+ + '</div>'
485
+
486
+ # Score table
487
+ '<div style="padding:18px 22px;background:#ffffff;">'
488
+ '<div style="font-size:13px;font-weight:600;color:#444;margin-bottom:12px;">'
489
+ '&#x1F4CA;&nbsp; Score Preview '
490
+ '<span style="font-weight:400;color:#888;">(self-reported · pending official verification)</span>'
491
+ '</div>'
492
+ '<div style="border-radius:8px;overflow:hidden;border:1px solid #e0eaf0;">'
493
+ '<table style="width:100%;border-collapse:collapse;font-size:13px;">'
494
+ '<thead>'
495
+ '<tr style="background:#e8f4f0;">'
496
+ '<th style="padding:9px 12px;text-align:left;color:#1a1a2e;font-weight:600;">Domain</th>'
497
+ '<th style="padding:9px 12px;text-align:center;color:#1a1a2e;font-weight:600;">Avg</th>'
498
+ '<th style="padding:9px 12px;text-align:center;color:#1a1a2e;font-weight:600;">Recall (A)</th>'
499
+ '<th style="padding:9px 12px;text-align:center;color:#1a1a2e;font-weight:600;">Causal Inf. (B)</th>'
500
+ '<th style="padding:9px 12px;text-align:center;color:#1a1a2e;font-weight:600;">State Upd. (C)</th>'
501
+ '<th style="padding:9px 12px;text-align:center;color:#1a1a2e;font-weight:600;">State Abs. (D)</th>'
502
+ '</tr>'
503
+ '</thead>'
504
+ f'<tbody>{dom_rows_html}</tbody>'
505
+ '</table>'
506
+ '</div>'
507
+ + warning_html +
508
+ '<div style="margin-top:14px;padding:10px 14px;background:#fffbea;border-radius:6px;'
509
+ 'font-size:12px;color:#7d5a00;line-height:1.7;border-left:3px solid #f5c518;">'
510
+ '&#x2139;&#xFE0F;&nbsp; This is a <strong style="color:#5a3e00;">self-reported preview</strong> based on your '
511
+ '<code style="background:#f5e9b8;color:#5a3e00;padding:1px 4px;border-radius:3px;">llm_as_judge_score_list</code>. '
512
+ 'Official scores will be recomputed by LLM-as-Judge — your entry will appear on the leaderboard after weekly verification.'
513
+ '</div>'
514
+ '</div>'
515
+ '</div>'
516
+ '</div>'
517
+ )
518
+ return result_html
519
 
520
  except Exception as e:
521
+ import traceback; traceback.print_exc()
522
+ return format_error(f"An error occurred: {str(e)}")