@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/package.json
CHANGED
|
@@ -1,65 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@wendellresearch-ai/benchwarmers",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Self-hosted panel gateway for LLMs: ask a collection of models at once, keep every answer side by side, rotate explorer models in, and let your verdicts pick the next panel.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Wendell Research",
|
|
7
|
-
"homepage": "https://github.com/WendellResearch-AI/benchwarmers#readme",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/WendellResearch-AI/benchwarmers.git"
|
|
11
|
-
},
|
|
12
|
-
"bugs": {
|
|
13
|
-
"url": "https://github.com/WendellResearch-AI/benchwarmers/issues"
|
|
14
|
-
},
|
|
15
|
-
"keywords": [
|
|
16
|
-
"llm",
|
|
17
|
-
"llm-gateway",
|
|
18
|
-
"openai-compatible",
|
|
19
|
-
"multi-model",
|
|
20
|
-
"code-review",
|
|
21
|
-
"llm-evaluation",
|
|
22
|
-
"benchmark",
|
|
23
|
-
"model-routing",
|
|
24
|
-
"self-hosted",
|
|
25
|
-
"open-webui",
|
|
26
|
-
"openrouter",
|
|
27
|
-
"litellm"
|
|
28
|
-
],
|
|
29
|
-
"bin": {
|
|
30
|
-
"benchwarmers": "bin/benchwarmers.js"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"suite
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
}
|
|
65
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@wendellresearch-ai/benchwarmers",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Self-hosted panel gateway for LLMs: ask a collection of models at once, keep every answer side by side, rotate explorer models in, and let your verdicts pick the next panel.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Wendell Research",
|
|
7
|
+
"homepage": "https://github.com/WendellResearch-AI/benchwarmers#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/WendellResearch-AI/benchwarmers.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/WendellResearch-AI/benchwarmers/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"llm",
|
|
17
|
+
"llm-gateway",
|
|
18
|
+
"openai-compatible",
|
|
19
|
+
"multi-model",
|
|
20
|
+
"code-review",
|
|
21
|
+
"llm-evaluation",
|
|
22
|
+
"benchmark",
|
|
23
|
+
"model-routing",
|
|
24
|
+
"self-hosted",
|
|
25
|
+
"open-webui",
|
|
26
|
+
"openrouter",
|
|
27
|
+
"litellm"
|
|
28
|
+
],
|
|
29
|
+
"bin": {
|
|
30
|
+
"benchwarmers": "bin/benchwarmers.js",
|
|
31
|
+
"bench": "bin/benchwarmers.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"bin/",
|
|
35
|
+
"launcher.py",
|
|
36
|
+
"serve.py",
|
|
37
|
+
"gateway.py",
|
|
38
|
+
"mcp_server.py",
|
|
39
|
+
"cli.py",
|
|
40
|
+
"metrics/*.py",
|
|
41
|
+
"scripts/*.py",
|
|
42
|
+
"suite/*.py",
|
|
43
|
+
"suite/*.md",
|
|
44
|
+
"suite/*.json",
|
|
45
|
+
"suite/tasks/",
|
|
46
|
+
"perf/*.py",
|
|
47
|
+
"perf/*.md",
|
|
48
|
+
"repoqa/*.py",
|
|
49
|
+
"repoqa/*.md",
|
|
50
|
+
"ui/",
|
|
51
|
+
"examples/",
|
|
52
|
+
"VERSION",
|
|
53
|
+
"NOTICE",
|
|
54
|
+
"AGENTS.md",
|
|
55
|
+
"BENCHMARKS.md",
|
|
56
|
+
"GATEWAY.md",
|
|
57
|
+
"PRIVACY.md",
|
|
58
|
+
"SECURITY.md",
|
|
59
|
+
"TERMS.md",
|
|
60
|
+
"USAGE.md"
|
|
61
|
+
],
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=18"
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public"
|
|
67
|
+
}
|
|
68
|
+
}
|
package/perf/README.md
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
# The performance tier
|
|
2
|
-
|
|
3
|
-
What the **serving path** does, tracked over time. Not what the models are
|
|
4
|
-
worth — that is `suite/`.
|
|
5
|
-
|
|
6
|
-
Latency through Open WebUI to whichever provider is behind it depends on
|
|
7
|
-
congestion, rate limits, routing and the hour of the day. None of those are
|
|
8
|
-
properties of a model, and all of them decide whether an offload is worth
|
|
9
|
-
making right now.
|
|
10
|
-
|
|
11
|
-
## One reading is weather; the history is climate
|
|
12
|
-
|
|
13
|
-
Every run appends to `history.csv` rather than overwriting a report. A single
|
|
14
|
-
number here is close to meaningless — the first run recorded a p95 of 44.5 s
|
|
15
|
-
against a p50 of 11.6 s on the *same* prompt, entirely from one cold first
|
|
16
|
-
call. A p50 that doubles over a month is a real finding. A p50 that is 40%
|
|
17
|
-
higher this afternoon is a Tuesday.
|
|
18
|
-
|
|
19
|
-
The runner prints a `(+N% vs <date>)` marker against the previous reading, and
|
|
20
|
-
says `(steady)` inside ±15%, so the comparison is made for you rather than left
|
|
21
|
-
in a spreadsheet.
|
|
22
|
-
|
|
23
|
-
## What is recorded, and why each
|
|
24
|
-
|
|
25
|
-
| Column | Why |
|
|
26
|
-
| --- | --- |
|
|
27
|
-
| `p50_s`, `p95_s` | **Not the mean.** One timeout drags a mean into fiction. p95 is where rate limiting shows itself, and rate limiting is what makes a model unusable while looking fine on average |
|
|
28
|
-
| `chars_per_s` | Throughput, from the **large prompt only** |
|
|
29
|
-
| `answered` / `refused` / `error` / `timeout` | Four columns, never one. A safety filter, an overloaded service and a slow model are three different facts, and collapsing them is the mistake this repo was built around |
|
|
30
|
-
| `timestamp` | The whole point is the trend |
|
|
31
|
-
|
|
32
|
-
**Two prompt sizes, because they measure different things.** The tiny prompt
|
|
33
|
-
asks for one word and measures the FLOOR — connection, queueing, cold start.
|
|
34
|
-
The large one is 18 KB and measures THROUGHPUT. The first run made the case
|
|
35
|
-
better than the design did: the 18 KB prompt came back in 9.0 s and the
|
|
36
|
-
one-word prompt took 11.6 s. Latency to these models is overhead, not
|
|
37
|
-
generation.
|
|
38
|
-
|
|
39
|
-
**Throughput is not recorded for the tiny prompt.** Six characters over ten
|
|
40
|
-
seconds is 0.5 ch/s, which reads as a catastrophically slow model and is
|
|
41
|
-
really connection overhead with a one-word answer attached. A number that
|
|
42
|
-
invites the wrong reading is worse than no number.
|
|
43
|
-
|
|
44
|
-
**Characters, not tokens.** The `llm` CLI does not report token counts for
|
|
45
|
-
these endpoints. Chars/s is a proxy, it is affected by how verbose a model is,
|
|
46
|
-
and saying so is better than publishing something that looks like tokens/s and
|
|
47
|
-
is not.
|
|
48
|
-
|
|
49
|
-
## Running it
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
# All default models, both prompt sizes, 5 samples each.
|
|
53
|
-
.venv/Scripts/python.exe perf/run.py
|
|
54
|
-
|
|
55
|
-
# One model, quick availability check.
|
|
56
|
-
.venv/Scripts/python.exe perf/run.py --model gemini-flash-3.8 -n 3 --tiny-only
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Worth running on a schedule — weekly is enough to see drift — and again
|
|
60
|
-
whenever a model starts feeling slow, so the feeling can be checked against a
|
|
61
|
-
number.
|
|
62
|
-
|
|
63
|
-
## One caveat: not every model takes the same path
|
|
64
|
-
|
|
65
|
-
`inkling-free` and `inkling-small-free` are called **directly at OpenRouter**,
|
|
66
|
-
because Open WebUI cannot relay them (see `runs/2026-09-04-registry-second-pass.md`).
|
|
67
|
-
Everything else goes through Open WebUI first.
|
|
68
|
-
|
|
69
|
-
That is one fewer hop, and it shows: `inkling-small-free` measured **245 ch/s**
|
|
70
|
-
against 88.7 for the next fastest. **Part of that gap is the path, not the
|
|
71
|
-
model** — which is exactly what this tier is named for. Do not read it as a
|
|
72
|
-
threefold quality difference; read it as the relay costing something.
|
|
73
|
-
|
|
74
|
-
The right comparison for these two is against each other, or against their own
|
|
75
|
-
history over time.
|
|
76
|
-
|
|
77
|
-
## How much this should count
|
|
78
|
-
|
|
79
|
-
**Least of the three tiers.** `suite/` decides which model to use, `floor/`
|
|
80
|
-
says whether one is broken, and this is a tiebreaker plus a veto for the
|
|
81
|
-
extreme cases. Differences of seconds are a wash — waiting is cheap and a
|
|
82
|
-
confident wrong answer is not. Speed only overrules capability when a model is
|
|
83
|
-
slow enough to change how you work.
|
|
84
|
-
|
|
85
|
-
## What this cannot tell you
|
|
86
|
-
|
|
87
|
-
Whether a model is any good. The fastest model here can still invent an API
|
|
88
|
-
that does not exist. That is what `suite/` is for, and the two should never be
|
|
89
|
-
read as one ranking.
|
|
1
|
+
# The performance tier
|
|
2
|
+
|
|
3
|
+
What the **serving path** does, tracked over time. Not what the models are
|
|
4
|
+
worth — that is `suite/`.
|
|
5
|
+
|
|
6
|
+
Latency through Open WebUI to whichever provider is behind it depends on
|
|
7
|
+
congestion, rate limits, routing and the hour of the day. None of those are
|
|
8
|
+
properties of a model, and all of them decide whether an offload is worth
|
|
9
|
+
making right now.
|
|
10
|
+
|
|
11
|
+
## One reading is weather; the history is climate
|
|
12
|
+
|
|
13
|
+
Every run appends to `history.csv` rather than overwriting a report. A single
|
|
14
|
+
number here is close to meaningless — the first run recorded a p95 of 44.5 s
|
|
15
|
+
against a p50 of 11.6 s on the *same* prompt, entirely from one cold first
|
|
16
|
+
call. A p50 that doubles over a month is a real finding. A p50 that is 40%
|
|
17
|
+
higher this afternoon is a Tuesday.
|
|
18
|
+
|
|
19
|
+
The runner prints a `(+N% vs <date>)` marker against the previous reading, and
|
|
20
|
+
says `(steady)` inside ±15%, so the comparison is made for you rather than left
|
|
21
|
+
in a spreadsheet.
|
|
22
|
+
|
|
23
|
+
## What is recorded, and why each
|
|
24
|
+
|
|
25
|
+
| Column | Why |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| `p50_s`, `p95_s` | **Not the mean.** One timeout drags a mean into fiction. p95 is where rate limiting shows itself, and rate limiting is what makes a model unusable while looking fine on average |
|
|
28
|
+
| `chars_per_s` | Throughput, from the **large prompt only** |
|
|
29
|
+
| `answered` / `refused` / `error` / `timeout` | Four columns, never one. A safety filter, an overloaded service and a slow model are three different facts, and collapsing them is the mistake this repo was built around |
|
|
30
|
+
| `timestamp` | The whole point is the trend |
|
|
31
|
+
|
|
32
|
+
**Two prompt sizes, because they measure different things.** The tiny prompt
|
|
33
|
+
asks for one word and measures the FLOOR — connection, queueing, cold start.
|
|
34
|
+
The large one is 18 KB and measures THROUGHPUT. The first run made the case
|
|
35
|
+
better than the design did: the 18 KB prompt came back in 9.0 s and the
|
|
36
|
+
one-word prompt took 11.6 s. Latency to these models is overhead, not
|
|
37
|
+
generation.
|
|
38
|
+
|
|
39
|
+
**Throughput is not recorded for the tiny prompt.** Six characters over ten
|
|
40
|
+
seconds is 0.5 ch/s, which reads as a catastrophically slow model and is
|
|
41
|
+
really connection overhead with a one-word answer attached. A number that
|
|
42
|
+
invites the wrong reading is worse than no number.
|
|
43
|
+
|
|
44
|
+
**Characters, not tokens.** The `llm` CLI does not report token counts for
|
|
45
|
+
these endpoints. Chars/s is a proxy, it is affected by how verbose a model is,
|
|
46
|
+
and saying so is better than publishing something that looks like tokens/s and
|
|
47
|
+
is not.
|
|
48
|
+
|
|
49
|
+
## Running it
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# All default models, both prompt sizes, 5 samples each.
|
|
53
|
+
.venv/Scripts/python.exe perf/run.py
|
|
54
|
+
|
|
55
|
+
# One model, quick availability check.
|
|
56
|
+
.venv/Scripts/python.exe perf/run.py --model gemini-flash-3.8 -n 3 --tiny-only
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Worth running on a schedule — weekly is enough to see drift — and again
|
|
60
|
+
whenever a model starts feeling slow, so the feeling can be checked against a
|
|
61
|
+
number.
|
|
62
|
+
|
|
63
|
+
## One caveat: not every model takes the same path
|
|
64
|
+
|
|
65
|
+
`inkling-free` and `inkling-small-free` are called **directly at OpenRouter**,
|
|
66
|
+
because Open WebUI cannot relay them (see `runs/2026-09-04-registry-second-pass.md`).
|
|
67
|
+
Everything else goes through Open WebUI first.
|
|
68
|
+
|
|
69
|
+
That is one fewer hop, and it shows: `inkling-small-free` measured **245 ch/s**
|
|
70
|
+
against 88.7 for the next fastest. **Part of that gap is the path, not the
|
|
71
|
+
model** — which is exactly what this tier is named for. Do not read it as a
|
|
72
|
+
threefold quality difference; read it as the relay costing something.
|
|
73
|
+
|
|
74
|
+
The right comparison for these two is against each other, or against their own
|
|
75
|
+
history over time.
|
|
76
|
+
|
|
77
|
+
## How much this should count
|
|
78
|
+
|
|
79
|
+
**Least of the three tiers.** `suite/` decides which model to use, `floor/`
|
|
80
|
+
says whether one is broken, and this is a tiebreaker plus a veto for the
|
|
81
|
+
extreme cases. Differences of seconds are a wash — waiting is cheap and a
|
|
82
|
+
confident wrong answer is not. Speed only overrules capability when a model is
|
|
83
|
+
slow enough to change how you work.
|
|
84
|
+
|
|
85
|
+
## What this cannot tell you
|
|
86
|
+
|
|
87
|
+
Whether a model is any good. The fastest model here can still invent an API
|
|
88
|
+
that does not exist. That is what `suite/` is for, and the two should never be
|
|
89
|
+
read as one ranking.
|