montagovian commited on
Commit
e840a29
·
verified ·
1 Parent(s): 6910a88

Deploy read-only benchmark explorer

Browse files
Files changed (4) hide show
  1. README.md +8 -9
  2. app.py +374 -0
  3. data.py +187 -0
  4. requirements.txt +3 -0
README.md CHANGED
@@ -1,15 +1,14 @@
1
  ---
2
- title: BasedBench
3
- emoji: 🏃
4
- colorFrom: pink
5
- colorTo: purple
6
  sdk: gradio
7
- sdk_version: 6.20.0
8
- python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
11
- license: other
12
- short_description: An evaluation benchmark for meme understanding
13
  ---
14
 
15
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
1
  ---
2
+ title: basedBench
 
 
 
3
  sdk: gradio
4
+ sdk_version: 6.17.3
 
5
  app_file: app.py
6
  pinned: false
7
+ license: mit
8
+ python_version: 3.12
9
  ---
10
 
11
+ # basedBench
12
+
13
+ Read-only explorer and leaderboard for the
14
+ [basedBench dataset](https://huggingface.co/datasets/montagovian/basedBench).
app.py ADDED
@@ -0,0 +1,374 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Two-tab read-only BasedBench explorer for Hugging Face Spaces."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import html
6
+ import secrets
7
+ from typing import Any
8
+
9
+ import gradio as gr
10
+
11
+ try:
12
+ from data import BenchmarkData, load_from_hub
13
+ except ImportError:
14
+ from space.data import BenchmarkData, load_from_hub
15
+
16
+
17
+ DATA: BenchmarkData = load_from_hub()
18
+
19
+
20
+ def _escaped(value: Any) -> str:
21
+ return html.escape(str(value or ""))
22
+
23
+
24
+ def _quoted(value: Any) -> str:
25
+ lines = _escaped(value).splitlines() or [""]
26
+ return "\n".join(f"> {line}" for line in lines)
27
+
28
+
29
+ def _prediction_markdown(post_id: str, selected_model: str) -> str:
30
+ blocks: list[str] = []
31
+ for prediction in DATA.predictions(post_id, selected_model):
32
+ prediction_id = int(prediction["prediction_id"])
33
+ judgments = DATA.judgments(prediction_id)
34
+ correct = sum(row.get("verdict") == "correct" for row in judgments)
35
+ incorrect = sum(row.get("verdict") == "incorrect" for row in judgments)
36
+ consensus = str(prediction.get("consensus_verdict") or "no consensus")
37
+ judge_lines = []
38
+ for judgment in judgments:
39
+ line = (
40
+ f"**{_escaped(judgment['judge_model'])}:** "
41
+ f"{_escaped(judgment['verdict'])}"
42
+ )
43
+ if judgment.get("reasoning"):
44
+ line += "\n\n" + _quoted(judgment["reasoning"])
45
+ judge_lines.append(line)
46
+ historical = DATA.historical_judgment_counts.get(prediction_id, 0)
47
+ history_note = (
48
+ f"\n\n_{historical} superseded judgment record"
49
+ f"{'s' if historical != 1 else ''} retained in the dataset._"
50
+ if historical
51
+ else ""
52
+ )
53
+ judge_details = "\n\n".join(judge_lines) or "_No judge records._"
54
+ blocks.append(
55
+ f"### `{_escaped(prediction['model_id'])}`\n\n"
56
+ f"**Consensus: {consensus}** · {correct} correct / {incorrect} incorrect\n\n"
57
+ f"<details><summary>Model prediction</summary>\n\n"
58
+ f"{_escaped(prediction['prediction'])}\n\n</details>\n\n"
59
+ f"<details><summary>Judge details</summary>\n\n"
60
+ f"{judge_details}"
61
+ f"{history_note}\n\n</details>"
62
+ )
63
+ return "\n\n---\n\n".join(blocks) or "_No prediction matches this filter._"
64
+
65
+
66
+ def _empty_render(position: str = "0 / 0") -> tuple[Any, ...]:
67
+ return (
68
+ 0,
69
+ position,
70
+ gr.update(value=None, visible=False),
71
+ gr.update(value="_No memes match these filters._", visible=True),
72
+ gr.update(value="", visible=False),
73
+ gr.update(value="", visible=False),
74
+ )
75
+
76
+
77
+ def _render(
78
+ ids: list[str], idx: int, hide_ground_truth: bool, selected_model: str
79
+ ) -> tuple[Any, ...]:
80
+ if not ids:
81
+ return _empty_render()
82
+ bounded = max(0, min(int(idx), len(ids) - 1))
83
+ post_id = ids[bounded]
84
+ meme = DATA.meme(post_id)
85
+ info = (
86
+ f"## {_escaped(meme['title'])}\n\n"
87
+ f"`r/{_escaped(meme['subreddit'])}` · `{_escaped(post_id)}`"
88
+ )
89
+ return (
90
+ bounded,
91
+ f"{bounded + 1} / {len(ids)}",
92
+ gr.update(value=DATA.image(post_id), visible=True),
93
+ gr.update(value=info, visible=True),
94
+ gr.update(
95
+ value=("Ground truth hidden." if hide_ground_truth else meme["ground_truth"]),
96
+ visible=True,
97
+ ),
98
+ gr.update(
99
+ value=_prediction_markdown(post_id, selected_model),
100
+ visible=True,
101
+ ),
102
+ )
103
+
104
+
105
+ def apply_filters(
106
+ search: str, model_id: str, result: str, hide_ground_truth: bool
107
+ ) -> tuple[Any, ...]:
108
+ ids = DATA.filtered_ids(search, model_id, result)
109
+ return (ids, *_render(ids, 0, hide_ground_truth, model_id))
110
+
111
+
112
+ def step_item(
113
+ ids: list[str], idx: int, delta: int, hide_ground_truth: bool, model_id: str
114
+ ) -> tuple[Any, ...]:
115
+ return _render(ids, int(idx) + delta, hide_ground_truth, model_id)
116
+
117
+
118
+ def random_item(
119
+ ids: list[str], hide_ground_truth: bool, model_id: str
120
+ ) -> tuple[Any, ...]:
121
+ if not ids:
122
+ return _empty_render()
123
+ return _render(ids, secrets.randbelow(len(ids)), hide_ground_truth, model_id)
124
+
125
+
126
+ def rerender_item(
127
+ ids: list[str], idx: int, hide_ground_truth: bool, model_id: str
128
+ ) -> tuple[Any, ...]:
129
+ return _render(ids, idx, hide_ground_truth, model_id)
130
+
131
+
132
+ CSS = """
133
+ .gradio-container {
134
+ max-width: 1180px !important;
135
+ }
136
+ .app-header {
137
+ align-items: baseline !important;
138
+ margin-bottom: 4px !important;
139
+ }
140
+ .app-title h1 {
141
+ margin: 0 !important;
142
+ line-height: 1.1 !important;
143
+ }
144
+ .app-subtitle {
145
+ color: var(--body-text-color-subdued) !important;
146
+ font-size: 14px !important;
147
+ }
148
+ .inspect-toolbar {
149
+ gap: 8px !important;
150
+ align-items: center !important;
151
+ flex-wrap: wrap !important;
152
+ margin-bottom: 8px !important;
153
+ }
154
+ .inspect-toolbar .block {
155
+ min-width: 0 !important;
156
+ }
157
+ .nav-button {
158
+ min-width: 82px !important;
159
+ max-width: 96px !important;
160
+ }
161
+ .random-button {
162
+ min-width: 78px !important;
163
+ max-width: 88px !important;
164
+ }
165
+ .inspect-position {
166
+ min-width: 72px !important;
167
+ max-width: 84px !important;
168
+ text-align: center !important;
169
+ color: var(--body-text-color-subdued) !important;
170
+ }
171
+ .inspect-position p {
172
+ margin: 0 !important;
173
+ }
174
+ .meme-image img {
175
+ width: 100% !important;
176
+ max-height: 72vh !important;
177
+ object-fit: contain !important;
178
+ object-position: top center !important;
179
+ }
180
+ .prediction-panel details {
181
+ border-top: 1px solid var(--border-color-primary);
182
+ padding: 8px 0;
183
+ }
184
+ .prediction-panel summary {
185
+ cursor: pointer;
186
+ font-weight: 600;
187
+ }
188
+ .leaderboard-table {
189
+ min-height: 250px !important;
190
+ }
191
+ @media (max-width: 700px) {
192
+ .gradio-container {
193
+ padding-left: 10px !important;
194
+ padding-right: 10px !important;
195
+ }
196
+ .inspect-toolbar {
197
+ gap: 6px !important;
198
+ }
199
+ .filter-toolbar .form {
200
+ display: grid !important;
201
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) !important;
202
+ gap: 6px !important;
203
+ width: 100% !important;
204
+ }
205
+ .filter-toolbar .form > .block {
206
+ flex: none !important;
207
+ min-width: 0 !important;
208
+ max-width: none !important;
209
+ width: 100% !important;
210
+ }
211
+ .filter-toolbar .form > .block:first-child,
212
+ .filter-toolbar .form > .block:last-child {
213
+ grid-column: 1 / -1 !important;
214
+ }
215
+ .nav-button,
216
+ .random-button {
217
+ min-width: 70px !important;
218
+ max-width: none !important;
219
+ flex: 1 1 auto !important;
220
+ }
221
+ .meme-image img {
222
+ max-height: none !important;
223
+ }
224
+ }
225
+ """
226
+
227
+
228
+ def build_app() -> gr.Blocks:
229
+ model_choices = [("All models", "all")] + [(model, model) for model in DATA.models]
230
+ with gr.Blocks(title="basedBench") as demo:
231
+ with gr.Row(elem_classes="app-header"):
232
+ gr.HTML(
233
+ "<div class='app-title'><h1>basedBench</h1>"
234
+ "<div class='app-subtitle'>Read-only benchmark explorer</div></div>"
235
+ )
236
+
237
+ with gr.Tabs(selected="inspect"):
238
+ with gr.Tab("Inspect", id="inspect"):
239
+ ids_state = gr.State([])
240
+ idx_state = gr.State(0)
241
+
242
+ with gr.Row(elem_classes=["inspect-toolbar", "filter-toolbar"]):
243
+ search = gr.Textbox(
244
+ placeholder="Search title, source, ID, or ground truth",
245
+ label="Search",
246
+ show_label=False,
247
+ min_width=260,
248
+ scale=3,
249
+ )
250
+ model = gr.Dropdown(
251
+ choices=model_choices,
252
+ value="all",
253
+ label="Model",
254
+ show_label=False,
255
+ min_width=210,
256
+ scale=2,
257
+ )
258
+ result = gr.Dropdown(
259
+ choices=[
260
+ ("Any result", "all"),
261
+ ("Consensus correct", "correct"),
262
+ ("Consensus incorrect", "incorrect"),
263
+ ("Judge disagreement", "disagreement"),
264
+ ],
265
+ value="all",
266
+ label="Result",
267
+ show_label=False,
268
+ min_width=180,
269
+ scale=2,
270
+ )
271
+ hide_ground_truth = gr.Checkbox(
272
+ label="Hide ground truth",
273
+ value=False,
274
+ min_width=150,
275
+ scale=1,
276
+ )
277
+
278
+ with gr.Row(elem_classes="inspect-toolbar"):
279
+ previous = gr.Button("Previous", elem_classes="nav-button")
280
+ random_button = gr.Button("Random", elem_classes="random-button")
281
+ position = gr.Markdown("0 / 0", elem_classes="inspect-position")
282
+ next_button = gr.Button("Next", elem_classes="nav-button")
283
+
284
+ with gr.Row(equal_height=False):
285
+ with gr.Column(scale=1, min_width=320):
286
+ image = gr.Image(
287
+ label="Meme",
288
+ type="pil",
289
+ interactive=False,
290
+ elem_classes="meme-image",
291
+ )
292
+ with gr.Column(scale=1, min_width=320):
293
+ info = gr.Markdown()
294
+ ground_truth = gr.Textbox(
295
+ label="Ground Truth",
296
+ lines=5,
297
+ interactive=False,
298
+ )
299
+ predictions = gr.Markdown(elem_classes="prediction-panel")
300
+
301
+ render_outputs = [
302
+ idx_state,
303
+ position,
304
+ image,
305
+ info,
306
+ ground_truth,
307
+ predictions,
308
+ ]
309
+ filter_outputs = [ids_state, *render_outputs]
310
+ filter_inputs = [search, model, result, hide_ground_truth]
311
+
312
+ demo.load(apply_filters, inputs=filter_inputs, outputs=filter_outputs)
313
+ search.submit(apply_filters, inputs=filter_inputs, outputs=filter_outputs)
314
+ model.change(apply_filters, inputs=filter_inputs, outputs=filter_outputs)
315
+ result.change(apply_filters, inputs=filter_inputs, outputs=filter_outputs)
316
+ previous.click(
317
+ lambda ids, idx, hidden, selected: step_item(
318
+ ids, idx, -1, hidden, selected
319
+ ),
320
+ inputs=[ids_state, idx_state, hide_ground_truth, model],
321
+ outputs=render_outputs,
322
+ )
323
+ next_button.click(
324
+ lambda ids, idx, hidden, selected: step_item(
325
+ ids, idx, 1, hidden, selected
326
+ ),
327
+ inputs=[ids_state, idx_state, hide_ground_truth, model],
328
+ outputs=render_outputs,
329
+ )
330
+ random_button.click(
331
+ random_item,
332
+ inputs=[ids_state, hide_ground_truth, model],
333
+ outputs=render_outputs,
334
+ )
335
+ hide_ground_truth.change(
336
+ rerender_item,
337
+ inputs=[ids_state, idx_state, hide_ground_truth, model],
338
+ outputs=render_outputs,
339
+ )
340
+
341
+ with gr.Tab("Leaderboard"):
342
+ gr.Markdown(
343
+ f"**Snapshot:** `{DATA.snapshot_id}` · "
344
+ f"**Memes:** {len(DATA.post_ids):,} · "
345
+ f"**Predictions:** {len(DATA.predictions_by_id):,}"
346
+ )
347
+ gr.Dataframe(
348
+ value=DATA.leaderboard_rows(),
349
+ headers=[
350
+ "Model",
351
+ "Correct",
352
+ "Incorrect",
353
+ "Total",
354
+ "Accuracy",
355
+ "Judge agreement",
356
+ ],
357
+ datatype=["str", "number", "number", "number", "str", "str"],
358
+ interactive=False,
359
+ wrap=True,
360
+ elem_classes="leaderboard-table",
361
+ )
362
+ gr.Markdown(
363
+ "Consensus requires at least two matching judge votes. "
364
+ "Judge agreement is the stricter rate where all latest votes match."
365
+ )
366
+
367
+ return demo
368
+
369
+
370
+ demo = build_app()
371
+
372
+
373
+ if __name__ == "__main__":
374
+ demo.launch(css=CSS)
data.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Normalized dataset loading and indexing for the read-only Space."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from collections import defaultdict
7
+ from collections.abc import Iterable, Mapping
8
+ from typing import Any
9
+
10
+
11
+ DEFAULT_DATASET_REPO = "montagovian/basedBench"
12
+
13
+
14
+ def _column(table: Any, name: str) -> list[Any]:
15
+ try:
16
+ return list(table[name])
17
+ except (KeyError, TypeError):
18
+ return [row[name] for row in table]
19
+
20
+
21
+ class BenchmarkData:
22
+ """In-memory indexes over the four normalized dataset configs."""
23
+
24
+ def __init__(
25
+ self,
26
+ memes: Any,
27
+ predictions: Iterable[Mapping[str, Any]],
28
+ judgments: Iterable[Mapping[str, Any]],
29
+ leaderboard: Iterable[Mapping[str, Any]],
30
+ ) -> None:
31
+ self._memes = memes
32
+ post_ids = [str(value) for value in _column(memes, "post_id")]
33
+ titles = [str(value) for value in _column(memes, "title")]
34
+ subreddits = [str(value) for value in _column(memes, "subreddit")]
35
+ ground_truths = [str(value) for value in _column(memes, "ground_truth")]
36
+ snapshot_ids = [str(value) for value in _column(memes, "snapshot_id")]
37
+
38
+ self.post_ids = post_ids
39
+ self._row_index = {post_id: idx for idx, post_id in enumerate(post_ids)}
40
+ self._meta = {
41
+ post_id: {
42
+ "post_id": post_id,
43
+ "title": titles[idx],
44
+ "subreddit": subreddits[idx],
45
+ "ground_truth": ground_truths[idx],
46
+ "snapshot_id": snapshot_ids[idx],
47
+ }
48
+ for idx, post_id in enumerate(post_ids)
49
+ }
50
+
51
+ self.predictions_by_post: dict[str, list[dict[str, Any]]] = defaultdict(list)
52
+ self.predictions_by_id: dict[int, dict[str, Any]] = {}
53
+ for source in predictions:
54
+ row = dict(source)
55
+ prediction_id = int(row["prediction_id"])
56
+ post_id = str(row["post_id"])
57
+ self.predictions_by_id[prediction_id] = row
58
+ self.predictions_by_post[post_id].append(row)
59
+ for rows in self.predictions_by_post.values():
60
+ rows.sort(key=lambda row: str(row["model_id"]))
61
+
62
+ self.latest_judgments: dict[int, list[dict[str, Any]]] = defaultdict(list)
63
+ self.historical_judgment_counts: dict[int, int] = defaultdict(int)
64
+ for source in judgments:
65
+ row = dict(source)
66
+ prediction_id = int(row["prediction_id"])
67
+ if bool(row.get("is_latest")):
68
+ self.latest_judgments[prediction_id].append(row)
69
+ else:
70
+ self.historical_judgment_counts[prediction_id] += 1
71
+ for rows in self.latest_judgments.values():
72
+ rows.sort(key=lambda row: str(row["judge_model"]))
73
+
74
+ self.leaderboard = [dict(row) for row in leaderboard]
75
+ self.leaderboard.sort(
76
+ key=lambda row: (-float(row["accuracy"]), str(row["model_id"]))
77
+ )
78
+ self.models = sorted(
79
+ {
80
+ str(row["model_id"])
81
+ for rows in self.predictions_by_post.values()
82
+ for row in rows
83
+ }
84
+ )
85
+
86
+ @property
87
+ def snapshot_id(self) -> str:
88
+ if not self.post_ids:
89
+ return ""
90
+ return str(self._meta[self.post_ids[0]]["snapshot_id"])
91
+
92
+ def meme(self, post_id: str) -> dict[str, Any]:
93
+ return self._meta[post_id]
94
+
95
+ def image(self, post_id: str) -> Any:
96
+ return self._memes[self._row_index[post_id]]["image"]
97
+
98
+ def predictions(self, post_id: str, model_id: str = "all") -> list[dict[str, Any]]:
99
+ rows = self.predictions_by_post.get(post_id, [])
100
+ if model_id == "all":
101
+ return rows
102
+ return [row for row in rows if str(row["model_id"]) == model_id]
103
+
104
+ def judgments(self, prediction_id: int) -> list[dict[str, Any]]:
105
+ return self.latest_judgments.get(prediction_id, [])
106
+
107
+ def filtered_ids(
108
+ self,
109
+ search: str = "",
110
+ model_id: str = "all",
111
+ result: str = "all",
112
+ ) -> list[str]:
113
+ needle = search.strip().casefold()
114
+ matches: list[str] = []
115
+ for post_id in self.post_ids:
116
+ meta = self._meta[post_id]
117
+ if needle and needle not in " ".join(
118
+ (
119
+ post_id,
120
+ str(meta["title"]),
121
+ str(meta["subreddit"]),
122
+ str(meta["ground_truth"]),
123
+ )
124
+ ).casefold():
125
+ continue
126
+
127
+ predictions = self.predictions(post_id, model_id)
128
+ if model_id != "all" and not predictions:
129
+ continue
130
+ if result == "correct" and not any(
131
+ row.get("consensus_verdict") == "correct" for row in predictions
132
+ ):
133
+ continue
134
+ if result == "incorrect" and not any(
135
+ row.get("consensus_verdict") == "incorrect" for row in predictions
136
+ ):
137
+ continue
138
+ if result == "disagreement" and not any(
139
+ len(
140
+ {
141
+ judgment.get("verdict")
142
+ for judgment in self.judgments(int(row["prediction_id"]))
143
+ }
144
+ )
145
+ > 1
146
+ for row in predictions
147
+ ):
148
+ continue
149
+ matches.append(post_id)
150
+ return matches
151
+
152
+ def leaderboard_rows(self) -> list[list[Any]]:
153
+ return [
154
+ [
155
+ row["model_id"],
156
+ int(row["correct"]),
157
+ int(row["incorrect"]),
158
+ int(row["total"]),
159
+ f"{float(row['accuracy']) * 100:.1f}%",
160
+ (
161
+ f"{int(row['unanimous_agreements'])}/"
162
+ f"{int(row['judged_by_multiple'])} "
163
+ f"({float(row['agreement_rate']) * 100:.1f}%)"
164
+ ),
165
+ ]
166
+ for row in self.leaderboard
167
+ ]
168
+
169
+
170
+ def load_from_hub(repo_id: str | None = None) -> BenchmarkData:
171
+ """Load the published snapshot directly from the Hub, not dataset-server."""
172
+ from datasets import load_dataset
173
+
174
+ repo = repo_id or os.getenv("HF_DATASET_REPO", DEFAULT_DATASET_REPO)
175
+ token = os.getenv("HF_TOKEN") or os.getenv("HF_API_KEY")
176
+ kwargs = {"token": token} if token else {}
177
+ try:
178
+ memes = load_dataset(repo, "memes", split="train", **kwargs)
179
+ predictions = load_dataset(repo, "predictions", split="train", **kwargs)
180
+ judgments = load_dataset(repo, "judgments", split="train", **kwargs)
181
+ leaderboard = load_dataset(repo, "leaderboard", split="train", **kwargs)
182
+ except Exception as exc:
183
+ raise RuntimeError(
184
+ f"Unable to load {repo}. For a private dataset, add an HF_TOKEN "
185
+ "with read access to the Space secrets."
186
+ ) from exc
187
+ return BenchmarkData(memes, predictions, judgments, leaderboard)
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ datasets==4.8.4
2
+ huggingface_hub==1.8.0
3
+ Pillow==12.2.0