@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
@@ -1,166 +1,166 @@
1
- """A record of REAL work handed to a model — not benchmark calls.
2
-
3
- python metrics/offload.py list
4
- python metrics/offload.py add kimi-k3 "audit the sync seam" --useful \\
5
- --note "found fetch_assignments was never called"
6
- python metrics/offload.py verdict p20261016-171200-a1b2.primary --useful --note "..."
7
-
8
- WHY THIS IS WRITTEN DOWN RATHER THAN DERIVED
9
- --------------------------------------------
10
- The first attempt counted offloads as `logged calls − benchmark calls`, and it
11
- was wrong in both directions. The benchmark ledger only started when it was
12
- built, so every earlier suite run counted as work: `kimi-k3` showed 26
13
- "offloads" and had never been given a real task. Meanwhile a model with a
14
- 164-call floor run looked like the most-used thing here.
15
-
16
- Subtracting one imperfect number from another gives a third number nobody can
17
- check. An offload is a deliberate act, so it is recorded deliberately.
18
-
19
- **And a count alone answers the wrong question.** "Which models am I using
20
- most" matters far less than what for, and whether it helped. Each entry keeps
21
- the task and the verdict, so the routing table can be argued from evidence
22
- rather than from a tally.
23
- """
24
-
25
- from __future__ import annotations
26
-
27
- import argparse
28
- import collections
29
- import datetime as dt
30
- import json
31
- import pathlib
32
-
33
- ROOT = pathlib.Path(__file__).resolve().parent.parent
34
- from paths import DATA # noqa: E402 -- the install's data folder (metrics/paths.py)
35
- LOG = DATA / "metrics" / "offloads.jsonl"
36
-
37
-
38
- def entries() -> list[dict]:
39
- """Every offload, oldest first. An entry with a `call` id (written by the
40
- bench gateway) is superseded by a later one with the same id: the gateway
41
- records each seat unjudged as it answers, and the verdict comes after."""
42
- if not LOG.exists():
43
- return []
44
- out, by_call = [], {}
45
- from aliases import current
46
- for line in LOG.read_text(encoding="utf-8").splitlines():
47
- if line.strip():
48
- try:
49
- e = json.loads(line)
50
- except ValueError:
51
- continue
52
- e["model"] = current(e.get("model", "")) # a renamed model keeps its record
53
- if e.get("call") in by_call:
54
- out[by_call[e["call"]]] = e
55
- continue
56
- if e.get("call"):
57
- by_call[e["call"]] = len(out)
58
- out.append(e)
59
- return out
60
-
61
-
62
- def add(model: str, task: str, useful: bool | None, note: str, call: str | None = None,
63
- real: list[str] | None = None, false: list[str] | None = None) -> dict:
64
- entry = {"date": dt.date.today().isoformat(), "model": model,
65
- "task": task, "useful": useful, "note": note}
66
- if call:
67
- entry["call"] = call
68
- # Verified claims, one sentence each: what this answer got right, and what
69
- # it asserted that is not true. suite/mine.py turns them into tasks.
70
- if real:
71
- entry["real"] = real
72
- if false:
73
- entry["false"] = false
74
- LOG.parent.mkdir(parents=True, exist_ok=True)
75
- with LOG.open("a", encoding="utf-8") as fh:
76
- fh.write(json.dumps(entry) + "\n")
77
- return entry
78
-
79
-
80
- def verdict(call: str, useful: bool | None, note: str,
81
- real: list[str] | None = None, false: list[str] | None = None,
82
- replace: bool = False) -> dict:
83
- """Judge one seat of a bench-gateway panel by its call id. A blank note or
84
- claim list keeps the earlier one, unless `replace` (the dashboard's form,
85
- which shows them for editing, so blank means cleared)."""
86
- found = next((e for e in reversed(entries()) if e.get("call") == call), None)
87
- if found is None:
88
- raise KeyError(f"no offload with call id {call!r}; the gateway prints them under each answer")
89
- if replace:
90
- real, false = real or None, false or None
91
- else:
92
- note, real, false = note or found.get("note", ""), real or found.get("real"), false or found.get("false")
93
- if (useful, note, real, false) == (found.get("useful"), found.get("note", ""), found.get("real"), found.get("false")):
94
- return found # the same verdict again writes nothing
95
- return add(found["model"], found["task"], useful, note, call, real, false)
96
-
97
-
98
- def summary() -> dict[str, dict]:
99
- """Per model: how often, how often it helped, and the last thing it did."""
100
- agg: dict[str, dict] = collections.defaultdict(
101
- lambda: {"offloads": 0, "useful": 0, "judged": 0,
102
- "last_task": "", "last_date": ""})
103
- for e in entries():
104
- a = agg[e["model"]]
105
- a["offloads"] += 1
106
- if e.get("useful") is not None:
107
- a["judged"] += 1
108
- a["useful"] += 1 if e["useful"] else 0
109
- if e["date"] >= a["last_date"]:
110
- a["last_date"], a["last_task"] = e["date"], e["task"]
111
- return dict(agg)
112
-
113
-
114
- def main() -> None:
115
- ap = argparse.ArgumentParser(description=__doc__)
116
- sub = ap.add_subparsers(dest="cmd", required=True)
117
- sub.add_parser("list")
118
- a = sub.add_parser("add")
119
- a.add_argument("model")
120
- a.add_argument("task", help="what it was asked to do, in a few words")
121
- a.add_argument("--useful", dest="useful", action="store_true", default=None)
122
- a.add_argument("--not-useful", dest="useful", action="store_false")
123
- a.add_argument("--note", default="")
124
- # Structured, so metrics/wrong.py can count who asserts false things.
125
- a.add_argument("--real", action="append", metavar="CLAIM",
126
- help="a finding this answer got right, verified; repeatable")
127
- a.add_argument("--false", action="append", dest="false_claims", metavar="CLAIM",
128
- help="a claim this answer made that is not true, verified; repeatable")
129
- v = sub.add_parser("verdict", help="judge one seat of a bench-gateway panel by call id")
130
- v.add_argument("call")
131
- judged = v.add_mutually_exclusive_group(required=True) # a verdict without one erases nothing
132
- judged.add_argument("--useful", dest="useful", action="store_true", default=None)
133
- judged.add_argument("--not-useful", dest="useful", action="store_false")
134
- v.add_argument("--note", default="")
135
- v.add_argument("--real", action="append", metavar="CLAIM",
136
- help="a finding this answer got right, verified; repeatable")
137
- v.add_argument("--false", action="append", dest="false_claims", metavar="CLAIM",
138
- help="a claim this answer made that is not true, verified; repeatable")
139
- args = ap.parse_args()
140
-
141
- if args.cmd == "add":
142
- e = add(args.model, args.task, args.useful, args.note, real=args.real, false=args.false_claims)
143
- print(f"recorded: {e['model']} — {e['task']}")
144
- return
145
- if args.cmd == "verdict":
146
- try:
147
- e = verdict(args.call, args.useful, args.note, args.real, args.false_claims)
148
- except KeyError as exc:
149
- raise SystemExit(exc.args[0])
150
- print(f"recorded: {e['model']} — {e['task']} — useful={e['useful']}")
151
- return
152
-
153
- data = summary()
154
- if not data:
155
- print("No offloads recorded yet.")
156
- print("This is empty because it counts REAL WORK only — a benchmark "
157
- "run is not an offload.")
158
- return
159
- print(f"{'model':<26} {'used':>5} {'helped':>7} last")
160
- for model, a in sorted(data.items(), key=lambda kv: -kv[1]["offloads"]):
161
- helped = (f"{a['useful']}/{a['judged']}" if a["judged"] else "—")
162
- print(f"{model:<26} {a['offloads']:>5} {helped:>7} {a['last_task'][:44]}")
163
-
164
-
165
- if __name__ == "__main__":
166
- main()
1
+ """A record of REAL work handed to a model — not benchmark calls.
2
+
3
+ python metrics/offload.py list
4
+ python metrics/offload.py add kimi-k3 "audit the sync seam" --useful \\
5
+ --note "found fetch_assignments was never called"
6
+ python metrics/offload.py verdict p20261016-171200-a1b2.primary --useful --note "..."
7
+
8
+ WHY THIS IS WRITTEN DOWN RATHER THAN DERIVED
9
+ --------------------------------------------
10
+ The first attempt counted offloads as `logged calls − benchmark calls`, and it
11
+ was wrong in both directions. The benchmark ledger only started when it was
12
+ built, so every earlier suite run counted as work: `kimi-k3` showed 26
13
+ "offloads" and had never been given a real task. Meanwhile a model with a
14
+ 164-call floor run looked like the most-used thing here.
15
+
16
+ Subtracting one imperfect number from another gives a third number nobody can
17
+ check. An offload is a deliberate act, so it is recorded deliberately.
18
+
19
+ **And a count alone answers the wrong question.** "Which models am I using
20
+ most" matters far less than what for, and whether it helped. Each entry keeps
21
+ the task and the verdict, so the routing table can be argued from evidence
22
+ rather than from a tally.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import argparse
28
+ import collections
29
+ import datetime as dt
30
+ import json
31
+ import pathlib
32
+
33
+ ROOT = pathlib.Path(__file__).resolve().parent.parent
34
+ from paths import DATA # noqa: E402 -- the install's data folder (metrics/paths.py)
35
+ LOG = DATA / "metrics" / "offloads.jsonl"
36
+
37
+
38
+ def entries() -> list[dict]:
39
+ """Every offload, oldest first. An entry with a `call` id (written by the
40
+ bench gateway) is superseded by a later one with the same id: the gateway
41
+ records each seat unjudged as it answers, and the verdict comes after."""
42
+ if not LOG.exists():
43
+ return []
44
+ out, by_call = [], {}
45
+ from aliases import current
46
+ for line in LOG.read_text(encoding="utf-8").splitlines():
47
+ if line.strip():
48
+ try:
49
+ e = json.loads(line)
50
+ except ValueError:
51
+ continue
52
+ e["model"] = current(e.get("model", "")) # a renamed model keeps its record
53
+ if e.get("call") in by_call:
54
+ out[by_call[e["call"]]] = e
55
+ continue
56
+ if e.get("call"):
57
+ by_call[e["call"]] = len(out)
58
+ out.append(e)
59
+ return out
60
+
61
+
62
+ def add(model: str, task: str, useful: bool | None, note: str, call: str | None = None,
63
+ real: list[str] | None = None, false: list[str] | None = None) -> dict:
64
+ entry = {"date": dt.date.today().isoformat(), "model": model,
65
+ "task": task, "useful": useful, "note": note}
66
+ if call:
67
+ entry["call"] = call
68
+ # Verified claims, one sentence each: what this answer got right, and what
69
+ # it asserted that is not true. suite/mine.py turns them into tasks.
70
+ if real:
71
+ entry["real"] = real
72
+ if false:
73
+ entry["false"] = false
74
+ LOG.parent.mkdir(parents=True, exist_ok=True)
75
+ with LOG.open("a", encoding="utf-8") as fh:
76
+ fh.write(json.dumps(entry) + "\n")
77
+ return entry
78
+
79
+
80
+ def verdict(call: str, useful: bool | None, note: str,
81
+ real: list[str] | None = None, false: list[str] | None = None,
82
+ replace: bool = False) -> dict:
83
+ """Judge one seat of a bench-gateway panel by its call id. A blank note or
84
+ claim list keeps the earlier one, unless `replace` (the dashboard's form,
85
+ which shows them for editing, so blank means cleared)."""
86
+ found = next((e for e in reversed(entries()) if e.get("call") == call), None)
87
+ if found is None:
88
+ raise KeyError(f"no offload with call id {call!r}; the gateway prints them under each answer")
89
+ if replace:
90
+ real, false = real or None, false or None
91
+ else:
92
+ note, real, false = note or found.get("note", ""), real or found.get("real"), false or found.get("false")
93
+ if (useful, note, real, false) == (found.get("useful"), found.get("note", ""), found.get("real"), found.get("false")):
94
+ return found # the same verdict again writes nothing
95
+ return add(found["model"], found["task"], useful, note, call, real, false)
96
+
97
+
98
+ def summary() -> dict[str, dict]:
99
+ """Per model: how often, how often it helped, and the last thing it did."""
100
+ agg: dict[str, dict] = collections.defaultdict(
101
+ lambda: {"offloads": 0, "useful": 0, "judged": 0,
102
+ "last_task": "", "last_date": ""})
103
+ for e in entries():
104
+ a = agg[e["model"]]
105
+ a["offloads"] += 1
106
+ if e.get("useful") is not None:
107
+ a["judged"] += 1
108
+ a["useful"] += 1 if e["useful"] else 0
109
+ if e["date"] >= a["last_date"]:
110
+ a["last_date"], a["last_task"] = e["date"], e["task"]
111
+ return dict(agg)
112
+
113
+
114
+ def main() -> None:
115
+ ap = argparse.ArgumentParser(description=__doc__)
116
+ sub = ap.add_subparsers(dest="cmd", required=True)
117
+ sub.add_parser("list")
118
+ a = sub.add_parser("add")
119
+ a.add_argument("model")
120
+ a.add_argument("task", help="what it was asked to do, in a few words")
121
+ a.add_argument("--useful", dest="useful", action="store_true", default=None)
122
+ a.add_argument("--not-useful", dest="useful", action="store_false")
123
+ a.add_argument("--note", default="")
124
+ # Structured, so metrics/wrong.py can count who asserts false things.
125
+ a.add_argument("--real", action="append", metavar="CLAIM",
126
+ help="a finding this answer got right, verified; repeatable")
127
+ a.add_argument("--false", action="append", dest="false_claims", metavar="CLAIM",
128
+ help="a claim this answer made that is not true, verified; repeatable")
129
+ v = sub.add_parser("verdict", help="judge one seat of a bench-gateway panel by call id")
130
+ v.add_argument("call")
131
+ judged = v.add_mutually_exclusive_group(required=True) # a verdict without one erases nothing
132
+ judged.add_argument("--useful", dest="useful", action="store_true", default=None)
133
+ judged.add_argument("--not-useful", dest="useful", action="store_false")
134
+ v.add_argument("--note", default="")
135
+ v.add_argument("--real", action="append", metavar="CLAIM",
136
+ help="a finding this answer got right, verified; repeatable")
137
+ v.add_argument("--false", action="append", dest="false_claims", metavar="CLAIM",
138
+ help="a claim this answer made that is not true, verified; repeatable")
139
+ args = ap.parse_args()
140
+
141
+ if args.cmd == "add":
142
+ e = add(args.model, args.task, args.useful, args.note, real=args.real, false=args.false_claims)
143
+ print(f"recorded: {e['model']} — {e['task']}")
144
+ return
145
+ if args.cmd == "verdict":
146
+ try:
147
+ e = verdict(args.call, args.useful, args.note, args.real, args.false_claims)
148
+ except KeyError as exc:
149
+ raise SystemExit(exc.args[0])
150
+ print(f"recorded: {e['model']} — {e['task']} — useful={e['useful']}")
151
+ return
152
+
153
+ data = summary()
154
+ if not data:
155
+ print("No offloads recorded yet.")
156
+ print("This is empty because it counts REAL WORK only — a benchmark "
157
+ "run is not an offload.")
158
+ return
159
+ print(f"{'model':<26} {'used':>5} {'helped':>7} last")
160
+ for model, a in sorted(data.items(), key=lambda kv: -kv[1]["offloads"]):
161
+ helped = (f"{a['useful']}/{a['judged']}" if a["judged"] else "—")
162
+ print(f"{model:<26} {a['offloads']:>5} {helped:>7} {a['last_task'][:44]}")
163
+
164
+
165
+ if __name__ == "__main__":
166
+ main()