JacobPEvans commited on
Commit
b8182d6
Β·
verified Β·
1 Parent(s): 85ff16e

Deploy from 1ad58abad9750ed5d30bb5f033c99a815a3813a2

Browse files
Files changed (3) hide show
  1. README.md +79 -34
  2. app.py +272 -197
  3. requirements.txt +18 -16
README.md CHANGED
@@ -1,48 +1,93 @@
1
  ---
2
- title: Mlx Benchmarks Viewer
3
- emoji: πŸ₯‡
4
- colorFrom: green
5
  colorTo: indigo
6
  sdk: gradio
 
7
  app_file: app.py
 
8
  pinned: true
9
  license: apache-2.0
10
- short_description: Duplicate this leaderboard to initialize your own!
11
- sdk_version: 5.43.1
12
  tags:
13
- - leaderboard
 
 
 
 
 
 
 
 
14
  ---
15
 
16
- # Start the configuration
17
-
18
- Most of the variables to change for a default leaderboard are in `src/env.py` (replace the path for your leaderboard) and `src/about.py` (for tasks).
19
-
20
- Results files should have the following format and be stored as json files:
21
- ```json
22
- {
23
- "config": {
24
- "model_dtype": "torch.float16", # or torch.bfloat16 or 8bit or 4bit
25
- "model_name": "path of the model on the hub: org/model",
26
- "model_sha": "revision on the hub",
27
- },
28
- "results": {
29
- "task_name": {
30
- "metric_name": score,
31
- },
32
- "task_name2": {
33
- "metric_name": score,
34
- }
35
- }
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  ```
38
 
39
- Request files are created automatically by this tool.
40
 
41
- If you encounter problem on the space, don't hesitate to restart it to remove the create eval-queue, eval-queue-bk, eval-results and eval-results-bk created folder.
42
 
43
- # Code logic for more complex edits
44
 
45
- You'll find
46
- - the main table' columns names and properties in `src/display/utils.py`
47
- - the logic to read all results and request files, then convert them in dataframe lines, in `src/leaderboard/read_evals.py`, and `src/populate.py`
48
- - the logic to allow or filter submissions in `src/submission/submit.py` and `src/submission/check_validity.py`
 
1
  ---
2
+ title: MLX Benchmarks Viewer
3
+ emoji: πŸ“Š
4
+ colorFrom: blue
5
  colorTo: indigo
6
  sdk: gradio
7
+ sdk_version: "6.13.0"
8
  app_file: app.py
9
+ python_version: "3.11"
10
  pinned: true
11
  license: apache-2.0
12
+ short_description: Interactive viewer for the MLX Benchmarks dataset
 
13
  tags:
14
+ - benchmarks
15
+ - mlx
16
+ - apple-silicon
17
+ - lm-eval
18
+ - visualization
19
+ models:
20
+ - mlx-community/Qwen3.5-9B-MLX-4bit
21
+ datasets:
22
+ - JacobPEvans/mlx-benchmarks
23
  ---
24
 
