mathamateur commited on
Commit
a1e9cea
·
1 Parent(s): b14f655

Update board

Browse files
README.md CHANGED
@@ -15,14 +15,25 @@ tags:
15
 
16
  # MERA Reason Leaderboard
17
 
18
- Zip-only leaderboard for **ruAIME**, **T-math**, **Luzitania**, **MMReD**.
19
 
20
  ## Submission flow
21
 
22
- 1. Upload a MERA-style zip on the **Submit** tab.
23
  2. Metrics are extracted from `results_*.json` inside `logs_public`.
24
- 3. Aggregated scores are saved to `results/{model}.json` and reloaded on every page visit.
25
- 4. Tables update immediately — no HF datasets or evaluation queue.
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  ## Configuration
28
 
 
15
 
16
  # MERA Reason Leaderboard
17
 
18
+ Zip-only leaderboard for **ruAIME**, **T-math**, **Luzitania**, **MMReD** (open-reasoning track).
19
 
20
  ## Submission flow
21
 
22
+ 1. Upload a MERA-style zip on the **Submit** tab (`ruaime.json`, `t_math.json`, `luzitania.json`, `mmred.json`, `logs_public/`).
23
  2. Metrics are extracted from `results_*.json` inside `logs_public`.
24
+ 3. Aggregated scores are saved to the results dataset repo and reloaded on every page visit.
25
+ 4. Tables update immediately — no evaluation queue.
26
+
27
+ ## Metrics
28
+
29
+ | Benchmark | Main leaderboard metric |
30
+ |-----------|-------------------------|
31
+ | ruAIME | Exact match |
32
+ | T-math | Exact match |
33
+ | Luzitania | Exact match |
34
+ | MMReD | `em.dc_aggregate` |
35
+
36
+ The MMReD dataset tab also shows group-level `em.dc_aggregate` and per-subtask `exact_match`.
37
 
38
  ## Configuration
39
 
