@wendellresearch-ai/benchwarmers 0.2.0 → 0.2.2

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.
Files changed (103) hide show
  1. package/AGENTS.md +152 -147
  2. package/BENCHMARKS.md +262 -262
  3. package/GATEWAY.md +415 -272
  4. package/NOTICE +28 -28
  5. package/PRIVACY.md +46 -46
  6. package/README.md +408 -314
  7. package/SECURITY.md +83 -83
  8. package/TERMS.md +22 -22
  9. package/USAGE.md +186 -163
  10. package/VERSION +1 -1
  11. package/bin/benchwarmers.js +16 -74
  12. package/cli.py +674 -0
  13. package/examples/config/gateway.json +5 -5
  14. package/examples/config/model-roles.json +11 -11
  15. package/gateway.py +939 -755
  16. package/launcher.py +120 -0
  17. package/mcp_server.py +266 -266
  18. package/metrics/alerts.py +116 -116
  19. package/metrics/aliases.py +41 -41
  20. package/metrics/bench_collections.py +141 -141
  21. package/metrics/catalog.py +304 -295
  22. package/metrics/client.py +804 -394
  23. package/metrics/cliproxy.py +191 -191
  24. package/metrics/connect.py +691 -691
  25. package/metrics/cost.py +171 -152
  26. package/metrics/decide.py +227 -227
  27. package/metrics/discover.py +318 -318
  28. package/metrics/endpoints.py +436 -436
  29. package/metrics/formats.py +1021 -0
  30. package/metrics/guard.py +647 -592
  31. package/metrics/learn.py +234 -221
  32. package/metrics/ledger.py +148 -0
  33. package/metrics/litellm_hub.py +206 -206
  34. package/metrics/model_roles.py +94 -84
  35. package/metrics/offload.py +166 -166
  36. package/metrics/owui.py +205 -205
  37. package/metrics/pacing.py +248 -0
  38. package/metrics/paths.py +60 -60
  39. package/metrics/quota.py +383 -0
  40. package/metrics/route.py +581 -579
  41. package/metrics/scheduler.py +275 -275
  42. package/metrics/sources.py +167 -167
  43. package/metrics/wrong.py +144 -144
  44. package/package.json +69 -65
  45. package/perf/README.md +89 -89
  46. package/perf/run.py +284 -261
  47. package/scripts/auto_retire.py +367 -367
  48. package/scripts/check.py +113 -106
  49. package/scripts/check_deprecations.py +378 -378
  50. package/scripts/check_themes.py +103 -103
  51. package/scripts/init.py +124 -124
  52. package/scripts/list_served.py +78 -78
  53. package/scripts/notice.py +78 -78
  54. package/scripts/onboard.py +410 -331
  55. package/scripts/provider_tags.py +129 -129
  56. package/scripts/reprefix.py +136 -136
  57. package/scripts/sync_config.py +105 -105
  58. package/scripts/test_auth.py +255 -255
  59. package/scripts/test_cli.py +189 -0
  60. package/scripts/test_client.py +277 -233
  61. package/scripts/test_connect.py +222 -222
  62. package/scripts/test_first_run.py +180 -180
  63. package/scripts/test_formats.py +549 -0
  64. package/scripts/test_hubs.py +328 -328
  65. package/scripts/test_launcher.py +67 -0
  66. package/scripts/test_llm_log.py +85 -81
  67. package/scripts/test_mcp.py +178 -178
  68. package/scripts/test_merged.py +215 -214
  69. package/scripts/test_no_owui.py +291 -291
  70. package/scripts/test_owui.py +90 -90
  71. package/scripts/test_quota.py +334 -0
  72. package/scripts/test_serve_edit.py +453 -438
  73. package/scripts/test_server_json.py +79 -0
  74. package/scripts/test_verdict.py +102 -98
  75. package/scripts/test_vision.py +231 -228
  76. package/serve.py +3972 -3764
  77. package/suite/mine.py +154 -154
  78. package/suite/pace.py +145 -145
  79. package/suite/run.py +1046 -1042
  80. package/suite/seed.py +421 -421
  81. package/suite/tasks/claim-alias-unbound/fixtures/onboard.py +286 -286
  82. package/suite/tasks/claim-error-text-string/fixtures/decide.py +187 -187
  83. package/suite/tasks/claim-list-body/fixtures/gateway.py +235 -235
  84. package/suite/tasks/claim-member-nominated-twice/fixtures/gateway.py +284 -284
  85. package/suite/tasks/claim-nominee-uses-slot/fixtures/gateway.py +284 -284
  86. package/suite/tasks/claim-silent-exit/fixtures/gateway.py +235 -235
  87. package/suite/tasks/claim-sse-hang/fixtures/gateway.py +235 -235
  88. package/suite/tasks/claim-unknown-nominee-crash/fixtures/gateway.py +284 -284
  89. package/suite/tasks/rebench-matchms-809-bug/task.json +23 -23
  90. package/suite/tasks/rebench-matchms-809-control/task.json +27 -27
  91. package/suite/tasks/rebench-more-itertools-1028-bug/task.json +23 -23
  92. package/suite/tasks/rebench-more-itertools-1028-control/task.json +27 -27
  93. package/suite/tasks/rebench-narwhals-2864-bug/task.json +23 -23
  94. package/suite/tasks/rebench-narwhals-2864-control/task.json +27 -27
  95. package/suite/tasks/rebench-quantecon-py-769-bug/task.json +23 -23
  96. package/suite/tasks/rebench-quantecon-py-769-control/task.json +27 -27
  97. package/suite/tasks/rebench-scrapy-6867-bug/task.json +23 -23
  98. package/suite/tasks/rebench-scrapy-6867-control/task.json +27 -27
  99. package/ui/bench.css +430 -425
  100. package/ui/bench.js +1757 -1576
  101. package/ui/themes/dark.css +35 -35
  102. package/ui/themes/hardwood.css +52 -52
  103. package/ui/themes/rainforest.css +32 -32