25
+ Interactive Gradio viewer for the
26
+ [`JacobPEvans/mlx-benchmarks`](https://huggingface.co/datasets/JacobPEvans/mlx-benchmarks)
27
+ dataset β€” a collection of benchmark runs of MLX-quantized and locally-hosted
28
+ LLMs on Apple Silicon, all serialized under the envelope v1 schema.
29
+
30
+ ## Installation
31
+
32
+ Requires Python 3.11+.
33
+
34
+ ```sh
35
+ cd space
36
+ python -m venv .venv
37
+ source .venv/bin/activate
38
+ pip install -r requirements.txt
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ Launch the viewer locally:
44
+
45
+ ```sh
46
+ python app.py
47
+ ```
48
+
49
+ Gradio prints a local URL (default `http://127.0.0.1:7860`). The app pulls all
50
+ `data/*.parquet` shards from the HF dataset, caches them for 10 minutes, and
51
+ offers three tabs:
52
+
53
+ - **Bar chart β€” latest run** β€” one bar per model for a given (suite, task, metric)
54
+ - **Trend β€” over time** β€” score trajectory per model across runs
55
+ - **Summary table** β€” pivot of models x tasks for the selected (suite, metric)
56
+
57
+ Hit **Refresh data** to invalidate the cache manually.
58
+
59
+ ## Deployment
60
+
61
+ This Space is synced automatically from the
62
+ [JacobPEvans/mlx-benchmarks](https://github.com/JacobPEvans/mlx-benchmarks)
63
+ GitHub repository via the `deploy-space.yml` workflow on every `main` push
64
+ that touches `space/`.
65
+
66
+ ## Contributing
67
+
68
+ Issues and PRs live in the upstream
69
+ [GitHub repo](https://github.com/JacobPEvans/mlx-benchmarks). See the
70
+ repository `CONTRIBUTING.md` for the full developer workflow.
71
+
72
+ ## API
73
+
74
+ The viewer is a Gradio UI β€” it has no stable programmatic API. To query the
75
+ underlying data yourself, read the HF dataset directly:
76
+
77
+ ```python
78
+ import pandas as pd
79
+ from huggingface_hub import HfFileSystem
80
+
81
+ fs = HfFileSystem()
82
+ paths = fs.glob("datasets/JacobPEvans/mlx-benchmarks/data/*.parquet")
83
+ df = pd.concat([pd.read_parquet(f"hf://{p}") for p in paths], ignore_index=True)
84
  ```
85
 
86
+ ## License
87
 
88
+ Apache-2.0. See [LICENSE](https://github.com/JacobPEvans/mlx-benchmarks/blob/main/LICENSE).
89
 
90
+ ## Source
91
 
92
+ Full source, tests, and developer docs:
93
+ <https://github.com/JacobPEvans/mlx-benchmarks>
 
 
app.py CHANGED
@@ -1,204 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
- from gradio_leaderboard import Leaderboard, ColumnFilter, SelectColumns
3
  import pandas as pd
4
- from apscheduler.schedulers.background import BackgroundScheduler
5
- from huggingface_hub import snapshot_download
6
-
7
- from src.about import (
8
- CITATION_BUTTON_LABEL,
9
- CITATION_BUTTON_TEXT,
10
- EVALUATION_QUEUE_TEXT,
11
- INTRODUCTION_TEXT,
12
- LLM_BENCHMARKS_TEXT,
13
- TITLE,
14
- )
15
- from src.display.css_html_js import custom_css
16
- from src.display.utils import (
17
- BENCHMARK_COLS,
18
- COLS,
19
- EVAL_COLS,
20
- EVAL_TYPES,
21
- AutoEvalColumn,
22
- ModelType,
23
- fields,
24
- WeightType,
25
- Precision
26
- )
27
- from src.envs import API, EVAL_REQUESTS_PATH, EVAL_RESULTS_PATH, QUEUE_REPO, REPO_ID, RESULTS_REPO, TOKEN
28
- from src.populate import get_evaluation_queue_df, get_leaderboard_df
29
- from src.submission.submit import add_new_eval
30
-
31
-
32
- def restart_space():
33
- API.restart_space(repo_id=REPO_ID)
34
-
35
- ### Space initialisation
36
- try:
37
- print(EVAL_REQUESTS_PATH)
38
- snapshot_download(
39
- repo_id=QUEUE_REPO, local_dir=EVAL_REQUESTS_PATH, repo_type="dataset", tqdm_class=None, etag_timeout=30, token=TOKEN
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  )
41
- except Exception:
42
- restart_space()
43
- try:
44
- print(EVAL_RESULTS_PATH)
45
- snapshot_download(
46
- repo_id=RESULTS_REPO, local_dir=EVAL_RESULTS_PATH, repo_type="dataset", tqdm_class=None, etag_timeout=30, token=TOKEN
 
 
47
  )
48
- except Exception:
49
- restart_space()
50
-
51
-
52
- LEADERBOARD_DF = get_leaderboard_df(EVAL_RESULTS_PATH, EVAL_REQUESTS_PATH, COLS, BENCHMARK_COLS)
53
-
54
- (
55
- finished_eval_queue_df,
56
- running_eval_queue_df,
57
- pending_eval_queue_df,
58
- ) = get_evaluation_queue_df(EVAL_REQUESTS_PATH, EVAL_COLS)
59
-
60
- def init_leaderboard(dataframe):
61
- if dataframe is None or dataframe.empty:
62
- raise ValueError("Leaderboard DataFrame is empty or None.")
63
- return Leaderboard(
64
- value=dataframe,
65
- datatype=[c.type for c in fields(AutoEvalColumn)],
66
- select_columns=SelectColumns(
67
- default_selection=[c.name for c in fields(AutoEvalColumn) if c.displayed_by_default],
68
- cant_deselect=[c.name for c in fields(AutoEvalColumn) if c.never_hidden],
69
- label="Select Columns to Display:",
70
- ),
71
- search_columns=[AutoEvalColumn.model.name, AutoEvalColumn.license.name],
72
- hide_columns=[c.name for c in fields(AutoEvalColumn) if c.hidden],
73
- filter_columns=[
74
- ColumnFilter(AutoEvalColumn.model_type.name, type="checkboxgroup", label="Model types"),
75
- ColumnFilter(AutoEvalColumn.precision.name, type="checkboxgroup", label="Precision"),
76
- ColumnFilter(
77
- AutoEvalColumn.params.name,
78
- type="slider",
79
- min=0.01,
80
- max=150,
81
- label="Select the number of parameters (B)",
82
- ),
83
- ColumnFilter(
84
- AutoEvalColumn.still_on_hub.name, type="boolean", label="Deleted/incomplete", default=True
85
- ),
86
- ],
87
- bool_checkboxgroup_label="Hide models",
88
- interactive=False,
89
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
 
92
- demo = gr.Blocks(css=custom_css)
93
- with demo:
94
- gr.HTML(TITLE)
95
- gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
96
-
97
- with gr.Tabs(elem_classes="tab-buttons") as tabs:
98
- with gr.TabItem("πŸ… LLM Benchmark", elem_id="llm-benchmark-tab-table", id=0):
99
- leaderboard = init_leaderboard(LEADERBOARD_DF)
100
-
101
- with gr.TabItem("πŸ“ About", elem_id="llm-benchmark-tab-table", id=2):
102
- gr.Markdown(LLM_BENCHMARKS_TEXT, elem_classes="markdown-text")
103
-
104
- with gr.TabItem("πŸš€ Submit here! ", elem_id="llm-benchmark-tab-table", id=3):
105
- with gr.Column():
106
- with gr.Row():
107
- gr.Markdown(EVALUATION_QUEUE_TEXT, elem_classes="markdown-text")
108
-
109
- with gr.Column():
110
- with gr.Accordion(
111
- f"βœ… Finished Evaluations ({len(finished_eval_queue_df)})",
112
- open=False,
113
- ):
114
- with gr.Row():
115
- finished_eval_table = gr.components.Dataframe(
116
- value=finished_eval_queue_df,
117
- headers=EVAL_COLS,
118
- datatype=EVAL_TYPES,
119
- row_count=5,
120
- )
121
- with gr.Accordion(
122
- f"πŸ”„ Running Evaluation Queue ({len(running_eval_queue_df)})",
123
- open=False,
124
- ):
125
- with gr.Row():
126
- running_eval_table = gr.components.Dataframe(
127
- value=running_eval_queue_df,
128
- headers=EVAL_COLS,
129
- datatype=EVAL_TYPES,
130
- row_count=5,
131
- )
132
-
133
- with gr.Accordion(
134
- f"⏳ Pending Evaluation Queue ({len(pending_eval_queue_df)})",
135
- open=False,
136
- ):
137
- with gr.Row():
138
- pending_eval_table = gr.components.Dataframe(
139
- value=pending_eval_queue_df,
140
- headers=EVAL_COLS,
141
- datatype=EVAL_TYPES,
142
- row_count=5,
143
- )
144
- with gr.Row():
145
- gr.Markdown("# βœ‰οΈβœ¨ Submit your model here!", elem_classes="markdown-text")
146
-
147
- with gr.Row():
148
- with gr.Column():
149
- model_name_textbox = gr.Textbox(label="Model name")
150
- revision_name_textbox = gr.Textbox(label="Revision commit", placeholder="main")
151
- model_type = gr.Dropdown(
152
- choices=[t.to_str(" : ") for t in ModelType if t != ModelType.Unknown],
153
- label="Model type",
154
- multiselect=False,
155
- value=None,
156
- interactive=True,
157
- )
158
-
159
- with gr.Column():
160
- precision = gr.Dropdown(
161
- choices=[i.value.name for i in Precision if i != Precision.Unknown],
162
- label="Precision",
163
- multiselect=False,
164
- value="float16",
165
- interactive=True,
166
- )
167
- weight_type = gr.Dropdown(
168
- choices=[i.value.name for i in WeightType],
169
- label="Weights type",
170
- multiselect=False,
171
- value="Original",
172
- interactive=True,
173
- )
174
- base_model_name_textbox = gr.Textbox(label="Base model (for delta or adapter weights)")
175
-
176
- submit_button = gr.Button("Submit Eval")
177
- submission_result = gr.Markdown()
178
- submit_button.click(
179
- add_new_eval,
180
- [
181
- model_name_textbox,
182
- base_model_name_textbox,
183
- revision_name_textbox,
184
- precision,
185
- weight_type,
186
- model_type,
187
- ],
188
- submission_result,
189
- )
190
-
191
- with gr.Row():
192
- with gr.Accordion("πŸ“™ Citation", open=False):
193
- citation_button = gr.Textbox(
194
- value=CITATION_BUTTON_TEXT,
195
- label=CITATION_BUTTON_LABEL,
196
- lines=20,
197
- elem_id="citation-button",
198
- show_copy_button=True,
199
- )
200
-
201
- scheduler = BackgroundScheduler()
202
- scheduler.add_job(restart_space, "interval", seconds=1800)
203
- scheduler.start()
204
- demo.queue(default_concurrency_limit=40).launch()
 
1
+ """
2
+ MLX Benchmarks Viewer β€” Gradio Space
3
+
4
+ Reads all parquet shards from JacobPEvans/mlx-benchmarks and renders
5
+ interactive comparison charts. Auto-refreshes data every 10 minutes.
6
+
7
+ Deploy to HF Spaces (SDK: gradio, Python 3.11+).
8
+ """
9
+
10
+ import re
11
+ import time
12
+ from threading import Lock
13
+
14
  import gradio as gr
 
15
  import pandas as pd
16
+ import plotly.express as px
17
+ import plotly.graph_objects as go
18
+ from huggingface_hub import HfFileSystem
19
+
20
+ DATASET = "datasets/JacobPEvans/mlx-benchmarks"
21
+ CACHE_TTL = 600 # seconds
22
+ EXPECTED_COLUMNS = ["timestamp", "suite", "name", "metric", "model", "value"]
23
+ CSS = """
24
+ #title { text-align: center; margin-bottom: 4px; }
25
+ #subtitle { text-align: center; color: #666; margin-bottom: 20px; }
26
+ """
27
+
28
+
29
+ # ── Data loading ──────────────────────────────────────────────────────────────
30
+
31
+ _cache: tuple[float, pd.DataFrame] | None = None
32
+ _cache_lock = Lock()
33
+
34
+
35
+ def empty_data() -> pd.DataFrame:
36
+ return pd.DataFrame(columns=[*EXPECTED_COLUMNS, "model_short"])
37
+
38
+
39
+ def load_data() -> pd.DataFrame:
40
+ global _cache
41
+ with _cache_lock:
42
+ if _cache and time.time() - _cache[0] < CACHE_TTL:
43
+ return _cache[1]
44
+
45
+ fs = HfFileSystem()
46
+ try:
47
+ paths = sorted(f"hf://{p}" for p in fs.glob(f"{DATASET}/data/*.parquet"))
48
+ except (FileNotFoundError, OSError):
49
+ paths = []
50
+
51
+ if not paths:
52
+ df = empty_data()
53
+ _cache = (time.time(), df)
54
+ return df
55
+
56
+ df = pd.concat([pd.read_parquet(p) for p in paths], ignore_index=True)
57
+ df["timestamp"] = pd.to_datetime(df["timestamp"], utc=True)
58
+ df["model_short"] = df["model"].apply(short_model)
59
+ _cache = (time.time(), df)
60
+ return df
61
+
62
+
63
+ def short_model(name: str) -> str:
64
+ """Strip common prefixes for axis labels."""
65
+ name = re.sub(r"^mlx-community/", "", name)
66
+ name = re.sub(r"^openrouter/openai/", "openrouter/", name)
67
+ return name
68
+
69
+
70
+ # ── Chart builders ────────────────────────────────────────────────────────────
71
+
72
+
73
+ def bar_chart(df: pd.DataFrame, suite: str, task: str, metric: str) -> go.Figure:
74
+ """Latest-run bar chart: one bar per model, sorted by score."""
75
+ sub = df[(df["suite"] == suite) & (df["name"] == task) & (df["metric"] == metric)].copy()
76
+ if sub.empty:
77
+ fig = go.Figure()
78
+ fig.add_annotation(
79
+ text="No data for this selection",
80
+ xref="paper",
81
+ yref="paper",
82
+ x=0.5,
83
+ y=0.5,
84
+ showarrow=False,
85
+ font_size=18,
86
+ )
87
+ return fig
88
+
89
+ # Keep only the latest run per model
90
+ sub = sub.sort_values("timestamp").groupby("model", as_index=False).last()
91
+ sub["label"] = sub["model_short"]
92
+ sub = sub.sort_values("value", ascending=True)
93
+ value_max = sub["value"].max()
94
+ axis_max = max(1.0, float(value_max) * 1.15) if pd.notna(value_max) else 1.0
95
+
96
+ fig = px.bar(
97
+ sub,
98
+ x="value",
99
+ y="label",
100
+ orientation="h",
101
+ text=sub["value"].map("{:.3f}".format),
102
+ color="value",
103
+ color_continuous_scale="Blues",
104
+ labels={"value": metric, "label": "Model"},
105
+ title=f"{task} β€” {metric} ({suite})",
106
  )
107
+ fig.update_traces(textposition="outside")
108
+ fig.update_coloraxes(showscale=False)
109
+ fig.update_layout(
110
+ height=max(350, len(sub) * 44),
111
+ margin={"l": 220, "r": 60, "t": 60, "b": 40},
112
+ yaxis_title="",
113
+ xaxis_range=[0, axis_max],
114
+ font_size=13,
115
  )
116
+ return fig
117
+
118
+
119
+ def trend_chart(df: pd.DataFrame, suite: str, task: str, metric: str, models: list[str]) -> go.Figure:
120
+ """Score-over-time line chart for selected models."""
121
+ sub = df[
122
+ (df["suite"] == suite)
123
+ & (df["name"] == task)
124
+ & (df["metric"] == metric)
125
+ & (df["model_short"].isin(models))
126
+ ].copy()
127
+ if sub.empty:
128
+ fig = go.Figure()
129
+ fig.add_annotation(
130
+ text="No data for this selection",
131
+ xref="paper",
132
+ yref="paper",
133
+ x=0.5,
134
+ y=0.5,
135
+ showarrow=False,
136
+ font_size=18,
137
+ )
138
+ return fig
139
+
140
+ fig = px.line(
141
+ sub.sort_values("timestamp"),
142
+ x="timestamp",
143
+ y="value",
144
+ color="model_short",
145
+ markers=True,
146
+ labels={"value": metric, "timestamp": "Run time", "model_short": "Model"},
147
+ title=f"{task} β€” {metric} over time",
 
 
 
 
 
 
 
 
 
148
  )
149
+ fig.update_layout(height=420, font_size=13, legend_title="")
150
+ return fig
151
+
152
+
153
+ def summary_table(df: pd.DataFrame, suite: str, metric: str) -> pd.DataFrame:
154
+ """Pivot table: models x tasks, latest run only."""
155
+ sub = df[(df["suite"] == suite) & (df["metric"] == metric)].copy()
156
+ if sub.empty:
157
+ return pd.DataFrame({"(no data)": []})
158
+
159
+ sub = sub.sort_values("timestamp").groupby(["model", "name"], as_index=False).last()
160
+ pivot = sub.pivot(index="model_short", columns="name", values="value")
161
+ pivot = pivot.round(4).reset_index().rename(columns={"model_short": "Model"})
162
+ return pivot
163
+
164
+
165
+ # ── Gradio UI ─────────────────────────────────────────────────────────────────
166
+
167
+
168
+ def build_ui():
169
+ df = load_data()
170
+
171
+ suites = sorted(df["suite"].dropna().unique().tolist()) if not df.empty else ["reasoning"]
172
+ tasks = sorted(df["name"].dropna().unique().tolist()) if not df.empty else []
173
+ metrics = sorted(df["metric"].dropna().unique().tolist()) if not df.empty else []
174
+ models = sorted(df["model"].dropna().unique().tolist()) if not df.empty else []
175
+ model_labels = [short_model(m) for m in models]
176
+
177
+ default_suite = "reasoning" if "reasoning" in suites else (suites[0] if suites else "reasoning")
178
+ default_metric = (
179
+ "exact_match_flexible" if "exact_match_flexible" in metrics else (metrics[0] if metrics else None)
180
+ )
181
+
182
+ def filtered_tasks(suite):
183
+ d = load_data()
184
+ t = sorted(d[d["suite"] == suite]["name"].dropna().unique().tolist()) if not d.empty else []
185
+ return gr.Dropdown(choices=t, value=t[0] if t else None)
186
+
187
+ def filtered_metrics(suite):
188
+ d = load_data()
189
+ m = sorted(d[d["suite"] == suite]["metric"].dropna().unique().tolist()) if not d.empty else []
190
+ return gr.Dropdown(
191
+ choices=m, value="exact_match_flexible" if "exact_match_flexible" in m else (m[0] if m else None)
192
+ )
193
+
194
+ def update_bar(suite, task, metric):
195
+ return bar_chart(load_data(), suite, task, metric)
196
+
197
+ def update_trend(suite, task, metric, selected_models):
198
+ return trend_chart(load_data(), suite, task, metric, selected_models or model_labels)
199
+
200
+ def update_table(suite, metric):
201
+ return summary_table(load_data(), suite, metric)
202
+
203
+ def refresh():
204
+ global _cache
205
+ with _cache_lock:
206
+ _cache = None
207
+ d = load_data()
208
+ new_suites = sorted(d["suite"].dropna().unique().tolist()) if not d.empty else ["reasoning"]
209
+ new_tasks = sorted(d["name"].dropna().unique().tolist()) if not d.empty else []
210
+ new_metrics = sorted(d["metric"].dropna().unique().tolist()) if not d.empty else []
211
+ new_models = sorted(d["model"].dropna().unique().tolist()) if not d.empty else []
212
+ new_model_labels = [short_model(m) for m in new_models]
213
+ return (
214
+ gr.Dropdown(choices=new_suites, value=new_suites[0] if new_suites else None),
215
+ gr.Dropdown(choices=new_tasks, value=new_tasks[0] if new_tasks else None),
216
+ gr.Dropdown(choices=new_metrics, value=new_metrics[0] if new_metrics else None),
217
+ gr.CheckboxGroup(choices=new_model_labels, value=new_model_labels[:6]),
218
+ f"Loaded {len(d)} rows from {len(d['model'].unique()) if not d.empty else 0} models",
219
+ )
220
+
221
+ with gr.Blocks(title="MLX Benchmarks") as demo:
222
+ gr.Markdown("# MLX Benchmarks Viewer", elem_id="title")
223
+ gr.Markdown(
224
+ "Compare local MLX models and cloud endpoints across coding and reasoning benchmarks. \n"
225
+ "Data: [JacobPEvans/mlx-benchmarks](https://huggingface.co/datasets/JacobPEvans/mlx-benchmarks)",
226
+ elem_id="subtitle",
227
+ )
228
+
229
+ with gr.Row():
230
+ suite_dd = gr.Dropdown(choices=suites, value=default_suite, label="Suite")
231
+ task_dd = gr.Dropdown(choices=tasks, value=tasks[0] if tasks else None, label="Task")
232
+ metric_dd = gr.Dropdown(choices=metrics, value=default_metric, label="Metric")
233
+ refresh_btn = gr.Button("↻ Refresh data", scale=0)
234
+
235
+ status = gr.Markdown(
236
+ f"Loaded {len(df)} rows from {df['model'].nunique() if not df.empty else 0} models."
237
+ )
238
+
239
+ with gr.Tabs():
240
+ with gr.Tab("Bar chart β€” latest run"):
241
+ bar_plot = gr.Plot(
242
+ value=bar_chart(df, default_suite, tasks[0] if tasks else "", default_metric)
243
+ )
244
+
245
+ with gr.Tab("Trend β€” over time"):
246
+ model_select = gr.CheckboxGroup(
247
+ choices=model_labels,
248
+ value=model_labels[:6],
249
+ label="Models to show",
250
+ )
251
+ trend_plot = gr.Plot()
252
+
253
+ with gr.Tab("Summary table"):
254
+ table_out = gr.DataFrame(
255
+ value=summary_table(df, default_suite, default_metric),
256
+ interactive=False,
257
+ )
258
+
259
+ # Wire up events
260
+ suite_dd.change(filtered_tasks, [suite_dd], [task_dd])
261
+ suite_dd.change(filtered_metrics, [suite_dd], [metric_dd])
262
+
263
+ for inp in [suite_dd, task_dd, metric_dd]:
264
+ inp.change(update_bar, [suite_dd, task_dd, metric_dd], [bar_plot])
265
+ inp.change(update_table, [suite_dd, metric_dd], [table_out])
266
+
267
+ for inp in [suite_dd, task_dd, metric_dd, model_select]:
268
+ inp.change(update_trend, [suite_dd, task_dd, metric_dd, model_select], [trend_plot])
269
+
270
+ refresh_btn.click(
271
+ refresh,
272
+ outputs=[suite_dd, task_dd, metric_dd, model_select, status],
273
+ )
274
+
275
+ return demo
276
 
277
 
278
+ if __name__ == "__main__":
279
+ build_ui().launch(css=CSS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,16 +1,18 @@
1
- APScheduler
2
- black
3
- datasets
4
- gradio
5
- gradio[oauth]
6
- gradio_leaderboard==0.0.13
7
- gradio_client
8
- huggingface-hub>=0.18.0
9
- matplotlib
10
- numpy
11
- pandas
12
- python-dateutil
13
- tqdm
14
- transformers
15
- tokenizers>=0.15.0
16
- sentencepiece
 
 
 
1
+ gradio>=6.12.0
2
+ pandas>=2.0
3
+ plotly>=6.7.0
4
+ huggingface-hub>=1.0.0
5
+ # Minimum-version pins for direct + transitive deps that have CVEs in
6
+ # older versions. Lower-bound only β€” HF Spaces resolves the actual
7
+ # installed version. Tighten to exact pins if reproducibility becomes
8
+ # a concern.
9
+ # pillow 9.5.0 β†’ 3 CVEs fixed in 10.4.0 (CVE-2024-28219, CVE-2023-50447, CVE-2024-44537)
10
+ # pillow 10.4.0 β†’ GHSA-cfh3-3jmp-rvhc + GHSA-whj4-6x5x-4v2j fixed in 12.2.0
11
+ # pyarrow 14 β†’ PYSEC-2023-238 + PYSEC-2024-161 fixed in 17.0.0
12
+ # pyarrow 17.0.0 β†’ PYSEC-2026-113 (CVSS 7.0 High) fixed in 23.0.1
13
+ # orjson 3.9.9 β†’ GHSA-hx9q-6w63-j58v fixed in 3.11.6
14
+ # idna 3.9.0 β†’ GHSA-65pc-fj4g-8rjx fixed in 3.15 (CVSS 6.9, transitive via requests/httpx)
15
+ pyarrow>=24.0.0
16
+ pillow>=12.2.0
17
+ orjson>=3.11.6
18
+ idna>=3.15