@wendellresearch-ai/benchwarmers 0.3.0 → 0.3.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 CHANGED
@@ -25,12 +25,11 @@ Python 3.11 or later; nothing else is required.
25
25
  git clone https://github.com/WendellResearch-AI/benchwarmers
26
26
  cd benchwarmers
27
27
  python -m venv .venv
28
- .venv/bin/pip install llm # optional; Windows: .venv\Scripts\pip install llm
29
28
 
30
- The `llm` CLI is today's default model client. Without it, choose the
31
- built-in client (`metrics/client.py`), which needs nothing installed: on
32
- Settings, Model client, once the dashboard runs, or with
33
- `BENCH_CLIENT=builtin` (which wins over Settings).
29
+ The built-in model client (`metrics/client.py`) makes every call and needs
30
+ nothing installed. The `llm` CLI is an optional alternative, for its plugins:
31
+ `pip install llm` in the venv, then choose it on Settings, Model client, or
32
+ with `BENCH_CLIENT=llm` (which wins over Settings).
34
33
 
35
34
  Check: `python scripts/check.py --quick` ends with `all checks pass`.
36
35
 
@@ -67,8 +66,9 @@ API) or a local runtime (Ollama). Three ways, best first:
67
66
  | `openrouter-2` | `BENCHWARMERS_KEY_OPENROUTER_2` |
68
67
  | `my-hub` | `BENCHWARMERS_KEY_MY_HUB` |
69
68
 
70
- Without the variable, keys come from the `llm` CLI's key store
71
- (`llm keys set <name>` writes there). The variable wins when both exist.
69
+ Without the variable, keys come from llm's key file (`keys.json` in llm's
70
+ user folder, which the dashboard and `init --key` write; the `llm` CLI need
71
+ not be installed). The variable wins when both exist.
72
72
 
73
73
  One provider can be several endpoints (a pasted key and a sign-in, or two
74
74
  accounts); each needs its own name, and Benchwarmers suggests a free one.
@@ -107,7 +107,10 @@ Any OpenAI-compatible client asks for the model `bench/<collection>`:
107
107
  Every seat's answer comes back, plus a `bench` field with each seat's call id.
108
108
  Clients of the Anthropic, Ollama, Gemini and Responses APIs work too, with
109
109
  the server's address as their base URL (GATEWAY.md, "Other API formats");
110
- requests carrying tools are refused, since a panel answers with text.
110
+ a panel answers with text, so requests carrying tools are refused there.
111
+ An agent that needs tools (Claude Code, Codex, opencode) runs on a collection
112
+ in route mode instead, `bench/<collection>:route`: one member per turn, with
113
+ its tools, kept for the conversation (GATEWAY.md, "Route mode").
111
114
  For a chat window that shows one answer, ask for `bench/<collection>:merged`