package/package.json CHANGED
@@ -1,65 +1,69 @@
1
- {
2
- "name": "@wendellresearch-ai/benchwarmers",
3
- "version": "0.2.0",
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
- "files": [
33
- "bin/",
34
- "serve.py",
35
- "gateway.py",
36
- "mcp_server.py",
37
- "metrics/*.py",
38
- "scripts/*.py",
39
- "suite/*.py",
40
- "suite/*.md",
41
- "suite/*.json",
42
- "suite/tasks/",
43
- "perf/*.py",
44
- "perf/*.md",
45
- "repoqa/*.py",
46
- "repoqa/*.md",
47
- "ui/",
48
- "examples/",
49
- "VERSION",
50
- "NOTICE",
51
- "AGENTS.md",
52
- "BENCHMARKS.md",
53
- "GATEWAY.md",
54
- "PRIVACY.md",
55
- "SECURITY.md",
56
- "TERMS.md",
57
- "USAGE.md"
58
- ],
59
- "engines": {
60
- "node": ">=18"
61
- },
62
- "publishConfig": {
63
- "access": "public"
64
- }
65
- }
1
+ {
2
+ "name": "@wendellresearch-ai/benchwarmers",
3
+ "version": "0.2.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
+ "mcpName": "io.github.WendellResearch-AI/benchwarmers",
6
+ "license": "MIT",
7
+ "author": "Wendell Research",
8
+ "homepage": "https://github.com/WendellResearch-AI/benchwarmers#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/WendellResearch-AI/benchwarmers.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/WendellResearch-AI/benchwarmers/issues"
15
+ },
16
+ "keywords": [
17
+ "llm",
18
+ "llm-gateway",
19
+ "openai-compatible",
20
+ "multi-model",
21
+ "code-review",
22
+ "llm-evaluation",
23
+ "benchmark",
24
+ "model-routing",
25
+ "self-hosted",
26
+ "open-webui",
27
+ "openrouter",
28
+ "litellm"
29
+ ],
30
+ "bin": {
31
+ "benchwarmers": "bin/benchwarmers.js",
32
+ "bench": "bin/benchwarmers.js"
33
+ },
34
+ "files": [
35
+ "bin/",
36
+ "launcher.py",
37
+ "serve.py",
38
+ "gateway.py",
39
+ "mcp_server.py",
40
+ "cli.py",
41
+ "metrics/*.py",
42
+ "scripts/*.py",
43
+ "suite/*.py",
44
+ "suite/*.md",
45
+ "suite/*.json",
46
+ "suite/tasks/",
47
+ "perf/*.py",
48
+ "perf/*.md",
49
+ "repoqa/*.py",
50
+ "repoqa/*.md",
51
+ "ui/",
52
+ "examples/",
53
+ "VERSION",
54
+ "NOTICE",
55
+ "AGENTS.md",
56
+ "BENCHMARKS.md",
57
+ "GATEWAY.md",
58
+ "PRIVACY.md",
59
+ "SECURITY.md",
60
+ "TERMS.md",
61
+ "USAGE.md"
62
+ ],
63
+ "engines": {
64
+ "node": ">=18"
65
+ },
66
+ "publishConfig": {
67
+ "access": "public"
68
+ }
69
+ }
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.