@wendellresearch-ai/benchwarmers 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +147 -147
- package/BENCHMARKS.md +262 -262
- package/GATEWAY.md +272 -272
- package/NOTICE +28 -28
- package/PRIVACY.md +46 -46
- package/README.md +368 -314
- package/SECURITY.md +83 -83
- package/TERMS.md +22 -22
- package/USAGE.md +170 -163
- package/VERSION +1 -1
- package/bin/benchwarmers.js +16 -74
- package/cli.py +666 -0
- package/examples/config/gateway.json +5 -5
- package/examples/config/model-roles.json +11 -11
- package/gateway.py +755 -755
- package/launcher.py +120 -0
- package/mcp_server.py +266 -266
- package/metrics/alerts.py +116 -116
- package/metrics/aliases.py +41 -41
- package/metrics/bench_collections.py +141 -141
- package/metrics/catalog.py +295 -295
- package/metrics/client.py +394 -394
- package/metrics/cliproxy.py +191 -191
- package/metrics/connect.py +691 -691
- package/metrics/cost.py +152 -152
- package/metrics/decide.py +227 -227
- package/metrics/discover.py +318 -318
- package/metrics/endpoints.py +436 -436
- package/metrics/guard.py +617 -592
- package/metrics/learn.py +221 -221
- package/metrics/litellm_hub.py +206 -206
- package/metrics/model_roles.py +84 -84
- package/metrics/offload.py +166 -166
- package/metrics/owui.py +205 -205
- package/metrics/paths.py +60 -60
- package/metrics/route.py +579 -579
- package/metrics/scheduler.py +275 -275
- package/metrics/sources.py +167 -167
- package/metrics/wrong.py +144 -144
- package/package.json +68 -65
- package/perf/README.md +89 -89
- package/perf/run.py +261 -261
- package/scripts/auto_retire.py +367 -367
- package/scripts/check.py +108 -106
- package/scripts/check_deprecations.py +378 -378
- package/scripts/check_themes.py +103 -103
- package/scripts/init.py +124 -124
- package/scripts/list_served.py +78 -78
- package/scripts/notice.py +78 -78
- package/scripts/onboard.py +401 -331
- package/scripts/provider_tags.py +129 -129
- package/scripts/reprefix.py +136 -136
- package/scripts/sync_config.py +105 -105
- package/scripts/test_auth.py +255 -255
- package/scripts/test_cli.py +189 -0
- package/scripts/test_client.py +233 -233
- package/scripts/test_connect.py +222 -222
- package/scripts/test_first_run.py +180 -180
- package/scripts/test_hubs.py +328 -328
- package/scripts/test_launcher.py +67 -0
- package/scripts/test_llm_log.py +85 -81
- package/scripts/test_mcp.py +178 -178
- package/scripts/test_merged.py +214 -214
- package/scripts/test_no_owui.py +291 -291
- package/scripts/test_owui.py +90 -90
- package/scripts/test_serve_edit.py +448 -438
- package/scripts/test_verdict.py +98 -98
- package/scripts/test_vision.py +228 -228
- package/serve.py +3904 -3764
- package/suite/mine.py +154 -154
- package/suite/pace.py +145 -145
- package/suite/run.py +1046 -1042
- package/suite/seed.py +421 -421
- package/suite/tasks/claim-alias-unbound/fixtures/onboard.py +286 -286
- package/suite/tasks/claim-error-text-string/fixtures/decide.py +187 -187
- package/suite/tasks/claim-list-body/fixtures/gateway.py +235 -235
- package/suite/tasks/claim-member-nominated-twice/fixtures/gateway.py +284 -284
- package/suite/tasks/claim-nominee-uses-slot/fixtures/gateway.py +284 -284
- package/suite/tasks/claim-silent-exit/fixtures/gateway.py +235 -235
- package/suite/tasks/claim-sse-hang/fixtures/gateway.py +235 -235
- package/suite/tasks/claim-unknown-nominee-crash/fixtures/gateway.py +284 -284
- package/suite/tasks/rebench-matchms-809-bug/task.json +23 -23
- package/suite/tasks/rebench-matchms-809-control/task.json +27 -27
- package/suite/tasks/rebench-more-itertools-1028-bug/task.json +23 -23
- package/suite/tasks/rebench-more-itertools-1028-control/task.json +27 -27
- package/suite/tasks/rebench-narwhals-2864-bug/task.json +23 -23
- package/suite/tasks/rebench-narwhals-2864-control/task.json +27 -27
- package/suite/tasks/rebench-quantecon-py-769-bug/task.json +23 -23
- package/suite/tasks/rebench-quantecon-py-769-control/task.json +27 -27
- package/suite/tasks/rebench-scrapy-6867-bug/task.json +23 -23
- package/suite/tasks/rebench-scrapy-6867-control/task.json +27 -27
- package/ui/bench.css +430 -425
- package/ui/bench.js +1756 -1576
- package/ui/themes/dark.css +35 -35
- package/ui/themes/hardwood.css +52 -52
- package/ui/themes/rainforest.css +32 -32
package/README.md
CHANGED
|
@@ -1,314 +1,368 @@
|
|
|
1
|
-
<h1 align="center">
|
|
2
|
-
<picture>
|
|
3
|
-
<source media="(prefers-color-scheme: dark)" srcset=".github/logo-dark.svg">
|
|
4
|
-
<img src=".github/logo.svg" width="480" alt="Benchwarmers, by Wendell Research: the name sitting on a crimson team bench">
|
|
5
|
-
</picture>
|
|
6
|
-
</h1>
|
|
7
|
-
|
|
8
|
-
<p align="center">
|
|
9
|
-
<a href="https://github.com/WendellResearch-AI/benchwarmers/releases"><img src="https://img.shields.io/github/v/release/WendellResearch-AI/benchwarmers?color=990000" alt="Latest release"></a>
|
|
10
|
-
<a href="https://www.npmjs.com/package/@wendellresearch-ai/benchwarmers"><img src="https://img.shields.io/npm/v/@wendellresearch-ai/benchwarmers?color=cb3837&logo=npm" alt="npm"></a>
|
|
11
|
-
<a href="https://
|
|
12
|
-
<a href="
|
|
13
|
-
<a href="
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
<a href="#try-it"><img src="https://img.shields.io/badge/
|
|
17
|
-
<a href="#try-it"><img src="https://img.shields.io/badge/
|
|
18
|
-
<a href="
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
</
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- [
|
|
66
|
-
- [
|
|
67
|
-
- [
|
|
68
|
-
- [
|
|
69
|
-
- [
|
|
70
|
-
- [
|
|
71
|
-
- [
|
|
72
|
-
- [
|
|
73
|
-
- [
|
|
74
|
-
- [
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
-d '{"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
- **
|
|
281
|
-
|
|
282
|
-
- **
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
Benchwarmers
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
1
|
+
<h1 align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset=".github/logo-dark.svg">
|
|
4
|
+
<img src=".github/logo.svg" width="480" alt="Benchwarmers, by Wendell Research: the name sitting on a crimson team bench">
|
|
5
|
+
</picture>
|
|
6
|
+
</h1>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://github.com/WendellResearch-AI/benchwarmers/releases"><img src="https://img.shields.io/github/v/release/WendellResearch-AI/benchwarmers?color=990000" alt="Latest release"></a>
|
|
10
|
+
<a href="https://www.npmjs.com/package/@wendellresearch-ai/benchwarmers"><img src="https://img.shields.io/npm/v/@wendellresearch-ai/benchwarmers?color=cb3837&logo=npm" alt="npm"></a>
|
|
11
|
+
<a href="https://pypi.org/project/benchwarmers/"><img src="https://img.shields.io/pypi/v/benchwarmers?color=3775a9&logo=pypi&logoColor=white" alt="PyPI"></a>
|
|
12
|
+
<a href="https://github.com/WendellResearch-AI/benchwarmers/actions/workflows/check.yml"><img src="https://github.com/WendellResearch-AI/benchwarmers/actions/workflows/check.yml/badge.svg" alt="Checks"></a>
|
|
13
|
+
<a href="#docker"><img src="https://img.shields.io/badge/Docker-compose-2496ED?logo=docker&logoColor=white" alt="Runs in Docker"></a>
|
|
14
|
+
<a href="GATEWAY.md"><img src="https://img.shields.io/badge/OpenAI--compatible-gateway-111111" alt="OpenAI-compatible gateway"></a>
|
|
15
|
+
<br>
|
|
16
|
+
<a href="#try-it"><img src="https://img.shields.io/badge/Python-3.11_%7C_3.12_%7C_3.13-3776AB?logo=python&logoColor=white" alt="Python 3.11 to 3.13"></a>
|
|
17
|
+
<a href="#try-it"><img src="https://img.shields.io/badge/core-stdlib_only-2ea44f" alt="The core needs only the Python standard library"></a>
|
|
18
|
+
<a href="#try-it"><img src="https://img.shields.io/badge/runs_on-Windows_%7C_macOS_%7C_Linux-555555" alt="Windows, macOS and Linux"></a>
|
|
19
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license"></a>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<p align="center"><strong><em>Put me in, coach.</em></strong><br>Every model on your bench gets its minutes.</p>
|
|
23
|
+
|
|
24
|
+
Benchwarmers is a self-hosted **panel gateway** for LLMs. Your app asks for one
|
|
25
|
+
of your **collections**, and the whole panel answers: the models you picked
|
|
26
|
+
for that kind of work, plus **explorer** seats that rotate in the models you
|
|
27
|
+
have used least. Every answer comes back side by side, labelled by model and
|
|
28
|
+
seat. You check them against the real work and record a verdict, and those
|
|
29
|
+
verdicts decide who sits on the next panel.
|
|
30
|
+
|
|
31
|
+
**What it is best at:**
|
|
32
|
+
|
|
33
|
+
- **Asking several models at once.** One OpenAI-compatible request, a panel of
|
|
34
|
+
answers. Where they disagree is where to look.
|
|
35
|
+
- **Reviewing.** Code review, claims about code, designs: different models
|
|
36
|
+
catch different things, and two models agreeing is not proof.
|
|
37
|
+
- **Playing to each model's strengths.** A collection per kind of work
|
|
38
|
+
(reviewers, designers, enumerators), each with the models that do it best.
|
|
39
|
+
- **Giving every model playing time.** Explorers cycle through the rest of
|
|
40
|
+
the bench, so new and free models get tried beside the ones you trust,
|
|
41
|
+
without you choosing them by hand.
|
|
42
|
+
- **Benchmarking on your own projects.** A review suite seeded from your own
|
|
43
|
+
repositories, plus your verdicts on real work, instead of public leaderboards.
|
|
44
|
+
- **Knowing who to bench.** Who is confidently wrong, who has stopped
|
|
45
|
+
answering (retired automatically), and who is resting after a rate limit.
|
|
46
|
+
- **Keeping free and cheap models useful.** Health probes, learned rate limits
|
|
47
|
+
and one endpoint for every app, over any OpenAI-compatible provider or hub.
|
|
48
|
+
|
|
49
|
+
| Related tools | Examples | What they do | What Benchwarmers does differently |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| Gateways and routers | LiteLLM, OpenRouter, Portkey | One request to one model, with fallback | Fans one prompt out to a panel and returns every answer |
|
|
52
|
+
| Councils and ensembles | LLM Council, Mixture-of-Agents | Several models merged into one answer | Keeps every answer, because who said what is the evidence; one merged summary only if you ask |
|
|
53
|
+
| Side-by-side chat | Open WebUI, LMArena | Several answers shown together | Records a verdict per answer and uses them to pick the next panel |
|
|
54
|
+
| Evaluation harnesses | promptfoo, Inspect, SWE-bench | Score models offline on fixed test sets | Scores them on your real work and your own code, and keeps rotating new models in |
|
|
55
|
+
|
|
56
|
+
<p align="center">
|
|
57
|
+
<picture>
|
|
58
|
+
<source media="(prefers-color-scheme: dark)" srcset=".github/flow-dark.svg">
|
|
59
|
+
<img src=".github/flow.svg" width="900" alt="Your app or agent asks the gateway for one of your collections. The gateway seats a panel: the collection's members, always seated, and an explorer that rotates in the model unused longest. Every answer comes back side by side, labelled by model and seat. You check them against the real work and record a verdict. Verdicts, daily health probes and the review suite make up the bench's record, which chooses who sits on the next panel.">
|
|
60
|
+
</picture>
|
|
61
|
+
</p>
|
|
62
|
+
|
|
63
|
+
## Contents
|
|
64
|
+
|
|
65
|
+
- [Try it](#try-it)
|
|
66
|
+
- [How it works](#how-it-works)
|
|
67
|
+
- [Asking a panel](#asking-a-panel)
|
|
68
|
+
- [From the command line](#from-the-command-line)
|
|
69
|
+
- [From Claude Code and other agents](#from-claude-code-and-other-agents)
|
|
70
|
+
- [The suite](#the-suite)
|
|
71
|
+
- [Docker](#docker)
|
|
72
|
+
- [What leaves your machine](#what-leaves-your-machine)
|
|
73
|
+
- [Limits](#limits)
|
|
74
|
+
- [Security](#security)
|
|
75
|
+
- [Development](#development)
|
|
76
|
+
- [Licence](#licence)
|
|
77
|
+
|
|
78
|
+
Day-to-day use (the dashboard's pages, connecting apps, onboarding in bulk) is
|
|
79
|
+
in [USAGE.md](USAGE.md). A coding agent setting it up for someone should start
|
|
80
|
+
with [AGENTS.md](AGENTS.md).
|
|
81
|
+
|
|
82
|
+
## Try it
|
|
83
|
+
|
|
84
|
+
**v0.2, early.** It runs with Python 3.11 or later on Windows, macOS and
|
|
85
|
+
Linux, from PyPI, npm, a checkout or Docker. Expect rough edges, and read the
|
|
86
|
+
code where these pages are thin.
|
|
87
|
+
|
|
88
|
+
**Quickest**, with Python 3.11+ installed, from PyPI:
|
|
89
|
+
|
|
90
|
+
pipx install benchwarmers # or: uv tool install benchwarmers
|
|
91
|
+
benchwarmers # or try it first, without installing: uvx benchwarmers
|
|
92
|
+
|
|
93
|
+
or from npm, with Node 18+ as well:
|
|
94
|
+
|
|
95
|
+
npx @wendellresearch-ai/benchwarmers
|
|
96
|
+
|
|
97
|
+
Either way you get the same two commands, `benchwarmers` and its short name
|
|
98
|
+
`bench`. It keeps your data in your user profile (`BENCHWARMERS_HOME` to
|
|
99
|
+
choose another folder), sets it up on the first run, and opens the dashboard
|
|
100
|
+
on http://localhost:8122, where steps 3 to 5 below continue. The built-in
|
|
101
|
+
model client makes every call, so nothing else needs installing. No
|
|
102
|
+
dependencies either way: the app is the Python standard library.
|
|
103
|
+
|
|
104
|
+
**From source:**
|
|
105
|
+
|
|
106
|
+
1. **Get the code** and a Python 3.11+ environment:
|
|
107
|
+
|
|
108
|
+
git clone https://github.com/WendellResearch-AI/benchwarmers
|
|
109
|
+
cd benchwarmers
|
|
110
|
+
python -m venv .venv
|
|
111
|
+
.venv/bin/pip install llm # Windows: .venv\Scripts\pip install llm
|
|
112
|
+
|
|
113
|
+
The [`llm`](https://llm.datasette.io/) CLI is the default model client today.
|
|
114
|
+
The built-in client (`metrics/client.py`) replaces it, chosen on Settings
|
|
115
|
+
or with `BENCH_CLIENT=builtin` (which wins), and will become the default.
|
|
116
|
+
|
|
117
|
+
2. **Make a data folder.** Everything that is yours (endpoints, model
|
|
118
|
+
registrations, collections, verdicts, probe history, task answers) lives
|
|
119
|
+
there, never in the code:
|
|
120
|
+
|
|
121
|
+
python scripts/init.py # or: python scripts/init.py <folder>
|
|
122
|
+
|
|
123
|
+
It starts the folder from `examples/` (in your OS's per-user data folder
|
|
124
|
+
unless you name one) and points the checkout at it with a
|
|
125
|
+
`.benchwarmers-home` file; `BENCHWARMERS_HOME` overrides that. With
|
|
126
|
+
`--endpoint NAME BASE_URL --key` it also adds a first endpoint and asks for
|
|
127
|
+
its key, which goes to the `llm` CLI's key store (`llm keys set NAME` does
|
|
128
|
+
the same), never into the data folder.
|
|
129
|
+
|
|
130
|
+
3. **Start the dashboard.** With no endpoint yet it opens on adding one: sign
|
|
131
|
+
in with a provider on its own page (OpenRouter, NanoGPT, Hugging Face,
|
|
132
|
+
Pollinations, your ChatGPT plan, GitHub Models, or a local Ollama), or paste
|
|
133
|
+
an API key for any OpenAI-compatible API, a hub such as Open WebUI or
|
|
134
|
+
LiteLLM included. Only providers that publish a sign-in for other apps are
|
|
135
|
+
offered; adding one is an entry in `metrics/connect.py` (its docstring says
|
|
136
|
+
how).
|
|
137
|
+
|
|
138
|
+
python serve.py # http://localhost:8122
|
|
139
|
+
|
|
140
|
+
4. **Onboard a few models** from it ("Onboard a model"): tick one or several;
|
|
141
|
+
each is registered, probed and run through the suite, one at a time, with
|
|
142
|
+
the progress beside the button.
|
|
143
|
+
|
|
144
|
+
5. **Make a collection** on the Collections page (the models a panel always
|
|
145
|
+
includes, and what it is for). The dashboard answers the gateway's routes on
|
|
146
|
+
its own port (`http://127.0.0.1:8122/v1`); `python gateway.py` runs the
|
|
147
|
+
gateway alone on `http://127.0.0.1:8123/v1`.
|
|
148
|
+
|
|
149
|
+
## How it works
|
|
150
|
+
|
|
151
|
+
- **Collections and seats.** A collection names the models a panel always
|
|
152
|
+
includes and the shape of work it is for (review, design, enumeration...).
|
|
153
|
+
Each call adds explorer seats chosen by how long a model has gone unused, so
|
|
154
|
+
the bench keeps learning about models a router would stop asking.
|
|
155
|
+
- **Verdicts.** Each seat's answer carries a call id. Once you have checked an
|
|
156
|
+
answer against the real work, record whether it was useful, and which of its
|
|
157
|
+
claims were real or false. Verdicts, not vibes, decide the next panel; a
|
|
158
|
+
model that is often confidently wrong shows as that.
|
|
159
|
+
- **The catalog.** Context window, output limit, tools, vision and price come
|
|
160
|
+
from models.dev for the provider that actually serves a model, then
|
|
161
|
+
OpenRouter's public catalog and FreeLLMAPI, and anything you set by hand.
|
|
162
|
+
- **Health.** Every morning each model gets a one-word prompt; once a week, a
|
|
163
|
+
short and a long one for speed. A model that stops answering, or that its
|
|
164
|
+
endpoint no longer serves, is retired automatically after one confirming
|
|
165
|
+
call, with the evidence kept, and can be restored in one click. Rate limits
|
|
166
|
+
are learned from providers' own error messages and kept per connection,
|
|
167
|
+
since models behind one key share them. The built-in scheduler runs all of
|
|
168
|
+
this once Settings switches it on; a fresh install spends no calls until then.
|
|
169
|
+
- **Discover.** models.dev and FreeLLMAPI, merged into one list of models
|
|
170
|
+
worth adding, each with the routes this install has to it.
|
|
171
|
+
|
|
172
|
+
## Asking a panel
|
|
173
|
+
|
|
174
|
+
Any OpenAI-compatible client works: ask for the model `bench/<collection>`.
|
|
175
|
+
|
|
176
|
+
curl http://127.0.0.1:8122/v1/chat/completions \
|
|
177
|
+
-H "Content-Type: application/json" \
|
|
178
|
+
-d '{"model": "bench/<collection>", "messages": [{"role": "user", "content": "Where can this function return a stale value? ..."}]}'
|
|
179
|
+
|
|
180
|
+
The reply carries every seat's answer in the message, and the same seats as
|
|
181
|
+
data in a `bench` field: model, seat, status, seconds and a call id. For a chat
|
|
182
|
+
window that shows one answer, ask for `bench/<collection>:merged`: a merger
|
|
183
|
+
model writes one answer saying where the seats agreed and disagreed, and every
|
|
184
|
+
seat is still in `bench`. Record a verdict per call once you have checked it:
|
|
185
|
+
|
|
186
|
+
curl http://127.0.0.1:8122/bench/verdict -H "Content-Type: application/json" \
|
|
187
|
+
-d '{"call": "<call id>", "useful": true, "note": "found the real bug", "real": ["..."], "false": ["..."]}'
|
|
188
|
+
|
|
189
|
+
or from the dashboard. See [GATEWAY.md](GATEWAY.md) for the full contract:
|
|
190
|
+
images, prompt sizes, who may call, and the reply's shape.
|
|
191
|
+
|
|
192
|
+
## From the command line
|
|
193
|
+
|
|
194
|
+
`bench` (or `benchwarmers`, the same program) does what the dashboard does,
|
|
195
|
+
without it: ask and judge, and run the bench itself. It is a client of a
|
|
196
|
+
running server, so the same commands work against one on another machine.
|
|
197
|
+
|
|
198
|
+
bench status what is running, and where
|
|
199
|
+
bench collections what you can ask
|
|
200
|
+
bench ask core "Where can this return a stale value?" -f cache.py
|
|
201
|
+
git diff | bench ask core "Review this diff" a pipe is the prompt
|
|
202
|
+
bench verdict <call id> --useful --real "the race in refresh()" --false "the SQL injection"
|
|
203
|
+
|
|
204
|
+
bench models --find mercury the roster, with each model's status
|
|
205
|
+
bench candidates --new what your endpoints serve that is not on the bench
|
|
206
|
+
bench onboard <served name> --wait onboard it (registered, probed, scored)
|
|
207
|
+
bench model retire <alias> --reason "..." and back: bench model restore <alias>
|
|
208
|
+
bench collection save reviewers --members a,b,c --description "adversarial review"
|
|
209
|
+
bench collection save reviewers --add d --remove a
|
|
210
|
+
bench endpoint add openrouter --base https://openrouter.ai/api/v1 (asks for the key, hidden)
|
|
211
|
+
bench key create my-app --collections core --rpm 30
|
|
212
|
+
bench settings scheduler run --off perf
|
|
213
|
+
|
|
214
|
+
`--help` on any command lists its options with examples, and `--json` prints
|
|
215
|
+
what the server answered, for scripts. `--url` points one command at another
|
|
216
|
+
server. `ask` prints every seat's answer on stdout and the table of call ids to
|
|
217
|
+
judge them by on stderr, so `bench ask core "..." > review.md` keeps only the
|
|
218
|
+
answers. Retiring a model, deleting a collection or an endpoint and revoking a
|
|
219
|
+
key ask first; `--yes` answers for a script. A key is never an argument (others
|
|
220
|
+
on the machine can read those): `endpoint add` prompts for it, or reads it
|
|
221
|
+
from stdin with `--key-stdin`.
|
|
222
|
+
|
|
223
|
+
Another program called `bench` (the Frappe framework's) may come first on your
|
|
224
|
+
PATH; type `benchwarmers` instead, which takes the same commands.
|
|
225
|
+
|
|
226
|
+
| Variable | Meaning |
|
|
227
|
+
| --- | --- |
|
|
228
|
+
| `BENCHWARMERS_URL` | the server, default `http://127.0.0.1:8122` |
|
|
229
|
+
| `BENCHWARMERS_ADMIN_TOKEN` | the server's admin token, where it has one; sent as `Authorization: Bearer` for every command but `ask`, `collections` and `verdict` |
|
|
230
|
+
| `BENCHWARMERS_KEY` | a gateway key, for `ask`, `collections` and `verdict`, where keys are required |
|
|
231
|
+
| `BENCHWARMERS_TIMEOUT` | seconds to wait for a panel, default 900 |
|
|
232
|
+
|
|
233
|
+
Exit codes: 0 done, 1 refused or failed (the reason on stderr), 2 bad usage, 3
|
|
234
|
+
no server at `BENCHWARMERS_URL`.
|
|
235
|
+
|
|
236
|
+
## From Claude Code and other agents
|
|
237
|
+
|
|
238
|
+
`benchwarmers mcp` is an MCP server on stdio with three tools:
|
|
239
|
+
`list_collections`, `ask` (a collection, a prompt, optionally `merged`,
|
|
240
|
+
`with` and `system`) and `verdict`. It is a client of a Benchwarmers that is
|
|
241
|
+
already running, over the routes above, so start the dashboard (or the
|
|
242
|
+
service) first. The tool descriptions carry the rules: ask a collection, not
|
|
243
|
+
a favourite model; verify before acting; name every seat; send a verdict per
|
|
244
|
+
seat you judged.
|
|
245
|
+
|
|
246
|
+
**Claude Code plugin.** The plugin adds the MCP server, a `/benchwarmers:ask
|
|
247
|
+
<collection> <what to ask>` command and a `benchwarmers` skill saying when a
|
|
248
|
+
panel helps (adversarial review, exhaustive enumeration over real files) and
|
|
249
|
+
how to judge it:
|
|
250
|
+
|
|
251
|
+
/plugin marketplace add WendellResearch-AI/benchwarmers
|
|
252
|
+
/plugin install benchwarmers@benchwarmers
|
|
253
|
+
|
|
254
|
+
It needs Node 18 or later and Python 3.11 or later on PATH.
|
|
255
|
+
|
|
256
|
+
**Any other MCP client** (or Claude Code without the plugin): run
|
|
257
|
+
`benchwarmers mcp` once installed, `uvx benchwarmers mcp` or
|
|
258
|
+
`npx -y @wendellresearch-ai/benchwarmers mcp` without installing, or `python mcp_server.py` from a
|
|
259
|
+
checkout. For example:
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
{"mcpServers": {"benchwarmers": {"command": "npx", "args": ["-y", "@wendellresearch-ai/benchwarmers", "mcp"],
|
|
263
|
+
"env": {"BENCHWARMERS_URL": "http://127.0.0.1:8122"}}}}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
On native Windows, a client that cannot start `npx` itself needs
|
|
267
|
+
`"command": "cmd", "args": ["/c", "npx", "-y", "@wendellresearch-ai/benchwarmers", "mcp"]`.
|
|
268
|
+
|
|
269
|
+
| Variable | Meaning |
|
|
270
|
+
| --- | --- |
|
|
271
|
+
| `BENCHWARMERS_URL` | the server, default `http://127.0.0.1:8122` (a `/v1` on the end is fine) |
|
|
272
|
+
| `BENCHWARMERS_KEY` | a gateway key from Settings, sent as `Authorization: Bearer`; needed for a server on another machine, or where keys are required |
|
|
273
|
+
| `BENCHWARMERS_TIMEOUT` | seconds to wait for a panel, default 900 |
|
|
274
|
+
|
|
275
|
+
## The suite
|
|
276
|
+
|
|
277
|
+
Models are scored on review work: find the faulty line, or say whether a
|
|
278
|
+
claim about some code is true. The repository ships a starter pack:
|
|
279
|
+
|
|
280
|
+
- **Real bugs** (`suite/tasks/rebench-*`): recent fixes from public projects,
|
|
281
|
+
drawn from the SWE-rebench-V2 dataset, each paired with a control.
|
|
282
|
+
- **Claims** (`suite/tasks/claim-*`): true and false statements about this
|
|
283
|
+
project's own code, with the code attached.
|
|
284
|
+
|
|
285
|
+
Add your own from your repositories (`config/repos.json` in the data folder
|
|
286
|
+
maps a name to a folder): `python suite/seed.py --repo <name>` plants
|
|
287
|
+
realistic bugs in your real functions, each with a clean control. Private
|
|
288
|
+
code makes the best tasks, because no model has trained on it. See
|
|
289
|
+
[suite/README.md](suite/README.md), [suite/REBENCH.md](suite/REBENCH.md) and
|
|
290
|
+
[BENCHMARKS.md](BENCHMARKS.md).
|
|
291
|
+
|
|
292
|
+
## Docker
|
|
293
|
+
|
|
294
|
+
One container runs the dashboard, the gateway (on the same port, at `/v1`)
|
|
295
|
+
and the scheduler, with the data folder as a volume and no `llm` CLI (the
|
|
296
|
+
built-in client makes every call). Put the secrets in a `.env` file beside
|
|
297
|
+
`compose.yaml`:
|
|
298
|
+
|
|
299
|
+
BENCHWARMERS_ADMIN_TOKEN=<16 or more characters>
|
|
300
|
+
BENCHWARMERS_KEY_OPENROUTER=sk-or-...
|
|
301
|
+
|
|
302
|
+
then `docker compose up -d` and open http://127.0.0.1:8122. The compose file
|
|
303
|
+
publishes on this machine only; see its comments for a proxy.
|
|
304
|
+
|
|
305
|
+
## What leaves your machine
|
|
306
|
+
|
|
307
|
+
Benchwarmers has no account, no telemetry and no server of its own; nothing
|
|
308
|
+
reaches Wendell Research. What it sends, and where:
|
|
309
|
+
|
|
310
|
+
- **Your prompts**, only to the endpoints you add: a provider, an aggregator,
|
|
311
|
+
a hub such as Open WebUI, or a local runtime. That includes the probes, the
|
|
312
|
+
suite and onboarding, which spend calls on your keys. Add only endpoints you
|
|
313
|
+
are content to send that work to.
|
|
314
|
+
- **Catalog reads**, no key and nothing of yours: models.dev and FreeLLMAPI
|
|
315
|
+
once a day, and OpenRouter's public model list.
|
|
316
|
+
- **Sign-ins**, only when you choose one: the provider's own sign-in page.
|
|
317
|
+
- **Charts**: the dashboard's browser page loads Observable Plot from jsDelivr
|
|
318
|
+
at a pinned version.
|
|
319
|
+
- **Seeding real-bug tasks**, only when you run `suite/rebench.py`: the
|
|
320
|
+
SWE-rebench-V2 rows from Hugging Face and the files from GitHub.
|
|
321
|
+
|
|
322
|
+
Keys stay in the environment or the `llm` CLI's key store, never in the data
|
|
323
|
+
folder. [PRIVACY.md](PRIVACY.md) and [SECURITY.md](SECURITY.md) have the
|
|
324
|
+
detail.
|
|
325
|
+
|
|
326
|
+
## Limits
|
|
327
|
+
|
|
328
|
+
- **Early.** v0.1: no installer yet, no streaming replies, and the LiteLLM and
|
|
329
|
+
CLI Proxy API hub adapters are built from those projects' source, not yet
|
|
330
|
+
run against a real hub.
|
|
331
|
+
- **Calls cost.** Probes, onboarding and the suite run on your keys and free
|
|
332
|
+
tiers. Onboarding runs one model at a time for that reason; a suite pass is
|
|
333
|
+
paced, not burst.
|
|
334
|
+
- **One install, one person** at heart. Logins and gateway keys exist for a
|
|
335
|
+
LAN or a server, but there are no per-user views or roles.
|
|
336
|
+
- **The onboarding queue lives in the dashboard's memory.** Restarting it
|
|
337
|
+
drops the rest of a batch; each started model's log stays in `runs/`.
|
|
338
|
+
- **Free tiers change without notice.** Discover's prices come from public
|
|
339
|
+
catalogs, and some providers' free models work only in their own app
|
|
340
|
+
(switched per provider on Settings).
|
|
341
|
+
|
|
342
|
+
## Security
|
|
343
|
+
|
|
344
|
+
Both servers listen on 127.0.0.1 by default and accept only same-origin writes
|
|
345
|
+
for their own host name, so a web page in your browser cannot drive them. To
|
|
346
|
+
listen anywhere else, set `BENCHWARMERS_ADMIN_TOKEN` first; without it they
|
|
347
|
+
refuse to start. With it, the dashboard asks for a login and the gateway takes
|
|
348
|
+
named, revocable **gateway keys**. Settings, under Server and access, shows how
|
|
349
|
+
this stands (whether a secret is set, never its value).
|
|
350
|
+
|
|
351
|
+
[SECURITY.md](SECURITY.md) has the whole model and how to report a
|
|
352
|
+
vulnerability.
|
|
353
|
+
|
|
354
|
+
## Development
|
|
355
|
+
|
|
356
|
+
python scripts/check.py # every self-check and test (--quick skips the slow one)
|
|
357
|
+
|
|
358
|
+
The core is the Python standard library only. CI runs the checks on Linux and
|
|
359
|
+
Windows for Python 3.11 to 3.13, and on macOS.
|
|
360
|
+
|
|
361
|
+
Benchwarmers isn't taking outside contributions yet; that opens once it is
|
|
362
|
+
more established. A security problem goes through [SECURITY.md](SECURITY.md).
|
|
363
|
+
|
|
364
|
+
## Licence
|
|
365
|
+
|
|
366
|
+
MIT, Copyright (c) 2026 Wendell Research ([LICENSE](LICENSE)). The starter
|
|
367
|
+
tasks and bundled fonts carry their own licences, listed in [NOTICE](NOTICE).
|
|
368
|
+
Use is also covered by [TERMS.md](TERMS.md).
|