@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/launcher.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""The `benchwarmers` and `bench` commands, however Benchwarmers was installed.
|
|
2
|
+
|
|
3
|
+
pipx install benchwarmers # PyPI: this file is the entry point (pyproject.toml)
|
|
4
|
+
npm install -g @wendellresearch-ai/benchwarmers # npm: bin/benchwarmers.js finds Python and runs this
|
|
5
|
+
python launcher.py [command] # a checkout
|
|
6
|
+
|
|
7
|
+
It keeps the data in the user's profile (scripts/init.py's default_folder, or
|
|
8
|
+
BENCHWARMERS_HOME), sets that folder up on the first run, then runs the
|
|
9
|
+
command: a server (serve, gateway, mcp, init) or, for everything else, the
|
|
10
|
+
client (cli.py) in the caller's own folder. One launcher for every install, so
|
|
11
|
+
the commands, the help and the first run are written once.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
ROOT = Path(__file__).resolve().parent # the app: a checkout, the npm package or the wheel's folder
|
|
22
|
+
SERVERS = {"serve": "serve.py", "gateway": "gateway.py", "mcp": "mcp_server.py",
|
|
23
|
+
"init": str(Path("scripts") / "init.py")}
|
|
24
|
+
# Everything but running a server is cli.py, a client of a running server.
|
|
25
|
+
CLIENT = ("status", "ask", "verdict", "models", "model", "candidates", "onboard", "dismiss",
|
|
26
|
+
"collections", "collection", "endpoints", "endpoint", "keys", "key", "settings")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def version() -> str:
|
|
30
|
+
return (ROOT / "VERSION").read_text(encoding="utf-8").strip()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def default_home() -> Path:
|
|
34
|
+
sys.path.insert(0, str(ROOT / "scripts"))
|
|
35
|
+
try:
|
|
36
|
+
import init
|
|
37
|
+
return init.default_folder()
|
|
38
|
+
finally:
|
|
39
|
+
sys.path.pop(0)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def prog() -> str:
|
|
43
|
+
"""`bench` or `benchwarmers`, as typed (npm passes it, being the one that saw it)."""
|
|
44
|
+
named = os.environ.get("BENCHWARMERS_PROG") or Path(sys.argv[0]).stem
|
|
45
|
+
return named if named in ("bench", "benchwarmers") else "benchwarmers"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def usage() -> str:
|
|
49
|
+
return "\n".join([
|
|
50
|
+
f"Usage: {prog()} [command]",
|
|
51
|
+
"",
|
|
52
|
+
"Running it:",
|
|
53
|
+
" serve the dashboard, with the gateway at /v1: http://localhost:8122 (the default)",
|
|
54
|
+
" gateway the gateway alone: http://127.0.0.1:8123/v1",
|
|
55
|
+
" init [...] set up a data folder, optionally with a first endpoint (init --help)",
|
|
56
|
+
" mcp an MCP server on stdio for agents",
|
|
57
|
+
"",
|
|
58
|
+
"Using it (a client of a running server, BENCHWARMERS_URL; --help on any):",
|
|
59
|
+
" status what is running, and where",
|
|
60
|
+
" ask <collection> [prompt] ask a collection; -f FILE adds a file, a pipe reads stdin",
|
|
61
|
+
" verdict <call> --useful|--not-useful [--real ...] [--false ...]",
|
|
62
|
+
" models | model <action> list, rename, role, retire, restore, max-tokens, ...",
|
|
63
|
+
" candidates | onboard | dismiss what could be onboarded, and onboarding it",
|
|
64
|
+
" collections | collection <action> list, show, save, rm",
|
|
65
|
+
" endpoints | endpoint <action> list, add, test, key, rm, default",
|
|
66
|
+
" keys | key <action> gateway keys: list, create, revoke",
|
|
67
|
+
" settings <gateway|client|scheduler>",
|
|
68
|
+
" --version the version",
|
|
69
|
+
"",
|
|
70
|
+
f"Your data lives in BENCHWARMERS_HOME, else {default_home()}.",
|
|
71
|
+
"BENCH_PORT changes the port. Docs: https://github.com/WendellResearch-AI/benchwarmers",
|
|
72
|
+
])
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def plan(argv: list[str]) -> dict:
|
|
76
|
+
"""What a command line means: {"say", "code"} to print and stop, or
|
|
77
|
+
{"script", "args", "cwd", "first_run"} to run. Pure, so it can be tested."""
|
|
78
|
+
first = argv[0] if argv else ""
|
|
79
|
+
command = first if first and not first.startswith("-") else "serve"
|
|
80
|
+
client = command in CLIENT
|
|
81
|
+
if first in ("--version", "-v"):
|
|
82
|
+
return {"say": version(), "code": 0}
|
|
83
|
+
# A client command's own --help is cli.py's, which knows its options; init has its own.
|
|
84
|
+
if command != "init" and not client and ("--help" in argv or "-h" in argv):
|
|
85
|
+
return {"say": usage(), "code": 0}
|
|
86
|
+
if not client and command not in SERVERS:
|
|
87
|
+
return {"say": f'{prog()}: no command "{command}"\n\n{usage()}', "code": 2, "stderr": True}
|
|
88
|
+
if client: # cli.py takes the command itself
|
|
89
|
+
return {"script": "cli.py", "args": argv, "cwd": os.getcwd(), "first_run": False}
|
|
90
|
+
# The first run makes the data folder; not for mcp, whose stdout carries protocol messages only.
|
|
91
|
+
return {"script": SERVERS[command], "args": argv[1:] if command == first else argv, "cwd": str(ROOT),
|
|
92
|
+
"first_run": command not in ("init", "mcp")}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main(argv: list[str] | None = None) -> int:
|
|
96
|
+
argv = sys.argv[1:] if argv is None else argv
|
|
97
|
+
p = plan(argv)
|
|
98
|
+
if "say" in p:
|
|
99
|
+
print(p["say"], file=sys.stderr if p.get("stderr") else sys.stdout)
|
|
100
|
+
return p["code"]
|
|
101
|
+
home = Path(os.environ.get("BENCHWARMERS_HOME", "").strip() or default_home()).expanduser()
|
|
102
|
+
# No bytecode beside the code: an installed folder may not be writable, and
|
|
103
|
+
# an upgrade replaces it anyway.
|
|
104
|
+
env = {**os.environ, "BENCHWARMERS_HOME": str(home), "PYTHONDONTWRITEBYTECODE": "1", "PYTHONUTF8": "1",
|
|
105
|
+
"BENCHWARMERS_LAUNCHER": os.environ.get("BENCHWARMERS_LAUNCHER") or "pip"}
|
|
106
|
+
if p["first_run"] and not home.exists():
|
|
107
|
+
print(f"First run: making your data folder at {home}")
|
|
108
|
+
done = subprocess.run([sys.executable, str(ROOT / SERVERS["init"]), str(home)], env=env, cwd=ROOT)
|
|
109
|
+
if done.returncode:
|
|
110
|
+
return done.returncode
|
|
111
|
+
child = subprocess.Popen([sys.executable, str(ROOT / p["script"]), *p["args"]], env=env, cwd=p["cwd"])
|
|
112
|
+
while True:
|
|
113
|
+
try:
|
|
114
|
+
return child.wait()
|
|
115
|
+
except KeyboardInterrupt: # the child has it too; let it finish stopping
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
sys.exit(main())
|