deepseek-reason-8b.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:e8eb5afc2528cc627ca4a49016ae381db1a74c98ee8dcfe53d1177bc93ceb176
3
- size 8450
 
 
 
 
gigachat-reason-xl.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:b3791d8f89c0828ec4736704fdfebcc8989f519c006a85987556ba9044001d78
3
- size 8383
 
 
 
 
glm-5.1.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:e724b19c59a3c4910019363f6af48726722dc69304e1ecdef47a9164fb9df25b
3
- size 21684272
 
 
 
 
glm-reason-9b.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:a8f53568ded55448cf06447365b49d777898ed1d94c7b35464935bd60cfc3836
3
- size 8407
 
 
 
 
qwen-reason-7b.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:26f228894bdca7a255e75caac15bd620896596e52bd505ed3a58fb801f60d7e2
3
- size 8458
 
 
 
 
results/.gitkeep DELETED
@@ -1 +0,0 @@
1
- # Keep directory in git; submission JSON files are stored here.
 
 
results/GLM.json DELETED
@@ -1,26 +0,0 @@
1
- {
2
- "model": "GLM",
3
- "team": "MERA",
4
- "submitted_time": "2026-06-18T13:14:20Z",
5
- "ruaime": {
6
- "dataset_total_score": 0.4165,
7
- "pass@1": 0.433,
8
- "em": 0.4
9
- },
10
- "t_math": {
11
- "dataset_total_score": 0.4645,
12
- "pass@1": 0.478,
13
- "em": 0.451
14
- },
15
- "luzitania": {
16
- "dataset_total_score": 0.598,
17
- "pass@1": 0.612,
18
- "em": 0.584
19
- },
20
- "mmred": {
21
- "dataset_total_score": 0.52,
22
- "pass@1": 0.533,
23
- "em": 0.507
24
- },
25
- "total_score": 0.49975
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
results/Qwen.json DELETED
@@ -1,26 +0,0 @@
1
- {
2
- "model": "Qwen",
3
- "team": "MERA",
4
- "submitted_time": "2026-06-18T13:16:06Z",
5
- "ruaime": {
6
- "dataset_total_score": 0.35,
7
- "pass@1": 0.367,
8
- "em": 0.333
9
- },
10
- "t_math": {
11
- "dataset_total_score": 0.39949999999999997,
12
- "pass@1": 0.412,
13
- "em": 0.387
14
- },
15
- "luzitania": {
16
- "dataset_total_score": 0.511,
17
- "pass@1": 0.524,
18
- "em": 0.498
19
- },
20
- "mmred": {
21
- "dataset_total_score": 0.4385,
22
- "pass@1": 0.456,
23
- "em": 0.421
24
- },
25
- "total_score": 0.42474999999999996
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/about.py CHANGED
@@ -1,4 +1,4 @@
1
- from dataclasses import dataclass, field
2
  from enum import Enum
3
 
4
 
@@ -6,6 +6,7 @@ from enum import Enum
6
  class Metric:
7
  key: str
8
  col_name: str
 
9
 
10
 
11
  @dataclass
@@ -16,6 +17,7 @@ class Task:
16
  description: str
17
  task_type: str
18
  test_size: int
 
19
  primary_metric: str = ""
20
 
21
  def __post_init__(self):
@@ -28,69 +30,125 @@ class Task:
28
  return self.primary_metric
29
 
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  # Select your tasks here
32
  # ---------------------------------------------------
33
  class Tasks(Enum):
34
  ruaime = Task(
35
  benchmark="ruaime",
36
  metrics=[
37
- Metric("pass@1", "pass@1"),
38
- Metric("em", "Exact match"),
 
 
 
39
  ],
40
  col_name="ruAIME",
 
41
  description=(
42
- "Бенчмарк математического рассуждения на русском языке в стиле AIME. "
43
- "Содержит олимпиадные задачи с числовыми ответами, требующие многошагового рассуждения. "
44
- "Ответы проверяются автоматически с помощью библиотеки math_verify."
45
  ),
46
  task_type="Математика, рассуждение",
47
- test_size=30,
 
48
  )
49
  t_math = Task(
50
  benchmark="t_math",
51
  metrics=[
52
- Metric("pass@1", "pass@1"),
53
- Metric("em", "Exact match"),
 
 
 
54
  ],
55
  col_name="T-math",
 
56
  description=(
57
- "Датасет олимпиадных математических задач на русском языке. "
58
- "331 задача из Всероссийской олимпиады школьников и Московской олимпиады "
59
- "(1998–2025). Задачи требуют многошагового рассуждения, ответы — числовые."
60
  ),
61
  task_type="Математика, олимпиадные задачи",
62
  test_size=331,
 
63
  )
64
  luzitania = Task(
65
  benchmark="luzitania",
66
  metrics=[
67
- Metric("pass@1", "pass@1"),
68
- Metric("em", "Exact match"),
 
 
 
69
  ],
70
  col_name="Luzitania",
 
71
  description=(
72
- "Бенчмарк логического и абстрактного рассуждения на русском языке. "
73
- "Оценивает способность моделей решать задачи с множественным выбором "
74
- "и свободным ответом в различных категориях рассуждений."
75
  ),
76
  task_type="Логика, рассуждение",
77
- test_size=500,
 
78
  )
79
  mmred = Task(
80
  benchmark="mmred",
81
- metrics=[
82
- Metric("pass@1", "pass@1"),
83
- Metric("em", "Exact match"),
84
- ],
85
  col_name="MMReD",
 
86
  description=(
87
- "Multi-Modal Reasoning in Dense Context — синтетический бенчмарк для оценки "
88
- "рассуждения на длинном плотном контексте. Модели анализируют последовательности "
89
- "состояний и отвечают на вопросы, требующие агрегации информации по всей последовательности. "
90
- "5 типов вопросов × 3 длины контекста (32, 64, 128) = 750 вопросов."
91
  ),
92
  task_type="Длинный контекст, рассуждение",
93
  test_size=750,
 
94
  )
95
 
96
 
@@ -118,10 +176,10 @@ LLM_BENCHMARKS_TEXT = """
118
 
119
  | Бенчмарк | Тип | Размер | Основная метрика |
120
  |----------|-----|--------|------------------|
121
- | ruAIME | Математика | 30 | pass@1 |
122
- | T-math | Олимпиадная математика | 331 | pass@1 |
123
- | Luzitania | Логика, рассуждение | 500 | pass@1, Exact match |
124
- | MMReD | Длинный контекст | 750 | pass@1, Exact match |
125
  """
126
 
127
  SUBMIT_TEXT = """
@@ -132,13 +190,13 @@ SUBMIT_TEXT = """
132
  - JSON-файлы с предсказаниями для каждого бенчмарка (`ruaime.json`, `t_math.json`, `luzitania.json`, `mmred.json`)
133
  - `logs_public.zip` или папка `logs_public/` с файлами `results_*.json`
134
 
135
- Оценки берутся **только** из `results_*.json` внутри `logs_public`. После загрузки результат сохраняется в локальную папку `results/{model}.json` и подхватывается при перезагрузке страницы.
136
  """
137
 
138
  CITATION_BUTTON_LABEL = "Скопируйте для цитирования"
139
  CITATION_BUTTON_TEXT = r"""
140
  @misc{mera_reason,
141
  title={MERA Reason: Russian Reasoning Evaluation Benchmark},
142
- howpublished={\url{https://huggingface.co/spaces/MERA-Evaluation/leaderboard}},
143
  }
144
  """
 
1
+ from dataclasses import dataclass
2
  from enum import Enum
3
 
4
 
 
6
  class Metric:
7
  key: str
8
  col_name: str
9
+ description: str = ""
10
 
11
 
12
  @dataclass
 
17
  description: str
18
  task_type: str
19
  test_size: int
20
+ dataset_url: str = ""
21
  primary_metric: str = ""
22
 
23
  def __post_init__(self):
 
30
  return self.primary_metric
31
 
32
 
33
+ MMRED_GROUP_DEFS = [
34
+ ("mmred_dc_sa_c", "DC-SA-C", "Кто провёл больше/меньше всего времени в одиночестве?"),
35
+ ("mmred_dc_sr_i", "DC-SR-I", "Сколько шагов X провёл в комнате Y?"),
36
+ ("mmred_dc_cc_i", "DC-CC-I", "Сколько шагов в комнатах существовала толпа (3+ человек)?"),
37
+ ("mmred_dc_ws_r", "DC-WS-R", "В какой комнате X провёл больше/меньше всего времени?"),
38
+ ("mmred_dc_whs_c", "DC-WHS-C", "Кто провёл больше/меньше всего времени в комнате X?"),
39
+ ]
40
+
41
+ MMRED_LENGTHS = [32, 64, 128]
42
+
43
+ MMRED_GROUP_KEYS = {group_key for group_key, _, _ in MMRED_GROUP_DEFS}
44
+
45
+ MMRED_SUBTASK_KEYS = {
46
+ f"{group_key}_{length}"
47
+ for group_key, _, _ in MMRED_GROUP_DEFS
48
+ for length in MMRED_LENGTHS
49
+ }
50
+
51
+
52
+ def _mmred_metrics() -> list[Metric]:
53
+ metrics = [
54
+ Metric(
55
+ "em.dc_aggregate",
56
+ "Overall",
57
+ "Итоговая оценка MMReD: гармоническое среднее пяти взвешенных EM по типам вопросов.",
58
+ ),
59
+ ]
60
+ for group_key, group_label, group_desc in MMRED_GROUP_DEFS:
61
+ metrics.append(
62
+ Metric(
63
+ f"{group_key}::em.dc_aggregate",
64
+ group_label,
65
+ f"{group_desc} Взвешенный EM по длинам 32/64/128.",
66
+ )
67
+ )
68
+ for group_key, group_label, _ in MMRED_GROUP_DEFS:
69
+ for length in MMRED_LENGTHS:
70
+ metrics.append(
71
+ Metric(
72
+ f"{group_key}_{length}::exact_match",
73
+ f"{group_label} × {length}",
74
+ f"Exact Match на подзадаче длины {length}.",
75
+ )
76
+ )
77
+ return metrics
78
+
79
+
80
  # Select your tasks here
81
  # ---------------------------------------------------
82
  class Tasks(Enum):
83
  ruaime = Task(
84
  benchmark="ruaime",
85
  metrics=[
86
+ Metric(
87
+ "exact_match",
88
+ "Exact match",
89
+ "Доля ответов, математически эквивалентных эталону (проверка через math_verify).",
90
+ ),
91
  ],
92
  col_name="ruAIME",
93
+ dataset_url="https://huggingface.co/datasets/MERA-evaluation/ruAIME",
94
  description=(
95
+ "Русскоязычный набор задач AIME (1983–2025): олимпиадная математика с числовыми "
96
+ "ответами, требующая многошагового рассуждения."
 
97
  ),
98
  task_type="Математика, рассуждение",
99
+ test_size=724,
100
+ primary_metric="exact_match",
101
  )
102
  t_math = Task(
103
  benchmark="t_math",
104
  metrics=[
105
+ Metric(
106
+ "exact_match",
107
+ "Exact match",
108
+ "Математическая эквивалентность предсказания и эталона (math_verify).",
109
+ ),
110
  ],
111
  col_name="T-math",
112
+ dataset_url="https://huggingface.co/datasets/MERA-evaluation/T-math",
113
  description=(
114
+ "331 олимпиадная математическая задача на русском из Всероссийской олимпиады "
115
+ "школьников и Московской олимпиады (1998–2025)."
 
116
  ),
117
  task_type="Математика, олимпиадные задачи",
118
  test_size=331,
119
+ primary_metric="exact_match",
120
  )
121
  luzitania = Task(
122
  benchmark="luzitania",
123
  metrics=[
124
+ Metric(
125
+ "exact_match",
126
+ "Exact match",
127
+ "Доля ответов, точно совпадающих с эталонным числовым ответом.",
128
+ ),
129
  ],
130
  col_name="Luzitania",
131
+ dataset_url="https://huggingface.co/datasets/MERA-evaluation/Luzitania",
132
  description=(
133
+ "Набор для оценки многошагового математического рассуждения на русском языке: "
134
+ "олимпиадные и продвинутые задачи с целочисленными ответами."
 
135
  ),
136
  task_type="Логика, рассуждение",
137
+ test_size=252,
138
+ primary_metric="exact_match",
139
  )
140
  mmred = Task(
141
  benchmark="mmred",
142
+ metrics=_mmred_metrics(),
 
 
 
143
  col_name="MMReD",
144
+ dataset_url="https://huggingface.co/datasets/MERA-evaluation/MMReD",
145
  description=(
146
+ "Multi-Modal Reasoning in Dense Context — синтетический бенчмарк рассуждения "
147
+ "на длинном плотном контексте. 5 типов вопросов × 3 длины (32, 64, 128) = 750 вопросов."
 
 
148
  ),
149
  task_type="Длинный контекст, рассуждение",
150
  test_size=750,
151
+ primary_metric="em.dc_aggregate",
152
  )
153
 
154
 
 
176
 
177
  | Бенчмарк | Тип | Размер | Основная метрика |
178
  |----------|-----|--------|------------------|
179
+ | ruAIME | Математика | 724 | Exact match |
180
+ | T-math | Олимпиадная математика | 331 | Exact match |
181
+ | Luzitania | Логика, рассуждение | 252 | Exact match |
182
+ | MMReD | Длинный контекст | 750 | em.dc_aggregate |
183
  """
184
 
185
  SUBMIT_TEXT = """
 
190
  - JSON-файлы с предсказаниями для каждого бенчмарка (`ruaime.json`, `t_math.json`, `luzitania.json`, `mmred.json`)
191
  - `logs_public.zip` или папка `logs_public/` с файлами `results_*.json`
192
 
193
+ Оценки берутся **только** из `results_*.json` внутри `logs_public`. После загрузки результат сохраняется в dataset-репозиторий и подхватывается при перезагрузке страницы.
194
  """
195
 
196
  CITATION_BUTTON_LABEL = "Скопируйте для цитирования"
197
  CITATION_BUTTON_TEXT = r"""
198
  @misc{mera_reason,
199
  title={MERA Reason: Russian Reasoning Evaluation Benchmark},
200
+ howpublished={\url{https://huggingface.co/spaces/MERA-evaluation/MERA_Reason}},
201
  }
202
  """
src/leaderboard/metrics.py CHANGED
@@ -1,18 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
  METRIC_ALIASES: dict[str, list[str]] = {
2
- "pass@1": ["pass@1", "exact_match,extract_number", "exact_match,strict-match"],
3
- "acc": ["acc", "exact_match,strict-match"],
4
- "em": ["em", "exact_match,flexible-extract"],
5
- "acc_32": ["acc_32"],
6
- "acc_64": ["acc_64"],
7
- "acc_128": ["acc_128"],
8
  }
9
 
10
 
11
  def extract_metric_value(task_result: dict, metric_key: str) -> float | None:
12
- if metric_key in task_result and isinstance(task_result[metric_key], (int, float)):
13
- return float(task_result[metric_key])
 
 
14
 
15
- candidates = [metric_key] + METRIC_ALIASES.get(metric_key, [])
16
  for candidate in candidates:
17
  if candidate in task_result and isinstance(task_result[candidate], (int, float)):
18
  return float(task_result[candidate])
@@ -27,7 +38,6 @@ def extract_metric_value(task_result: dict, metric_key: str) -> float | None:
27
  for key, value in task_result.items():
28
  if not isinstance(value, (int, float)) or key in ("alias",) or ",stderr" in key:
29
  continue
30
- base = key.split(",")[0]
31
- if base == metric_key or key.startswith(f"{metric_key},"):
32
  return float(value)
33
  return None
 
1
+ EXACT_MATCH_ALIASES = [
2
+ "exact_match",
3
+ "exact_match,flexible-extract",
4
+ "exact_match,strict-match",
5
+ "exact_match,scoring",
6
+ ]
7
+
8
+ EM_DC_AGGREGATE_ALIASES = [
9
+ "em.dc_aggregate",
10
+ "em.dc_aggregate,scoring",
11
+ ]
12
+
13
  METRIC_ALIASES: dict[str, list[str]] = {
14
+ "exact_match": EXACT_MATCH_ALIASES,
15
+ "em.dc_aggregate": EM_DC_AGGREGATE_ALIASES,
16
+ "em": EXACT_MATCH_ALIASES,
 
 
 
17
  }
18
 
19
 
20
  def extract_metric_value(task_result: dict, metric_key: str) -> float | None:
21
+ base_key = metric_key.split("::")[-1]
22
+
23
+ if base_key in task_result and isinstance(task_result[base_key], (int, float)):
24
+ return float(task_result[base_key])
25
 
26
+ candidates = [base_key] + METRIC_ALIASES.get(base_key, [])
27
  for candidate in candidates:
28
  if candidate in task_result and isinstance(task_result[candidate], (int, float)):
29
  return float(task_result[candidate])
 
38
  for key, value in task_result.items():
39
  if not isinstance(value, (int, float)) or key in ("alias",) or ",stderr" in key:
40
  continue
41
+ if key == base_key or key.startswith(f"{base_key},"):
 
42
  return float(value)
43
  return None
src/leaderboard/store.py CHANGED
@@ -22,10 +22,12 @@ def has_submission(model: str) -> bool:
22
  return os.path.isfile(result_path_for_model(model))
23
 
24
 
25
- def _dataset_total_score(metrics: dict[str, float]) -> float:
26
- if not metrics:
27
- return 0.0
28
- return sum(metrics.values()) / len(metrics)
 
 
29
 
30
 
31
  def build_result_payload(
@@ -47,7 +49,7 @@ def build_result_payload(
47
  if not raw_metrics:
48
  continue
49
  metrics = {key: float(value) for key, value in raw_metrics.items()}
50
- dataset_total = _dataset_total_score(metrics)
51
  dataset_scores.append(dataset_total)
52
  payload[benchmark] = {
53
  "dataset_total_score": dataset_total,
 
22
  return os.path.isfile(result_path_for_model(model))
23
 
24
 
25
+ def _primary_metric_score(benchmark: str, metrics: dict[str, float]) -> float:
26
+ task = next(t for t in Tasks if t.value.benchmark == benchmark)
27
+ primary = task.value.primary_metric
28
+ if primary in metrics:
29
+ return float(metrics[primary])
30
+ return 0.0
31
 
32
 
33
  def build_result_payload(
 
49
  if not raw_metrics:
50
  continue
51
  metrics = {key: float(value) for key, value in raw_metrics.items()}
52
+ dataset_total = _primary_metric_score(benchmark, metrics)
53
  dataset_scores.append(dataset_total)
54
  payload[benchmark] = {
55
  "dataset_total_score": dataset_total,
src/populate.py CHANGED
@@ -61,8 +61,14 @@ def get_dataset_leaderboard_df(task_name: str) -> pd.DataFrame:
61
 
62
  def get_dataset_info_markdown(task_name: str) -> str:
63
  task = Tasks[task_name].value
 
 
 
 
 
64
  metrics_table = "\n".join(
65
- f"| {m.col_name} | `{m.key}` |" for m in task.metrics
 
66
  )
67
  return f"""
68
  ### {task.col_name}
@@ -71,14 +77,16 @@ def get_dataset_info_markdown(task_name: str) -> str:
71
 
72
  | Параметр | Значение |
73
  |----------|----------|
 
74
  | Тип | {task.task_type} |
75
  | Размер теста | {task.test_size} |
76
  | Ключ в JSON | `{task.benchmark}` |
 
77
 
78
  #### Метрики
79
 
80
- | Отображение | Ключ в JSON |
81
- |-------------|-------------|
82
  {metrics_table}
83
  """
84
 
 
61
 
62
  def get_dataset_info_markdown(task_name: str) -> str:
63
  task = Tasks[task_name].value
64
+ dataset_link = (
65
+ f"[{task.col_name}]({task.dataset_url})"
66
+ if task.dataset_url
67
+ else task.col_name
68
+ )
69
  metrics_table = "\n".join(
70
+ f"| {m.col_name} | `{m.key}` | {m.description or '—'} |"
71
+ for m in task.metrics
72
  )
73
  return f"""
74
  ### {task.col_name}
 
77
 
78
  | Параметр | Значение |
79
  |----------|----------|
80
+ | Датасет | {dataset_link} |
81
  | Тип | {task.task_type} |
82
  | Размер теста | {task.test_size} |
83
  | Ключ в JSON | `{task.benchmark}` |
84
+ | Основная метрика | `{task.primary_metric}` |
85
 
86
  #### Метрики
87
 
88
+ | Отображение | Ключ в JSON | Описание |
89
+ |-------------|-------------|----------|
90
  {metrics_table}
91
  """
92
 
src/submission/process_zip.py CHANGED
@@ -5,7 +5,7 @@ import tempfile
5
  import zipfile
6
  from dataclasses import dataclass
7
 
8
- from src.about import Tasks
9
  from src.leaderboard.metrics import extract_metric_value
10
 
11
 
@@ -13,22 +13,24 @@ LOGS_PUBLIC_ZIP = "logs_public.zip"
13
  LOGS_PUBLIC_DIR = "logs_public"
14
  RESULTS_PREFIX = "results_"
15
 
 
 
 
 
 
 
 
 
16
  TASK_KEY_ALIASES: dict[str, set[str]] = {
17
  task.value.benchmark: {task.value.benchmark, task.value.col_name.lower()}
18
  for task in Tasks
19
  }
 
20
  for task in Tasks:
21
  benchmark = task.value.benchmark
22
  TASK_KEY_ALIASES[benchmark].add(benchmark.replace("_", ""))
23
 
24
 
25
- @dataclass
26
- class ParsedResultFile:
27
- path: str
28
- benchmark: str
29
- data: dict
30
-
31
-
32
  def _normalize_task_key(task_key: str) -> str:
33
  key = task_key.lower().removesuffix("_gen")
34
  return key.replace("-", "_")
@@ -36,6 +38,10 @@ def _normalize_task_key(task_key: str) -> str:
36
 
37
  def _benchmark_for_task_key(task_key: str) -> str | None:
38
  normalized = _normalize_task_key(task_key)
 
 
 
 
39
  for benchmark, aliases in TASK_KEY_ALIASES.items():
40
  alias_norm = {_normalize_task_key(alias) for alias in aliases}
41
  if normalized in alias_norm or normalized.startswith(benchmark):
@@ -52,25 +58,60 @@ def _submission_name_candidates(task) -> set[str]:
52
  benchmark.replace("_", "").lower(),
53
  col_name.replace("-", "").lower(),
54
  col_name.replace("-", "_").lower(),
 
55
  }
56
 
57
 
58
- def get_scores_from_result(parsed: ParsedResultFile) -> dict[str, float]:
59
- task = next(t for t in Tasks if t.value.benchmark == parsed.benchmark)
60
- task_result = None
61
- for task_key, result in parsed.data.get("results", {}).items():
62
- if _benchmark_for_task_key(task_key) == parsed.benchmark:
63
- task_result = result
64
- break
65
- if task_result is None:
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  return {}
67
 
68
- scores = {}
69
- for metric in task.value.metrics:
70
- value = extract_metric_value(task_result, metric.key)
71
- if value is not None:
72
- scores[metric.key] = value
73
- return scores
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
 
76
  def extract_submission_archive(zip_path: str) -> str:
@@ -137,35 +178,51 @@ def validate_submission_files(submission_root: str) -> list[str]:
137
  return missing
138
 
139
 
140
- def parse_logs_public_results(logs_public_dir: str) -> list[ParsedResultFile]:
141
- parsed = []
142
- for name in sorted(os.listdir(logs_public_dir)):
143
- if not name.startswith(RESULTS_PREFIX) or not name.endswith(".json"):
144
  continue
145
- path = os.path.join(logs_public_dir, name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  with open(path, encoding="utf-8") as fp:
147
  data = json.load(fp)
148
-
149
- for task_key, task_result in data.get("results", {}).items():
150
- benchmark = _benchmark_for_task_key(task_key)
151
- if benchmark is None:
152
- continue
153
- parsed.append(ParsedResultFile(path=path, benchmark=benchmark, data=data))
154
- break
155
- return parsed
156
 
157
 
158
  def build_normalized_result_payload(
159
- parsed_results: list[ParsedResultFile],
160
  model_name: str,
161
  team: str,
162
  ) -> dict:
163
- results = {}
164
- for parsed in parsed_results:
165
- scores = get_scores_from_result(parsed)
166
- if scores:
167
- results[parsed.benchmark] = scores
168
-
169
  return {
170
  "model": model_name,
171
  "team": team,
@@ -177,24 +234,34 @@ def process_submission_zip(
177
  zip_path: str,
178
  model_name: str,
179
  team: str,
180
- ) -> tuple[dict, list[ParsedResultFile], list[str]]:
181
  extract_dir = extract_submission_archive(zip_path)
182
  logs_public_dir = None
183
  try:
184
  submission_root = _find_submission_root(extract_dir)
185
  missing_files = validate_submission_files(submission_root)
186
  logs_public_dir = _resolve_logs_public_dir(submission_root)
187
- parsed_results = parse_logs_public_results(logs_public_dir)
188
 
189
  if not parsed_results:
 
 
 
 
 
 
 
 
 
 
 
190
  raise ValueError(
191
- "No leaderboard task scores found in logs_public. "
192
  f"Expected results for: {', '.join(t.value.col_name for t in Tasks)}."
193
  )
194
 
195
- found_benchmarks = {parsed.benchmark for parsed in parsed_results}
196
  expected_benchmarks = {task.value.benchmark for task in Tasks}
197
- missing_benchmarks = expected_benchmarks - found_benchmarks
198
  if missing_benchmarks:
199
  missing_names = [
200
  next(t.value.col_name for t in Tasks if t.value.benchmark == benchmark)
@@ -205,12 +272,13 @@ def process_submission_zip(
205
  + ", ".join(missing_names)
206
  )
207
 
208
- for parsed in parsed_results:
209
- if not get_scores_from_result(parsed):
210
- task_name = next(
211
- t.value.col_name for t in Tasks if t.value.benchmark == parsed.benchmark
 
 
212
  )
213
- raise ValueError(f"Could not extract metrics for {task_name} from logs_public.")
214
 
215
  resolved_model = model_name.strip()
216
  if not resolved_model:
 
5
  import zipfile
6
  from dataclasses import dataclass
7
 
8
+ from src.about import MMRED_GROUP_KEYS, MMRED_SUBTASK_KEYS, Tasks
9
  from src.leaderboard.metrics import extract_metric_value
10
 
11
 
 
13
  LOGS_PUBLIC_DIR = "logs_public"
14
  RESULTS_PREFIX = "results_"
15
 
16
+
17
+ @dataclass
18
+ class ParsedResultFile:
19
+ path: str
20
+ benchmark: str
21
+ data: dict
22
+
23
+
24
  TASK_KEY_ALIASES: dict[str, set[str]] = {
25
  task.value.benchmark: {task.value.benchmark, task.value.col_name.lower()}
26
  for task in Tasks
27
  }
28
+ TASK_KEY_ALIASES["t_math"].update({"tmath", "t-math"})
29
  for task in Tasks:
30
  benchmark = task.value.benchmark
31
  TASK_KEY_ALIASES[benchmark].add(benchmark.replace("_", ""))
32
 
33
 
 
 
 
 
 
 
 
34
  def _normalize_task_key(task_key: str) -> str:
35
  key = task_key.lower().removesuffix("_gen")
36
  return key.replace("-", "_")
 
38
 
39
  def _benchmark_for_task_key(task_key: str) -> str | None:
40
  normalized = _normalize_task_key(task_key)
41
+ if normalized == "tmath":
42
+ return "t_math"
43
+ if normalized == "mmred" or normalized.startswith("mmred_"):
44
+ return "mmred"
45
  for benchmark, aliases in TASK_KEY_ALIASES.items():
46
  alias_norm = {_normalize_task_key(alias) for alias in aliases}
47
  if normalized in alias_norm or normalized.startswith(benchmark):
 
58
  benchmark.replace("_", "").lower(),
59
  col_name.replace("-", "").lower(),
60
  col_name.replace("-", "_").lower(),
61
+ "tmath",
62
  }
63
 
64
 
65
+ def _extract_mmred_scores(task_key: str, task_result: dict, scores: dict[str, float]) -> None:
66
+ normalized = _normalize_task_key(task_key)
67
+ if normalized == "mmred":
68
+ value = extract_metric_value(task_result, "em.dc_aggregate")
69
+ if value is not None:
70
+ scores["em.dc_aggregate"] = value
71
+ return
72
+ if normalized in MMRED_GROUP_KEYS:
73
+ value = extract_metric_value(task_result, "em.dc_aggregate")
74
+ if value is not None:
75
+ scores[f"{normalized}::em.dc_aggregate"] = value
76
+ return
77
+ if normalized in MMRED_SUBTASK_KEYS:
78
+ value = extract_metric_value(task_result, "exact_match")
79
+ if value is not None:
80
+ scores[f"{normalized}::exact_match"] = value
81
+
82
+
83
+ def _extract_benchmark_scores(task_key: str, task_result: dict) -> dict[str, float]:
84
+ benchmark = _benchmark_for_task_key(task_key)
85
+ if benchmark is None:
86
  return {}
87
 
88
+ if benchmark == "mmred":
89
+ scores: dict[str, float] = {}
90
+ _extract_mmred_scores(task_key, task_result, scores)
91
+ return scores
92
+
93
+ value = extract_metric_value(task_result, "exact_match")
94
+ if value is None:
95
+ return {}
96
+ return {"exact_match": value}
97
+
98
+
99
+ def parse_results_data(data: dict) -> dict[str, dict[str, float]]:
100
+ merged: dict[str, dict[str, float]] = {}
101
+ for task_key, task_result in data.get("results", {}).items():
102
+ if not isinstance(task_result, dict):
103
+ continue
104
+ benchmark = _benchmark_for_task_key(task_key)
105
+ if benchmark is None:
106
+ continue
107
+ scores = _extract_benchmark_scores(task_key, task_result)
108
+ if scores:
109
+ merged.setdefault(benchmark, {}).update(scores)
110
+ return merged
111
+
112
+
113
+ def get_scores_from_result(parsed: ParsedResultFile) -> dict[str, float]:
114
+ return parse_results_data(parsed.data).get(parsed.benchmark, {})
115
 
116
 
117
  def extract_submission_archive(zip_path: str) -> str:
 
178
  return missing
179
 
180
 
181
+ def _iter_results_files(*roots: str):
182
+ seen = set()
183
+ for root in roots:
184
+ if not os.path.isdir(root):
185
  continue
186
+ for name in sorted(os.listdir(root)):
187
+ if not name.startswith(RESULTS_PREFIX) or not name.endswith(".json"):
188
+ continue
189
+ path = os.path.join(root, name)
190
+ if path in seen:
191
+ continue
192
+ seen.add(path)
193
+ yield path
194
+
195
+
196
+ def _count_results_files(*roots: str) -> int:
197
+ count = 0
198
+ for root in roots:
199
+ if not os.path.isdir(root):
200
+ continue
201
+ for name in os.listdir(root):
202
+ if name.startswith(RESULTS_PREFIX) and name.endswith(".json"):
203
+ count += 1
204
+ return count
205
+
206
+
207
+ def parse_logs_public_results(logs_public_dir: str, submission_root: str | None = None) -> dict[str, dict[str, float]]:
208
+ merged: dict[str, dict[str, float]] = {}
209
+ roots = [logs_public_dir]
210
+ if submission_root:
211
+ roots.append(submission_root)
212
+
213
+ for path in _iter_results_files(*roots):
214
  with open(path, encoding="utf-8") as fp:
215
  data = json.load(fp)
216
+ for benchmark, scores in parse_results_data(data).items():
217
+ merged.setdefault(benchmark, {}).update(scores)
218
+ return merged
 
 
 
 
 
219
 
220
 
221
  def build_normalized_result_payload(
222
+ results: dict[str, dict[str, float]],
223
  model_name: str,
224
  team: str,
225
  ) -> dict:
 
 
 
 
 
 
226
  return {
227
  "model": model_name,
228
  "team": team,
 
234
  zip_path: str,
235
  model_name: str,
236
  team: str,
237
+ ) -> tuple[dict, dict[str, dict[str, float]], list[str]]:
238
  extract_dir = extract_submission_archive(zip_path)
239
  logs_public_dir = None
240
  try:
241
  submission_root = _find_submission_root(extract_dir)
242
  missing_files = validate_submission_files(submission_root)
243
  logs_public_dir = _resolve_logs_public_dir(submission_root)
244
+ parsed_results = parse_logs_public_results(logs_public_dir, submission_root)
245
 
246
  if not parsed_results:
247
+ roots = [logs_public_dir]
248
+ if submission_root:
249
+ roots.append(submission_root)
250
+ has_results = _count_results_files(*roots) > 0
251
+ if has_results:
252
+ detail = "Found results_*.json files but could not extract leaderboard metrics from them."
253
+ else:
254
+ detail = (
255
+ "logs_public contains no results_*.json files (only samples are not enough). "
256
+ "Re-pack the submission with log_to_reasoning_submission.py after evaluation."
257
+ )
258
  raise ValueError(
259
+ f"No leaderboard task scores found in logs_public. {detail} "
260
  f"Expected results for: {', '.join(t.value.col_name for t in Tasks)}."
261
  )
262
 
 
263
  expected_benchmarks = {task.value.benchmark for task in Tasks}
264
+ missing_benchmarks = expected_benchmarks - set(parsed_results)
265
  if missing_benchmarks:
266
  missing_names = [
267
  next(t.value.col_name for t in Tasks if t.value.benchmark == benchmark)
 
272
  + ", ".join(missing_names)
273
  )
274
 
275
+ for benchmark in expected_benchmarks:
276
+ scores = parsed_results.get(benchmark, {})
277
+ task = next(t for t in Tasks if t.value.benchmark == benchmark)
278
+ if task.value.primary_metric not in scores:
279
+ raise ValueError(
280
+ f"Could not extract {task.value.primary_metric} for {task.value.col_name} from logs_public."
281
  )
 
282
 
283
  resolved_model = model_name.strip()
284
  if not resolved_model:
yandex-reason-pro.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:be3c9af2f36b8c533581e1a230a08dfe09af4eb77914fdbb2be4d590170ef5a8
3
- size 8410