PeanutUp commited on
Commit
6d35426
·
1 Parent(s): a4b537f

Build MBench leaderboard Space

Browse files
.gitignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ .env
4
+ .venv/
5
+ mbench_leaderboard_submission/
6
+ membench_leaderboard_submission/
README.md CHANGED
@@ -1,13 +1,33 @@
1
  ---
2
  title: MBench Leaderboard
3
- emoji: 🏃
4
- colorFrom: green
5
- colorTo: red
6
  sdk: gradio
7
- sdk_version: 6.15.1
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: MBench Leaderboard
3
+ emoji: 🏆
4
+ colorFrom: indigo
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 4.36.1
8
+ python_version: 3.10
9
  app_file: app.py
10
  pinned: false
11
+ license: mit
12
  ---
13
 
14
+ # MBench Leaderboard
15
+
16
+ MBench is a benchmark for evaluating the memory capability of video world models. It focuses on whether a model can preserve a coherent world state across long-horizon video continuation and interaction.
17
+
18
+ The benchmark is organized around three core memory dimensions:
19
+
20
+ - **Entity Consistency:** persistent object and human identity, geometry, texture, and appearance.
21
+ - **Environment Consistency:** stable spatial layout, reprojection behavior, lighting, and style.
22
+ - **Causal Consistency:** reliable state evolution and interaction consequences over time.
23
+
24
+ MBench uses trigger-conditioned scoring: Trigger Coverage measures whether the model actually enters the intended memory challenge, Memory Reliability measures consistency after the challenge is triggered, and M-Score balances both with a harmonic mean.
25
+
26
+ The bundled seed leaderboard is transcribed from Table 2 of the MBench paper. Aggregate leaderboard columns are derived as unweighted averages over the reported sub-dimensions until official leaderboard totals are released.
27
+
28
+ ## Links
29
+
30
+ - **Dataset:** `studyOverflow/TempMemoryData`
31
+ - **Leaderboard data repo:** `PeanutUp/membench_leaderboard_submission`
32
+ - **GitHub repo:** https://github.com/study-overflow/MBench
33
+ - **Project page:** https://peanutup.github.io/MBench-project/
app.py ADDED
@@ -0,0 +1,450 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import re
4
+ import shutil
5
+ import tempfile
6
+ import zipfile
7
+ from datetime import datetime, timezone
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ import gradio as gr
12
+ import numpy as np
13
+ import pandas as pd
14
+ from huggingface_hub import HfApi, snapshot_download
15
+
16
+ from constants import (
17
+ ALL_COLUMNS,
18
+ LEADERBOARD_INTRO,
19
+ LEADERBOARD_REPO,
20
+ LOCAL_LEADERBOARD_DIR,
21
+ METRIC_COLUMNS,
22
+ MODEL_INFO_COLUMNS,
23
+ MODEL_TYPE_CHOICES,
24
+ RESULTS_CSV,
25
+ SUBMIT_INTRO,
26
+ )
27
+ from scripts.validate_submission import validate_submission_json
28
+
29
+
30
+ SPACE_ROOT = Path(__file__).resolve().parent
31
+ LOCAL_LEADERBOARD_PATH = Path(LOCAL_LEADERBOARD_DIR).resolve()
32
+ RESULTS_PATH = Path(RESULTS_CSV).resolve()
33
+ SEED_RESULTS_PATH = SPACE_ROOT / "seed" / "results.csv"
34
+ PENDING_DIR = LOCAL_LEADERBOARD_PATH / "submissions" / "pending"
35
+ VERIFIED_DIR = LOCAL_LEADERBOARD_PATH / "submissions" / "verified"
36
+ NUMERIC_COLUMNS = [
37
+ "Total M-Score",
38
+ "Entity Score",
39
+ "Environment Score",
40
+ "Causal Score",
41
+ *METRIC_COLUMNS,
42
+ ]
43
+ UPLOAD_ALLOW_PATTERNS = ["results.csv", "submissions/**"]
44
+
45
+
46
+ def empty_results() -> pd.DataFrame:
47
+ return pd.DataFrame(columns=ALL_COLUMNS)
48
+
49
+
50
+ def ensure_columns(df: pd.DataFrame) -> pd.DataFrame:
51
+ df = df.copy()
52
+ for column in ALL_COLUMNS:
53
+ if column not in df.columns:
54
+ df[column] = 0 if column in NUMERIC_COLUMNS or column == "Rank" else ""
55
+ return df[ALL_COLUMNS]
56
+
57
+
58
+ def clean_numeric_columns(df: pd.DataFrame) -> pd.DataFrame:
59
+ df = df.copy()
60
+ for column in NUMERIC_COLUMNS:
61
+ df[column] = pd.to_numeric(df[column], errors="coerce").fillna(0.0)
62
+ return df
63
+
64
+
65
+ def read_results_csv(path: Path) -> pd.DataFrame:
66
+ df = pd.read_csv(path)
67
+ return clean_numeric_columns(ensure_columns(df))
68
+
69
+
70
+ def load_seed_results(reason: str) -> tuple[pd.DataFrame, str]:
71
+ if SEED_RESULTS_PATH.exists():
72
+ try:
73
+ df = read_results_csv(SEED_RESULTS_PATH)
74
+ return (
75
+ df,
76
+ f"{reason}\n\nUsing bundled seed results from Table 2 of the MBench paper.",
77
+ )
78
+ except Exception as exc:
79
+ return empty_results(), f"{reason}\n\nCould not read bundled seed results: {exc}"
80
+ return empty_results(), f"{reason}\n\nBundled seed results are missing."
81
+
82
+
83
+ def load_remote_results() -> tuple[pd.DataFrame, str]:
84
+ try:
85
+ snapshot_download(
86
+ repo_id=LEADERBOARD_REPO,
87
+ repo_type="dataset",
88
+ local_dir=str(LOCAL_LEADERBOARD_PATH),
89
+ local_dir_use_symlinks=False,
90
+ )
91
+ except Exception as exc:
92
+ message = (
93
+ "Leaderboard data is not available yet. Please run "
94
+ "`python scripts/upload_seed_results.py` after setting `HF_TOKEN`."
95
+ f"\n\nDetails: {exc}"
96
+ )
97
+ return load_seed_results(message)
98
+
99
+ if not RESULTS_PATH.exists():
100
+ message = (
101
+ "`results.csv` was not found in the leaderboard data repo. Please run "
102
+ "`python scripts/upload_seed_results.py` to initialize it."
103
+ )
104
+ return load_seed_results(message)
105
+
106
+ try:
107
+ df = read_results_csv(RESULTS_PATH)
108
+ except Exception as exc:
109
+ return load_seed_results(f"Could not read `results.csv`: {exc}")
110
+
111
+ return df, f"Loaded results from `{LEADERBOARD_REPO}`."
112
+
113
+
114
+ def prepare_leaderboard(
115
+ model_type: str,
116
+ selected_metrics: list[str] | None,
117
+ ) -> tuple[pd.DataFrame, str]:
118
+ df, status = load_remote_results()
119
+
120
+ if model_type and model_type != "All" and not df.empty:
121
+ df = df[df["Model Type"] == model_type].copy()
122
+
123
+ if not df.empty:
124
+ df = df.sort_values(
125
+ by="Total M-Score",
126
+ ascending=False,
127
+ kind="mergesort",
128
+ ).reset_index(drop=True)
129
+ df["Rank"] = np.arange(1, len(df) + 1)
130
+
131
+ metrics = [metric for metric in (selected_metrics or []) if metric in METRIC_COLUMNS]
132
+ columns = MODEL_INFO_COLUMNS + metrics
133
+ return df[columns], status
134
+
135
+
136
+ def sanitize_filename(value: str) -> str:
137
+ cleaned = re.sub(r"[^A-Za-z0-9_.-]+", "_", value.strip())
138
+ cleaned = cleaned.strip("._-")
139
+ return cleaned or "model"
140
+
141
+
142
+ def get_uploaded_path(file_obj: Any) -> Path | None:
143
+ if file_obj is None:
144
+ return None
145
+ if isinstance(file_obj, (str, os.PathLike)):
146
+ return Path(file_obj)
147
+ if isinstance(file_obj, dict):
148
+ path = file_obj.get("path") or file_obj.get("name")
149
+ return Path(path) if path else None
150
+ name = getattr(file_obj, "name", None)
151
+ return Path(name) if name else None
152
+
153
+
154
+ def safe_extract_zip(zip_path: Path, target_dir: Path) -> None:
155
+ target_root = target_dir.resolve()
156
+ with zipfile.ZipFile(zip_path, "r") as zip_ref:
157
+ for member in zip_ref.infolist():
158
+ member_path = (target_root / member.filename).resolve()
159
+ try:
160
+ member_path.relative_to(target_root)
161
+ except ValueError:
162
+ raise ValueError("ZIP contains an unsafe path.")
163
+ zip_ref.extractall(target_root)
164
+
165
+
166
+ def read_first_json_from_zip(zip_path: Path) -> dict:
167
+ if not zipfile.is_zipfile(zip_path):
168
+ raise ValueError("Uploaded file must be a valid ZIP archive.")
169
+
170
+ with tempfile.TemporaryDirectory(prefix="mbench_submission_") as tmp_dir:
171
+ extract_dir = Path(tmp_dir)
172
+ safe_extract_zip(zip_path, extract_dir)
173
+ json_files = sorted(
174
+ path for path in extract_dir.rglob("*.json") if path.is_file()
175
+ )
176
+ if not json_files:
177
+ raise ValueError("No JSON file found inside the ZIP archive.")
178
+
179
+ with json_files[0].open("r", encoding="utf-8") as handle:
180
+ data = json.load(handle)
181
+
182
+ if not isinstance(data, dict):
183
+ raise ValueError("The first JSON file must contain a JSON object.")
184
+ return data
185
+
186
+
187
+ def require_text(value: str, label: str) -> str:
188
+ if value is None or not str(value).strip():
189
+ raise ValueError(f"{label} is required.")
190
+ return str(value).strip()
191
+
192
+
193
+ def ensure_submission_dirs() -> None:
194
+ PENDING_DIR.mkdir(parents=True, exist_ok=True)
195
+ VERIFIED_DIR.mkdir(parents=True, exist_ok=True)
196
+ (PENDING_DIR / ".gitkeep").touch(exist_ok=True)
197
+ (VERIFIED_DIR / ".gitkeep").touch(exist_ok=True)
198
+
199
+
200
+ def ensure_local_results_file() -> None:
201
+ if RESULTS_PATH.exists() or not SEED_RESULTS_PATH.exists():
202
+ return
203
+ RESULTS_PATH.parent.mkdir(parents=True, exist_ok=True)
204
+ shutil.copy2(SEED_RESULTS_PATH, RESULTS_PATH)
205
+
206
+
207
+ def save_pending_submission(
208
+ zip_path: Path,
209
+ result_json: dict,
210
+ model_name: str,
211
+ model_link: str,
212
+ team_name: str,
213
+ contact_email: str,
214
+ model_type: str,
215
+ accessibility: str,
216
+ ) -> tuple[Path, Path]:
217
+ ensure_submission_dirs()
218
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
219
+ safe_model_name = sanitize_filename(model_name)
220
+ stem = f"{timestamp}_{safe_model_name}"
221
+
222
+ payload = {
223
+ "submitted_at_utc": datetime.now(timezone.utc).isoformat(),
224
+ "status": "pending",
225
+ "model_name": model_name,
226
+ "model_link": model_link,
227
+ "team_name": team_name,
228
+ "contact_email": contact_email,
229
+ "model_type": model_type,
230
+ "accessibility": accessibility,
231
+ "result_json": result_json,
232
+ }
233
+
234
+ json_path = PENDING_DIR / f"{stem}.json"
235
+ raw_zip_path = PENDING_DIR / f"{stem}.zip"
236
+ json_path.write_text(
237
+ json.dumps(payload, indent=2, ensure_ascii=False),
238
+ encoding="utf-8",
239
+ )
240
+ shutil.copyfile(zip_path, raw_zip_path)
241
+ return json_path, raw_zip_path
242
+
243
+
244
+ def upload_local_leaderboard(token: str, model_name: str) -> None:
245
+ api = HfApi(token=token)
246
+ api.create_repo(repo_id=LEADERBOARD_REPO, repo_type="dataset", exist_ok=True)
247
+ ensure_local_results_file()
248
+ api.upload_folder(
249
+ folder_path=str(LOCAL_LEADERBOARD_PATH),
250
+ repo_id=LEADERBOARD_REPO,
251
+ repo_type="dataset",
252
+ allow_patterns=UPLOAD_ALLOW_PATTERNS,
253
+ commit_message=f"Add pending MBench submission for {model_name}",
254
+ )
255
+
256
+
257
+ def submit_result(
258
+ zip_file: Any,
259
+ model_name: str,
260
+ model_link: str,
261
+ team_name: str,
262
+ contact_email: str,
263
+ model_type: str,
264
+ accessibility: str,
265
+ ) -> str:
266
+ token = os.environ.get("HF_TOKEN")
267
+ if not token:
268
+ return "HF_TOKEN is not set. Please add it in Space Settings -> Secrets."
269
+
270
+ try:
271
+ model_name = require_text(model_name, "Model name")
272
+ model_link = require_text(model_link, "Model link")
273
+ contact_email = require_text(contact_email, "Contact email")
274
+ team_name = str(team_name or "").strip()
275
+ accessibility = str(accessibility or "Unknown").strip()
276
+
277
+ if model_type not in MODEL_TYPE_CHOICES[1:]:
278
+ raise ValueError("Model type must be text-conditioned or action-conditioned.")
279
+
280
+ zip_path = get_uploaded_path(zip_file)
281
+ if zip_path is None or not zip_path.exists():
282
+ raise ValueError("Please upload a ZIP file.")
283
+
284
+ result_json = read_first_json_from_zip(zip_path)
285
+ ok, message = validate_submission_json(result_json)
286
+ if not ok:
287
+ raise ValueError(message)
288
+
289
+ # Refresh the local dataset checkout before adding the pending submission.
290
+ try:
291
+ snapshot_download(
292
+ repo_id=LEADERBOARD_REPO,
293
+ repo_type="dataset",
294
+ local_dir=str(LOCAL_LEADERBOARD_PATH),
295
+ token=token,
296
+ local_dir_use_symlinks=False,
297
+ )
298
+ except Exception:
299
+ ensure_submission_dirs()
300
+
301
+ ensure_local_results_file()
302
+ save_pending_submission(
303
+ zip_path=zip_path,
304
+ result_json=result_json,
305
+ model_name=model_name,
306
+ model_link=model_link,
307
+ team_name=team_name,
308
+ contact_email=contact_email,
309
+ model_type=model_type,
310
+ accessibility=accessibility,
311
+ )
312
+ upload_local_leaderboard(token, model_name)
313
+ except Exception as exc:
314
+ return f"Submission failed: {exc}"
315
+
316
+ return "Submission received. It is pending official verification."
317
+
318
+
319
+ def about_markdown() -> str:
320
+ return """
321
+ # About MBench
322
+
323
+ MBench evaluates whether video world models preserve memory over long-horizon generation and interaction.
324
+
325
+ **Entity Consistency** measures whether objects and humans keep stable identity, geometry, texture, and appearance after occlusion, exit/re-entry, or viewpoint changes.
326
+
327
+ **Environment Consistency** measures whether the world layout remains stable, including epipolar geometry, reprojection consistency, lighting, and style.
328
+
329
+ **Causal Consistency** measures whether earlier events continue to affect later frames through state progress, physical plausibility, and text/action interaction.
330
+
331
+ **Trigger Coverage** checks whether a generated video actually enters the intended memory challenge. **Memory Reliability** evaluates consistency only after that challenge is triggered. **M-Score** is the harmonic mean of Trigger Coverage and Memory Reliability.
332
+
333
+ Submitted results are not automatically verified. Official results will be added after MBench team review.
334
+ """
335
+
336
+
337
+ def build_demo() -> gr.Blocks:
338
+ initial_df, initial_status = prepare_leaderboard("All", METRIC_COLUMNS)
339
+
340
+ css = """
341
+ .metric-panel textarea { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
342
+ footer { visibility: hidden; }
343
+ """
344
+
345
+ with gr.Blocks(
346
+ title="MBench Leaderboard",
347
+ theme=gr.themes.Soft(primary_hue="indigo", secondary_hue="purple"),
348
+ css=css,
349
+ ) as demo:
350
+ with gr.Tab("Leaderboard"):
351
+ gr.Markdown(LEADERBOARD_INTRO)
352
+ status = gr.Markdown(initial_status)
353
+
354
+ with gr.Row():
355
+ model_type_filter = gr.Radio(
356
+ choices=MODEL_TYPE_CHOICES,
357
+ value="All",
358
+ label="Model Type",
359
+ )
360
+ refresh_button = gr.Button("Refresh", variant="primary")
361
+
362
+ metric_selector = gr.CheckboxGroup(
363
+ choices=METRIC_COLUMNS,
364
+ value=METRIC_COLUMNS,
365
+ label="Metric Columns",
366
+ )
367
+
368
+ leaderboard_table = gr.Dataframe(
369
+ value=initial_df,
370
+ label="MBench Results",
371
+ interactive=False,
372
+ wrap=True,
373
+ height=520,
374
+ )
375
+
376
+ refresh_button.click(
377
+ fn=prepare_leaderboard,
378
+ inputs=[model_type_filter, metric_selector],
379
+ outputs=[leaderboard_table, status],
380
+ )
381
+ model_type_filter.change(
382
+ fn=prepare_leaderboard,
383
+ inputs=[model_type_filter, metric_selector],
384
+ outputs=[leaderboard_table, status],
385
+ )
386
+ metric_selector.change(
387
+ fn=prepare_leaderboard,
388
+ inputs=[model_type_filter, metric_selector],
389
+ outputs=[leaderboard_table, status],
390
+ )
391
+
392
+ with gr.Tab("Submit"):
393
+ gr.Markdown(SUBMIT_INTRO)
394
+ with gr.Row():
395
+ with gr.Column():
396
+ zip_input = gr.File(
397
+ label="Submission ZIP",
398
+ file_types=[".zip"],
399
+ type="filepath",
400
+ )
401
+ model_name_input = gr.Textbox(label="Model Name")
402
+ model_link_input = gr.Textbox(label="Model Link")
403
+ team_name_input = gr.Textbox(label="Team Name")
404
+ contact_email_input = gr.Textbox(label="Contact Email")
405
+ model_type_input = gr.Dropdown(
406
+ choices=MODEL_TYPE_CHOICES[1:],
407
+ value="text-conditioned",
408
+ label="Model Type",
409
+ )
410
+ accessibility_input = gr.Dropdown(
411
+ choices=[
412
+ "Open weights",
413
+ "API only",
414
+ "Closed",
415
+ "Research preview",
416
+ "Unknown",
417
+ ],
418
+ value="Unknown",
419
+ label="Accessibility",
420
+ )
421
+ submit_button = gr.Button("Submit", variant="primary")
422
+
423
+ with gr.Column():
424
+ submit_status = gr.Markdown()
425
+
426
+ submit_button.click(
427
+ fn=submit_result,
428
+ inputs=[
429
+ zip_input,
430
+ model_name_input,
431
+ model_link_input,
432
+ team_name_input,
433
+ contact_email_input,
434
+ model_type_input,
435
+ accessibility_input,
436
+ ],
437
+ outputs=submit_status,
438
+ )
439
+
440
+ with gr.Tab("About"):
441
+ gr.Markdown(about_markdown())
442
+
443
+ return demo
444
+
445
+
446
+ demo = build_demo()
447
+
448
+
449
+ if __name__ == "__main__":
450
+ demo.launch()
constants.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ BENCHMARK_DATASET_REPO = "studyOverflow/TempMemoryData"
2
+ LEADERBOARD_REPO = "PeanutUp/membench_leaderboard_submission"
3
+ LOCAL_LEADERBOARD_DIR = "./membench_leaderboard_submission"
4
+ RESULTS_CSV = "./membench_leaderboard_submission/results.csv"
5
+
6
+ MODEL_INFO_COLUMNS = [
7
+ "Rank",
8
+ "Model Name",
9
+ "Model Link",
10
+ "Model Type",
11
+ "Certification",
12
+ "Accessibility",
13
+ "Sampled by",
14
+ "Evaluated by",
15
+ "Date",
16
+ "Total M-Score",
17
+ "Entity Score",
18
+ "Environment Score",
19
+ "Causal Score",
20
+ ]
21
+
22
+ METRIC_COLUMNS = [
23
+ "Object Geometry",
24
+ "Object Texture",
25
+ "Human Identity",
26
+ "Human Appearance",
27
+ "Epipolar Geometry",
28
+ "Reprojection Consistency",
29
+ "Lighting Consistency",
30
+ "Style Consistency",
31
+ "State Progress",
32
+ "Physical Plausibility",
33
+ "Text Interaction",
34
+ "Action Interaction",
35
+ "Trigger Coverage",
36
+ "Memory Reliability",
37
+ ]
38
+
39
+ ALL_COLUMNS = MODEL_INFO_COLUMNS + METRIC_COLUMNS
40
+
41
+ MODEL_TYPE_CHOICES = [
42
+ "All",
43
+ "text-conditioned",
44
+ "action-conditioned",
45
+ ]
46
+
47
+ LEADERBOARD_INTRO = """
48
+ # MBench Leaderboard
49
+
50
+ **MBench** evaluates memory capability in video world models: whether generated worlds keep entities, environments, and causal state coherent across long horizons and interaction.
51
+
52
+ - **Entity Consistency:** object geometry/texture and human identity/appearance.
53
+ - **Environment Consistency:** spatial consistency, reprojection, lighting, and style stability.
54
+ - **Causal Consistency:** state progress, physical plausibility, and text/action interaction consistency.
55
+ - **Trigger-Conditioned Scoring:** M-Score is the harmonic mean of Trigger Coverage and Memory Reliability, so models are rewarded only when they both trigger the memory challenge and remain consistent after it.
56
+
57
+ Seed leaderboard values are transcribed from Table 2 of the MBench paper. Aggregate columns are derived as unweighted averages over the reported sub-dimensions until official leaderboard totals are released.
58
+
59
+ Dataset: `studyOverflow/TempMemoryData`
60
+ Leaderboard data repo: `PeanutUp/membench_leaderboard_submission`
61
+ Project page: https://peanutup.github.io/MBench-project/
62
+ GitHub: https://github.com/study-overflow/MBench
63
+ """
64
+
65
+ SUBMIT_INTRO = """
66
+ # Submit to MBench
67
+
68
+ Upload a ZIP file containing one JSON result file. Submissions are saved to `submissions/pending/` in the leaderboard data repo and are **not** added to the public leaderboard automatically.
69
+
70
+ Required JSON keys: `model_name`, `model_link`, `model_type`, and `total_m_score`.
71
+ """
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio==4.36.1
2
+ pandas
3
+ numpy
4
+ huggingface_hub==0.23.4
5
+ fastapi==0.111.0
6
+ starlette==0.37.2
7
+ socksio
scripts/upload_seed_results.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ import sys
4
+ import tempfile
5
+ from pathlib import Path
6
+
7
+ from huggingface_hub import HfApi
8
+
9
+ ROOT = Path(__file__).resolve().parents[1]
10
+ if str(ROOT) not in sys.path:
11
+ sys.path.insert(0, str(ROOT))
12
+
13
+ from constants import LEADERBOARD_REPO
14
+
15
+
16
+ REPO_TYPE = "dataset"
17
+
18
+
19
+ def main() -> None:
20
+ token = os.environ.get("HF_TOKEN")
21
+
22
+ seed_results = ROOT / "seed" / "results.csv"
23
+ if not seed_results.exists():
24
+ raise FileNotFoundError(f"Seed file not found: {seed_results}")
25
+
26
+ api = HfApi(token=token)
27
+ try:
28
+ api.whoami(token=token)
29
+ except Exception as exc:
30
+ raise RuntimeError(
31
+ "HF_TOKEN is not set and no cached Hugging Face login was found. "
32
+ "Please run `export HF_TOKEN=...` or `hf auth login` first."
33
+ ) from exc
34
+
35
+ api.create_repo(repo_id=LEADERBOARD_REPO, repo_type=REPO_TYPE, exist_ok=True)
36
+
37
+ with tempfile.TemporaryDirectory(prefix="mbench_seed_") as tmp_dir:
38
+ tmp_root = Path(tmp_dir)
39
+ shutil.copy2(seed_results, tmp_root / "results.csv")
40
+
41
+ pending_dir = tmp_root / "submissions" / "pending"
42
+ verified_dir = tmp_root / "submissions" / "verified"
43
+ pending_dir.mkdir(parents=True, exist_ok=True)
44
+ verified_dir.mkdir(parents=True, exist_ok=True)
45
+ (pending_dir / ".gitkeep").write_text("", encoding="utf-8")
46
+ (verified_dir / ".gitkeep").write_text("", encoding="utf-8")
47
+
48
+ api.upload_folder(
49
+ folder_path=str(tmp_root),
50
+ repo_id=LEADERBOARD_REPO,
51
+ repo_type=REPO_TYPE,
52
+ commit_message="Initialize MBench leaderboard seed results",
53
+ )
54
+
55
+ print(f"Uploaded seed results to {LEADERBOARD_REPO}: results.csv")
56
+ print("Created submissions/pending/.gitkeep and submissions/verified/.gitkeep")
57
+
58
+
59
+ if __name__ == "__main__":
60
+ main()
scripts/validate_submission.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import sys
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ ROOT = Path(__file__).resolve().parents[1]
7
+ if str(ROOT) not in sys.path:
8
+ sys.path.insert(0, str(ROOT))
9
+
10
+ from constants import METRIC_COLUMNS
11
+
12
+
13
+ ALLOWED_MODEL_TYPES = {"text-conditioned", "action-conditioned"}
14
+ REQUIRED_KEYS = ["model_name", "model_link", "model_type", "total_m_score"]
15
+ SUMMARY_SCORE_FIELDS = [
16
+ "total_m_score",
17
+ "entity_score",
18
+ "environment_score",
19
+ "causal_score",
20
+ ]
21
+
22
+
23
+ def _normalize_key(key: str) -> str:
24
+ return re.sub(r"[^a-z0-9]+", "_", key.lower()).strip("_")
25
+
26
+
27
+ def _is_present(value: Any) -> bool:
28
+ return value is not None and str(value).strip() != ""
29
+
30
+
31
+ def validate_submission_json(data: dict) -> tuple[bool, str]:
32
+ if not isinstance(data, dict):
33
+ return False, "Submission JSON must be an object."
34
+
35
+ normalized = {_normalize_key(str(key)): value for key, value in data.items()}
36
+
37
+ for key in REQUIRED_KEYS:
38
+ if key not in normalized or not _is_present(normalized[key]):
39
+ return False, f"Missing required field: {key}"
40
+
41
+ model_type = str(normalized["model_type"]).strip()
42
+ if model_type not in ALLOWED_MODEL_TYPES:
43
+ return (
44
+ False,
45
+ "model_type must be either text-conditioned or action-conditioned.",
46
+ )
47
+
48
+ score_fields = set(SUMMARY_SCORE_FIELDS)
49
+ score_fields.update(_normalize_key(column) for column in METRIC_COLUMNS)
50
+
51
+ for key, value in normalized.items():
52
+ if key in score_fields and _is_present(value):
53
+ try:
54
+ float(value)
55
+ except (TypeError, ValueError):
56
+ return False, f"Score field {key} must be convertible to float."
57
+
58
+ return True, "Submission JSON is valid."
seed/results.csv ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Rank,Model Name,Model Link,Model Type,Certification,Accessibility,Sampled by,Evaluated by,Date,Total M-Score,Entity Score,Environment Score,Causal Score,Object Geometry,Object Texture,Human Identity,Human Appearance,Epipolar Geometry,Reprojection Consistency,Lighting Consistency,Style Consistency,State Progress,Physical Plausibility,Text Interaction,Action Interaction,Trigger Coverage,Memory Reliability
2
+ 1,HY-WorldPlay,,action-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,67.25,58.67,81.69,55.53,47.12,68.54,52.46,66.58,83.86,68.17,82.67,92.04,0,0,25.38,85.69,0,0
3
+ 2,Infinite-World,,action-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,56.59,41.88,71.13,56.95,35.70,61.88,23.08,46.85,74.04,61.51,62.63,86.34,0,0,27.53,86.37,0,0
4
+ 3,Matrix-Game 3.0,,action-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,53.79,48.16,60.20,52.21,44.15,58.22,42.38,47.91,61.99,32.86,62.06,83.90,0,0,22.48,81.93,0,0
5
+ 4,Self Forcing,,text-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,49.44,61.42,46.77,30.81,85.23,66.16,37.67,56.64,52.76,8.70,52.01,73.61,33.50,0,28.13,0,0,0
6
+ 5,Yume-1.5,,action-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,46.67,42.23,50.57,47.76,60.96,49.99,17.41,40.57,51.86,24.55,51.21,74.65,0,0,33.32,62.20,0,0
7
+ 6,LongLive,,text-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,45.28,55.88,36.10,42.45,75.29,55.83,34.89,57.49,31.11,2.38,43.58,67.32,54.22,0,30.69,0,0,0
8
+ 7,MemFlow,,text-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,45.13,56.51,37.36,37.92,80.60,63.78,31.44,50.22,42.00,1.29,39.03,67.12,45.72,0,30.13,0,0,0
9
+ 8,Skyreels V2,,text-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,38.16,40.10,35.13,40.34,63.54,40.94,14.62,41.31,33.43,2.49,31.08,73.54,51.92,0,28.77,0,0,0
10
+ 9,LongCat-Video,,text-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,36.83,35.90,30.48,51.38,45.97,39.15,16.32,42.16,16.66,0.74,40.08,64.46,72.67,0,30.08,0,0,0
11
+ 10,Helios,,text-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,34.73,42.86,26.80,34.32,71.72,49.60,20.28,29.85,14.37,2.47,27.20,63.16,41.11,0,27.53,0,0,0
12
+ 11,Causal Forcing,,text-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,34.26,42.10,24.34,38.44,49.77,39.32,28.16,51.14,10.02,0.00,41.46,45.86,47.92,0,28.95,0,0,0
13
+ 12,Lingbot-World,,action-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,34.12,30.71,30.81,47.54,33.20,44.54,11.57,33.53,22.12,7.57,40.06,53.50,0,0,31.76,63.32,0,0
14
+ 13,Cosmos-Predict 2.5,,text-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,30.93,27.81,24.25,50.51,36.98,32.49,9.53,32.26,5.14,0.17,40.56,51.14,71.92,0,29.10,0,0,0
15
+ 14,Matrix-Game 2.0,,action-conditioned,MBench Paper,TBD,MBench Team,MBench Team,2026-05-28,20.34,11.44,23.44,31.93,14.62,28.99,1.22,0.94,14.78,3.08,38.79,37.10,0,0,16.00,47.86,0,0