112
115
  (or set the collection's reply to merged): a merger model writes one answer
113
116
  saying where the seats agreed and disagreed, and every seat is still in
package/PRIVACY.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Privacy policy
2
2
 
3
- *Effective 22 October 2026.*
3
+ *Effective 23 October 2026.*
4
4
 
5
5
  Benchwarmers runs on your own machine or server. It has no account, no
6
6
  telemetry and no server of its own, and nothing you do with it reaches
@@ -22,13 +22,15 @@ Wendell Research.
22
22
  - **Real-bug task seeding**, only when you run `suite/rebench.py`: dataset rows
23
23
  from Hugging Face and source files from GitHub.
24
24
 
25
- Keys and sign-in tokens stay in your environment or the `llm` CLI's key
26
- store. [SECURITY.md](SECURITY.md) has the full detail.
25
+ Keys and sign-in tokens stay in your environment or llm's key file
26
+ (`keys.json` in llm's user folder, used whether or not the `llm` CLI is
27
+ installed). [SECURITY.md](SECURITY.md) has the full detail.
27
28
 
28
29
  ## What stays on your machine
29
30
 
30
- Everything else: your endpoints, collections, verdicts, call logs, probe
31
- history and task answers live in your data folder, which is yours to back up,
31
+ Everything else: your endpoints, collections, verdicts, call logs, the usage
32
+ ledger (tokens per call, never the prompt), probe history and task answers
33
+ live in your data folder, which is yours to back up,
32
34
  move or delete.
33
35
 
34
36
  ## Third parties
package/README.md CHANGED
@@ -10,8 +10,14 @@
10
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
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
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>
13
+ <a href="#docker-and-hosting"><img src="https://img.shields.io/badge/Docker-compose-2496ED?logo=docker&logoColor=white" alt="Runs in Docker"></a>
14
+ <br>
15
+ <a href="GATEWAY.md#other-api-formats"><img src="https://img.shields.io/badge/API-OpenAI_%7C_Responses-111111" alt="OpenAI chat completions and Responses API"></a>
16
+ <a href="GATEWAY.md#other-api-formats"><img src="https://img.shields.io/badge/API-Anthropic_Messages-111111" alt="Anthropic Messages API"></a>
17
+ <a href="GATEWAY.md#other-api-formats"><img src="https://img.shields.io/badge/API-Gemini-111111" alt="Gemini API"></a>
18
+ <a href="GATEWAY.md#other-api-formats"><img src="https://img.shields.io/badge/API-Ollama-111111" alt="Ollama API"></a>
19
+ <a href="GATEWAY.md#route-mode-agent-clients"><img src="https://img.shields.io/badge/agents-tool_calling-111111" alt="Agent clients with tool calling, through route mode"></a>
20
+ <a href="https://registry.modelcontextprotocol.io/v0/servers?search=io.github.WendellResearch-AI/benchwarmers"><img src="https://img.shields.io/badge/MCP-Registry-111111" alt="MCP server in the official MCP Registry"></a>
15
21
  <br>
16
22
  <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
23
  <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>
@@ -30,8 +36,12 @@ verdicts decide who sits on the next panel.
30
36
 
31
37
  **What it is best at:**
32
38
 
33
- - **Asking several models at once.** One OpenAI-compatible request, a panel of
34
- answers. Where they disagree is where to look.
39
+ - **Asking several models at once.** One request, in the OpenAI, Anthropic,
40
+ Gemini or Ollama format, a panel of answers. Where they disagree is where
41
+ to look.
42
+ - **Agents too.** Claude Code and other tool-calling clients use a collection
43
+ through route mode: one member per turn, kept for the conversation, with
44
+ failover, and every call on the record.
35
45
  - **Reviewing.** Code review, claims about code, designs: different models
36
46
  catch different things, and two models agreeing is not proof.
37
47
  - **Playing to each model's strengths.** A collection per kind of work
@@ -43,8 +53,9 @@ verdicts decide who sits on the next panel.
43
53
  repositories, plus your verdicts on real work, instead of public leaderboards.
44
54
  - **Knowing who to bench.** Who is confidently wrong, who has stopped
45
55
  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.
56
+ - **Keeping free and cheap models useful.** Health probes, rate limits taken
57
+ from what providers say and enforced, and one endpoint for every app, over
58
+ any OpenAI-compatible provider or hub, or Anthropic's and Gemini's own APIs.
48
59
 
49
60
  | Related tools | Examples | What they do | What Benchwarmers does differently |
50
61
  |---|---|---|---|
@@ -68,7 +79,7 @@ verdicts decide who sits on the next panel.
68
79
  - [From the command line](#from-the-command-line)
69
80
  - [From Claude Code and other agents](#from-claude-code-and-other-agents)
70
81
  - [The suite](#the-suite)
71
- - [Docker](#docker)
82
+ - [Docker and hosting](#docker-and-hosting)
72
83
  - [What leaves your machine](#what-leaves-your-machine)
73
84
  - [Limits](#limits)
74
85
  - [Security](#security)
@@ -82,7 +93,7 @@ with [AGENTS.md](AGENTS.md).
82
93
 
83
94
  ## Try it
84
95
 
85
- **v0.2, early.** It runs with Python 3.11 or later on Windows, macOS and
96
+ **v0.3, early.** It runs with Python 3.11 or later on Windows, macOS and
86
97
  Linux, from PyPI, npm, a checkout or Docker. Expect rough edges, and read the
87
98
  code where these pages are thin.
88
99
 
@@ -108,12 +119,12 @@ dependencies either way: the app is the Python standard library.
108
119
 
109
120
  git clone https://github.com/WendellResearch-AI/benchwarmers
110
121
  cd benchwarmers
111
- python -m venv .venv
112
- .venv/bin/pip install llm # Windows: .venv\Scripts\pip install llm
122
+ python -m venv .venv # nothing to pip install: the standard library is enough
113
123
 
114
- The [`llm`](https://llm.datasette.io/) CLI is the default model client today.
115
- The built-in client (`metrics/client.py`) replaces it, chosen on Settings
116
- or with `BENCH_CLIENT=builtin` (which wins), and will become the default.
124
+ The built-in model client (`metrics/client.py`) makes every call. The
125
+ [`llm`](https://llm.datasette.io/) CLI is an alternative for its plugins:
126
+ install it in the venv and choose it on Settings (or `BENCH_CLIENT=llm`,
127
+ which wins).
117
128
 
118
129
  2. **Make a data folder.** Everything that is yours (endpoints, model
119
130
  registrations, collections, verdicts, probe history, task answers) lives
@@ -125,14 +136,16 @@ dependencies either way: the app is the Python standard library.
125
136
  unless you name one) and points the checkout at it with a
126
137
  `.benchwarmers-home` file; `BENCHWARMERS_HOME` overrides that. With
127
138
  `--endpoint NAME BASE_URL --key` it also adds a first endpoint and asks for
128
- its key, which goes to the `llm` CLI's key store (`llm keys set NAME` does
129
- the same), never into the data folder.
139
+ its key, which goes to the key file in llm's user folder (`keys.json`,
140
+ shared with the `llm` CLI if you have it, not needed otherwise), never into
141
+ the data folder. `BENCHWARMERS_KEY_<NAME>` in the environment works too.
130
142
 
131
143
  3. **Start the dashboard.** With no endpoint yet it opens on adding one: sign
132
144
  in with a provider on its own page (OpenRouter, NanoGPT, Hugging Face,
133
145
  Pollinations, your ChatGPT plan, GitHub Models, or a local Ollama), or paste
134
146
  an API key for any OpenAI-compatible API, a hub such as Open WebUI or
135
- LiteLLM included. Only providers that publish a sign-in for other apps are
147
+ LiteLLM included. Anthropic and Gemini keys can also be called through
148
+ those providers' own APIs (the API choice in the same dialog). Only providers that publish a sign-in for other apps are
136
149
  offered; adding one is an entry in `metrics/connect.py` (its docstring says
137
150
  how).
138
151
 
@@ -140,7 +153,9 @@ dependencies either way: the app is the Python standard library.
140
153
 
141
154
  4. **Onboard a few models** from it ("Onboard a model"): tick one or several;
142
155
  each is registered, probed and run through the suite, one at a time, with
143
- the progress beside the button.
156
+ the progress beside the button. **Quick** probes now and leaves the suite
157
+ to the scheduled passes; **Quick + ad hoc** also keeps the bench from ever
158
+ calling the model on its own, for a metered or trial key.
144
159
 
145
160
  5. **Make a collection** on the Collections page (the models a panel always
146
161
  includes, and what it is for). The dashboard answers the gateway's routes on
@@ -163,10 +178,16 @@ dependencies either way: the app is the Python standard library.
163
178
  - **Health.** Every morning each model gets a one-word prompt; once a week, a
164
179
  short and a long one for speed. A model that stops answering, or that its
165
180
  endpoint no longer serves, is retired automatically after one confirming
166
- call, with the evidence kept, and can be restored in one click. Rate limits
167
- are learned from providers' own error messages and kept per connection,
168
- since models behind one key share them. The built-in scheduler runs all of
169
- this once Settings switches it on; a fresh install spends no calls until then.
181
+ call, with the evidence kept, and can be restored in one click. The
182
+ built-in scheduler runs all of this once Settings switches it on; a fresh
183
+ install spends no calls until then.
184
+ - **Rate limits and usage.** Limits come from providers' rate-limit headers
185
+ and error messages, and from what you set per connection or model
186
+ (requests a minute, day or month; tokens a minute or day). They are kept per
187
+ connection, since models behind one key share them, and enforced: a call
188
+ waits when the wait fits, or is refused before it goes out. Every call lands
189
+ in a monthly usage ledger with its tokens, and the Quotas page shows what is
190
+ left and when it resets.
170
191
  - **Discover.** models.dev and FreeLLMAPI, merged into one list of models
171
192
  worth adding, each with the routes this install has to it.
172
193
 
@@ -206,10 +227,11 @@ that speaks one of them works with the server's address as its base URL:
206
227
  -d '{"model": "bench/<collection>", "max_tokens": 1024, "messages": [{"role": "user", "content": "..."}]}'
207
228
 
208
229
  Each streams when asked: the reply starts at once, and each seat's answer
209
- arrives as that seat finishes. Agent and tool use is not supported yet: a
210
- request carrying tools is refused with a 400 rather than answered without
211
- them. GATEWAY.md, "Other API formats", has a curl for each and what is
212
- refused or ignored.
230
+ arrives as that seat finishes. A panel answers with text, so a request
231
+ carrying tools is refused with a 400 that points to **route mode**
232
+ (`bench/<collection>:route`, one member with its tools; see
233
+ [below](#from-claude-code-and-other-agents)). GATEWAY.md, "Other API formats",
234
+ has a curl for each and what is refused or ignored.
213
235
 
214
236
  ## From the command line
215
237
 
@@ -324,7 +346,7 @@ code makes the best tasks, because no model has trained on it. See
324
346
  [suite/README.md](suite/README.md), [suite/REBENCH.md](suite/REBENCH.md) and
325
347
  [BENCHMARKS.md](BENCHMARKS.md).
326
348
 
327
- ## Docker
349
+ ## Docker and hosting
328
350
 
329
351
  One container runs the dashboard, the gateway (on the same port, at `/v1`)
330
352
  and the scheduler, with the data folder as a volume and no `llm` CLI (the
@@ -366,21 +388,33 @@ reaches Wendell Research. What it sends, and where:
366
388
  - **Seeding real-bug tasks**, only when you run `suite/rebench.py`: the
367
389
  SWE-rebench-V2 rows from Hugging Face and the files from GitHub.
368
390
 
369
- Keys stay in the environment or the `llm` CLI's key store, never in the data
370
- folder. [PRIVACY.md](PRIVACY.md) and [SECURITY.md](SECURITY.md) have the
391
+ Keys stay in the environment or llm's key file (`keys.json` in its user
392
+ folder), never in the data folder. [PRIVACY.md](PRIVACY.md) and [SECURITY.md](SECURITY.md) have the
371
393
  detail.
372
394
 
373
395
  ## Limits
374
396
 
375
- - **No tools yet.** A panel answers with text, not a tool call, so agents
376
- that need tool use (Claude Code, Codex, opencode as callers) wait for a
377
- route mode; requests with tools are refused, not trimmed.
378
- - **Early.** v0.1: no installer yet, merged replies stream in one piece, and the LiteLLM and
379
- CLI Proxy API hub adapters are built from those projects' source, not yet
380
- run against a real hub.
397
+ - **Tools go through route mode only.** A panel answers with text; a request
398
+ with tools is refused there, not trimmed. Route mode passes turns only to
399
+ members on chat-completions endpoints, keeps its sticky conversations in
400
+ memory (a restart starts each on a fresh pick), and has been run end to end
401
+ with Claude Code; the Codex, opencode and Cline set-ups are written from
402
+ their docs. Shadow seats (other members answering an agent's turn for
403
+ evaluation) are planned, not built.
404
+ - **Some parts are checked against fakes only.** The native Anthropic and
405
+ Gemini endpoints, and the LiteLLM and CLI Proxy API hub adapters, are built
406
+ from those APIs' documentation and source and tested against fake servers,
407
+ not yet against the real services.
408
+ - **A merged reply arrives in one piece** once the merger has finished; the
409
+ seats before it stream as they finish.
410
+ - **Pacing and the ledger cover the built-in client.** Calls made through the
411
+ `llm` CLI, when you choose it, are neither paced nor counted.
381
412
  - **Calls cost.** Probes, onboarding and the suite run on your keys and free
382
- tiers. Onboarding runs one model at a time for that reason; a suite pass is
383
- paced, not burst.
413
+ tiers. Onboarding runs one model at a time for that reason (Quick spreads
414
+ the suite over the scheduled passes); a suite pass is paced, not burst.
415
+ - **The scheduler lives in the server.** Its jobs run while the dashboard (or
416
+ the container, or the service) is up; a missed run is made up once, within
417
+ seven days, when it comes back.
384
418
  - **One install, one person** at heart. Logins and gateway keys exist for a
385
419
  LAN or a server, but there are no per-user views or roles.
386
420
  - **The onboarding queue lives in the dashboard's memory.** Restarting it
package/SECURITY.md CHANGED
@@ -58,7 +58,8 @@ open itself to anyone.
58
58
  ## What Benchwarmers touches
59
59
 
60
60
  - **Keys.** Endpoint keys come from `BENCHWARMERS_KEY_<NAME>` (the key name
61
- upper-cased, `-` as `_`) first, then from the `llm` CLI's key store. They are
61
+ upper-cased, `-` as `_`) first, then from llm's key file (`keys.json` in
62
+ its user folder, used whether or not the `llm` CLI is installed). They are
62
63
  never written to the data folder, never shown back, and never put in a log.
63
64
  Sign-ins through a provider's page store their token the same way.
64
65
  - **The network.** Your prompts go only to the endpoints you add; the probes,
@@ -68,9 +69,9 @@ open itself to anyone.
68
69
  dashboard's page, and Hugging Face and GitHub only when you run
69
70
  `suite/rebench.py`. [PRIVACY.md](PRIVACY.md) lists them.
70
71
  - **Files.** It writes inside its data folder (`BENCHWARMERS_HOME`, or the
71
- folder `.benchwarmers-home` names) and, in the `llm` CLI's own folder, its
72
- key store (when you paste a key or sign in) and its model list (when it
73
- registers a model). Images sent to the gateway are never written to a log.
72
+ folder `.benchwarmers-home` names) and, in llm's user folder, the key file
73
+ (when you paste a key or sign in) and the model list (when it registers a
74
+ model). Images sent to the gateway are never written to a log.
74
75
  - **Processes.** Onboarding, the probes and the suite run this repository's
75
76
  own scripts with the same Python. Model ids that reach a command line are
76
77
  checked against a strict pattern first.
package/USAGE.md CHANGED
@@ -47,8 +47,19 @@ Dismiss hides one you never want offered (an image model, say); the dismissed
47
47
  list brings it back.
48
48
 
49
49
  Each model is registered, probed with four short calls, and, if it answers,
50
- run through the suite. That takes minutes and spends calls, so they run **one
51
- at a time**, in a queue the dashboard keeps:
50
+ run through the suite. **Onboard as**, under the list, chooses how much that
51
+ spends:
52
+
53
+ - **Full**: the probe, then the suite now.
54
+ - **Quick**: the probe now; the scheduled suite passes fill the suite in, six
55
+ tasks a run. The same calls, spread out.
56
+ - **Quick + ad hoc**: the probe only, and the bench never calls the model on
57
+ its own (no explorer seat, no scheduled runs). The one that saves calls, for
58
+ a metered, capped or trial key. A collection that lists it still calls it.
59
+
60
+ `bench onboard <served> --quick [--ad-hoc]` does the same from a terminal.
61
+ Onboarding takes minutes and spends calls, so models run **one at a time**,
62
+ in a queue the dashboard keeps:
52
63
 
53
64
  - One model with nothing ahead of it is followed step by step in the popup.
54
65
  - Otherwise the popup shows the queue, and a chip beside the button shows the
@@ -62,6 +73,12 @@ at a time**, in a queue the dashboard keeps:
62
73
  A model that registered but did not answer stays on the bench as
63
74
  unavailable, so the daily probe keeps watching it.
64
75
 
76
+ **Roles.** A model's popup sets what it is for: `general` (the default),
77
+ `code`, `light` (small and fast), `vision` (reads images well, weak at text:
78
+ OCR and document models; seated only when a request carries images) or
79
+ `image` (makes images; never seated). A model that reads images and does
80
+ everything else well stays `general`.
81
+
65
82
  ## Collections
66
83
 
67
84
  A collection is a named panel: the models it always includes, the shape of
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.1
package/metrics/client.py CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  python metrics/client.py <alias> "prompt" # one call; prints what ask_once would return
4
4
 
5
- RELEASE-PLAN.md Phases 2a and 2b. A drop-in for `llm -m <alias>`, chosen on
6
- Settings or by BENCH_CLIENT=builtin (see chosen()), used by suite/run.py's
5
+ RELEASE-PLAN.md Phases 2a and 2b. A drop-in for `llm -m <alias>`, and the
6
+ default since v0.3 (`llm` stays a choice on Settings or BENCH_CLIENT=llm; see
7
+ chosen()), used by suite/run.py's
7
8
  ask_once, and so the perf probe, the suite, retries and auto-retire's
8
9
  confirmation call, and by the gateway's seats. It reads the same
9
10
  registration (the yaml entry's model name, base, key name and headers), the
@@ -118,7 +119,6 @@ import json
118
119
  import os
119
120
  import pathlib
120
121
  import random
121
- import shutil
122
122
  import sys
123
123
  import threading
124
124
  import time
@@ -146,10 +146,11 @@ CHOICE = DATA / "config" / "client.json" # the Settings page's choice
146
146
 
147
147
  def chosen() -> tuple[str, str]:
148
148
  """The configured client and what set it: BENCH_CLIENT ("environment"),
149
- else the Settings page's choice ("settings"), else ("default") `llm` where
150
- the llm CLI is installed and the built-in client where it is not, as in
151
- an install from npm or a container. Read on every call, so a change
152
- applies to the next one."""
149
+ else the Settings page's choice ("settings"), else ("default") the
150
+ built-in client, installed `llm` or not: since v0.3 the `llm` CLI is an
151
+ alternative chosen on Settings, no longer the default (its trial week:
152
+ RELEASE-PLAN Phase 2a). Read on every call, so a change applies to the
153
+ next one."""
153
154
  env = (os.environ.get("BENCH_CLIENT") or "").strip().lower()
154
155
  if env:
155
156
  return env, "environment"
@@ -157,7 +158,7 @@ def chosen() -> tuple[str, str]:
157
158
  saved = json.loads(CHOICE.read_text(encoding="utf-8")).get("client")
158
159
  except (OSError, ValueError, AttributeError):
159
160
  saved = None
160
- return (saved, "settings") if saved in CLIENTS else ("llm" if shutil.which("llm") else "builtin", "default")
161
+ return (saved, "settings") if saved in CLIENTS else ("builtin", "default")
161
162
 
162
163
 
163
164
  def backend(alias: str = "") -> str:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wendellresearch-ai/benchwarmers",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
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
5
  "mcpName": "io.github.WendellResearch-AI/benchwarmers",
6
6
  "license": "MIT",
@@ -291,7 +291,7 @@ def main() -> int:
291
291
  model_client.CHOICE = cfg / "client.json"
292
292
  env, real_which = os.environ.pop("BENCH_CLIENT", None), shutil.which
293
293
  try:
294
- shutil.which = lambda name: "/usr/bin/" + name # llm installed: the default is llm
294
+ shutil.which = lambda name: "/usr/bin/" + name # llm installed: still built-in
295
295
  first = model_client.chosen()
296
296
  shutil.which = lambda name: None # not installed (npm, a container)
297
297
  bare = model_client.chosen()
@@ -310,7 +310,7 @@ def main() -> int:
310
310
  os.environ.pop("BENCH_CLIENT", None)
311
311
  if env is not None:
312
312
  os.environ["BENCH_CLIENT"] = env
313
- ok = (first == ("llm", "default") and bare == ("builtin", "default") and made["ok"] and picked == (("builtin", "settings"), "builtin")
313
+ ok = (first == ("builtin", "default") and bare == ("builtin", "default") and made["ok"] and picked == (("builtin", "settings"), "builtin")
314
314
  and pinned == (("llm", "environment"), False) and not no_llm and not unknown)
315
315
  print(("ok " if ok else "FAIL ") + "model client choice" + ("" if ok else f" -> {first} {made} {picked} {pinned}"))
316
316
  failures += 0 if ok else 1