benchkit-cli 0.1.0__py3-none-any.whl

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 (149) hide show
  1. benchkit/__init__.py +1 -0
  2. benchkit/_pi_proxy.py +219 -0
  3. benchkit/answer_key_guard.ts +49 -0
  4. benchkit/artifacts.py +87 -0
  5. benchkit/benchmarks/__init__.py +105 -0
  6. benchkit/benchmarks/aider_polyglot.py +405 -0
  7. benchkit/benchmarks/arc.py +52 -0
  8. benchkit/benchmarks/banking77.py +149 -0
  9. benchkit/benchmarks/base.py +19 -0
  10. benchkit/benchmarks/boolq.py +49 -0
  11. benchkit/benchmarks/chartqa.py +133 -0
  12. benchkit/benchmarks/code_verifier.py +77 -0
  13. benchkit/benchmarks/downloads.py +166 -0
  14. benchkit/benchmarks/evalplus.py +288 -0
  15. benchkit/benchmarks/git_surgery.py +485 -0
  16. benchkit/benchmarks/gpqa.py +56 -0
  17. benchkit/benchmarks/gsm8k.py +97 -0
  18. benchkit/benchmarks/hellaswag.py +49 -0
  19. benchkit/benchmarks/humaneval.py +103 -0
  20. benchkit/benchmarks/ifeval.py +56 -0
  21. benchkit/benchmarks/ifeval_instructions.py +477 -0
  22. benchkit/benchmarks/mbpp.py +60 -0
  23. benchkit/benchmarks/mc_arena.py +479 -0
  24. benchkit/benchmarks/mcq.py +94 -0
  25. benchkit/benchmarks/medfailbench.py +537 -0
  26. benchkit/benchmarks/mmlu.py +54 -0
  27. benchkit/benchmarks/mmlu_pro.py +62 -0
  28. benchkit/benchmarks/mmvp.py +115 -0
  29. benchkit/benchmarks/ocrbench.py +96 -0
  30. benchkit/benchmarks/openbookqa.py +52 -0
  31. benchkit/benchmarks/patcheval.py +929 -0
  32. benchkit/benchmarks/piqa.py +52 -0
  33. benchkit/benchmarks/ruler.py +172 -0
  34. benchkit/benchmarks/ruler_generator.py +535 -0
  35. benchkit/benchmarks/sanity.py +174 -0
  36. benchkit/benchmarks/treejs_arena.py +268 -0
  37. benchkit/benchmarks/truthfulqa.py +52 -0
  38. benchkit/benchmarks/utils.py +25 -0
  39. benchkit/benchmarks/vision.py +230 -0
  40. benchkit/benchmarks/vstar.py +108 -0
  41. benchkit/benchmarks/winogrande.py +52 -0
  42. benchkit/benchmarks/xstest.py +116 -0
  43. benchkit/browser.py +429 -0
  44. benchkit/catalog.py +121 -0
  45. benchkit/cli.py +1000 -0
  46. benchkit/client.py +1413 -0
  47. benchkit/controls.py +87 -0
  48. benchkit/datasets/MMLU_LICENSE.txt +21 -0
  49. benchkit/datasets/README.md +142 -0
  50. benchkit/datasets/arc_challenge.jsonl +1172 -0
  51. benchkit/datasets/banking77.jsonl +3080 -0
  52. benchkit/datasets/banking77_intents.json +79 -0
  53. benchkit/datasets/boolq.jsonl +3270 -0
  54. benchkit/datasets/code_verifier.jsonl +500 -0
  55. benchkit/datasets/gpqa_diamond.jsonl +198 -0
  56. benchkit/datasets/gsm8k.jsonl +1319 -0
  57. benchkit/datasets/hellaswag.jsonl +1000 -0
  58. benchkit/datasets/humaneval.jsonl +164 -0
  59. benchkit/datasets/ifeval.jsonl +541 -0
  60. benchkit/datasets/mbpp.jsonl +500 -0
  61. benchkit/datasets/mc_blocks_1_20_1.jsonl +1003 -0
  62. benchkit/datasets/medfailbench_hard30.jsonl +30 -0
  63. benchkit/datasets/medfailbench_hard30.md +13 -0
  64. benchkit/datasets/openbookqa.jsonl +500 -0
  65. benchkit/datasets/piqa.jsonl +1838 -0
  66. benchkit/datasets/sanity_code.jsonl +6 -0
  67. benchkit/datasets/truthfulqa.jsonl +817 -0
  68. benchkit/datasets/winogrande.jsonl +1267 -0
  69. benchkit/datasets/xstest.csv +451 -0
  70. benchkit/decision.py +194 -0
  71. benchkit/demo.py +451 -0
  72. benchkit/dockerfiles.py +247 -0
  73. benchkit/engine.py +1270 -0
  74. benchkit/evaluation.py +195 -0
  75. benchkit/events.py +141 -0
  76. benchkit/executor.py +96 -0
  77. benchkit/git_surgery/backport-release-stack/setup.sh +328 -0
  78. benchkit/git_surgery/backport-release-stack/verify.sh +119 -0
  79. benchkit/git_surgery/bisect-the-regression/setup.sh +49 -0
  80. benchkit/git_surgery/bisect-the-regression/verify.sh +34 -0
  81. benchkit/git_surgery/rebase-conflict-chain/setup.sh +65 -0
  82. benchkit/git_surgery/rebase-conflict-chain/verify.sh +43 -0
  83. benchkit/git_surgery/recover-complex-stash/setup.sh +152 -0
  84. benchkit/git_surgery/recover-complex-stash/verify.sh +95 -0
  85. benchkit/git_surgery/recover-lost-work/setup.sh +40 -0
  86. benchkit/git_surgery/recover-lost-work/verify.sh +36 -0
  87. benchkit/git_surgery/repair-force-pushed-remote/setup.sh +171 -0
  88. benchkit/git_surgery/repair-force-pushed-remote/verify.sh +83 -0
  89. benchkit/git_surgery/revert-merge-with-followups/setup.sh +198 -0
  90. benchkit/git_surgery/revert-merge-with-followups/verify.sh +98 -0
  91. benchkit/git_surgery/secret-in-history/README.md +27 -0
  92. benchkit/git_surgery/secret-in-history/setup.sh +107 -0
  93. benchkit/git_surgery/secret-in-history/verify.sh +106 -0
  94. benchkit/git_surgery/split-the-mega-commit/setup.sh +50 -0
  95. benchkit/git_surgery/split-the-mega-commit/verify.sh +48 -0
  96. benchkit/git_surgery/untangle-nested-submodules/setup.sh +193 -0
  97. benchkit/git_surgery/untangle-nested-submodules/verify.sh +90 -0
  98. benchkit/history.py +474 -0
  99. benchkit/jobs.py +332 -0
  100. benchkit/leakage.py +51 -0
  101. benchkit/looping.py +873 -0
  102. benchkit/mc_render.py +274 -0
  103. benchkit/mc_viewer/dist/atlas.png +0 -0
  104. benchkit/mc_viewer/dist/blockStates.json.gz +0 -0
  105. benchkit/mc_viewer/dist/version.json +4 -0
  106. benchkit/mc_viewer/dist/viewer.html +23 -0
  107. benchkit/mc_viewer/dist/viewer.js +2 -0
  108. benchkit/mc_viewer/dist/viewer.js.LICENSE.txt +25 -0
  109. benchkit/mc_viewer/dist/worker.js +2 -0
  110. benchkit/mc_viewer/dist/worker.js.LICENSE.txt +19 -0
  111. benchkit/metrics.py +100 -0
  112. benchkit/ollama.py +60 -0
  113. benchkit/perf.py +353 -0
  114. benchkit/perf_report.py +207 -0
  115. benchkit/perturbations.py +208 -0
  116. benchkit/pi_agent.py +476 -0
  117. benchkit/pi_package/package-lock.json +1827 -0
  118. benchkit/pi_package/package.json +8 -0
  119. benchkit/report.py +142 -0
  120. benchkit/report_markdown.py +645 -0
  121. benchkit/results.py +688 -0
  122. benchkit/retry.py +163 -0
  123. benchkit/runner.py +769 -0
  124. benchkit/sandbox.py +1167 -0
  125. benchkit/template_check.py +291 -0
  126. benchkit/templates/arena.html +504 -0
  127. benchkit/templates/history.html +58 -0
  128. benchkit/templates/perf.html +509 -0
  129. benchkit/templates/report.html +769 -0
  130. benchkit/tui/__init__.py +5 -0
  131. benchkit/tui/app.py +87 -0
  132. benchkit/tui/app.tcss +512 -0
  133. benchkit/tui/formatting.py +84 -0
  134. benchkit/tui/screens/__init__.py +1 -0
  135. benchkit/tui/screens/connect.py +191 -0
  136. benchkit/tui/screens/detail.py +288 -0
  137. benchkit/tui/screens/help.py +84 -0
  138. benchkit/tui/screens/modals.py +544 -0
  139. benchkit/tui/screens/results.py +361 -0
  140. benchkit/tui/screens/run.py +988 -0
  141. benchkit/tui/screens/setup.py +733 -0
  142. benchkit/tui/theme.py +135 -0
  143. benchkit/tui/widgets.py +120 -0
  144. benchkit_cli-0.1.0.dist-info/METADATA +294 -0
  145. benchkit_cli-0.1.0.dist-info/RECORD +149 -0
  146. benchkit_cli-0.1.0.dist-info/WHEEL +5 -0
  147. benchkit_cli-0.1.0.dist-info/entry_points.txt +3 -0
  148. benchkit_cli-0.1.0.dist-info/licenses/LICENSE +201 -0
  149. benchkit_cli-0.1.0.dist-info/top_level.txt +1 -0
benchkit/mc_render.py ADDED
@@ -0,0 +1,274 @@
1
+ """Fixed-camera Minecraft build rendering for the mc-arena suite.
2
+
3
+ The renderer is prismarine-viewer, pinned to one Minecraft version and built
4
+ into ``mc_viewer/dist`` (see ``mc_viewer/build/README.md``). This module is the
5
+ Python half: it serves that bundle over a loopback HTTP server, hands one build
6
+ to the page, and photographs the result from three frozen camera positions.
7
+
8
+ Two builds are only comparable if they were photographed the same way, so the
9
+ volume, the camera rig, the lighting and the image size all live in the page's
10
+ own source and never vary per model or per run.
11
+
12
+ The page here is BenchKit's own code, not the model's. A model contributes a
13
+ list of block positions and nothing executable - its script already ran, under
14
+ Docker, in :func:`benchkit.sandbox.run_python_script`.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import atexit
20
+ import contextlib
21
+ import functools
22
+ import http.server
23
+ import json
24
+ import shutil
25
+ import socketserver
26
+ import tempfile
27
+ import threading
28
+ from dataclasses import dataclass, field
29
+ from importlib.resources import files
30
+ from pathlib import Path
31
+
32
+ from benchkit.browser import chromium_executable, launch_args
33
+
34
+ # Frozen alongside the camera rig in ``mc_viewer/build/entry.js``.
35
+ VIEWS = ("iso", "side", "top")
36
+ BUILD_SIZE = 32
37
+ DEFAULT_TIMEOUT_S = 120.0
38
+
39
+
40
+ @dataclass
41
+ class MCRenderResult:
42
+ """What one build's three photographs came back with."""
43
+
44
+ rendered: bool = False
45
+ views: dict[str, str] = field(default_factory=dict)
46
+ contact_sheet: str = ""
47
+ # Blocks the renderer placed, and ones its block table did not recognise.
48
+ placed: int = 0
49
+ unresolved: int = 0
50
+ page_errors: list[str] = field(default_factory=list)
51
+ console_errors: list[str] = field(default_factory=list)
52
+ # Set when this machine could not render at all (no browser, no WebGL).
53
+ # mc-arena scores the block list, not the picture, so this is a missing
54
+ # illustration rather than a failed task.
55
+ error: str = ""
56
+
57
+ @property
58
+ def diagnostics(self) -> str:
59
+ lines = [f"[uncaught] {message}" for message in self.page_errors]
60
+ lines += [f"[console] {message}" for message in self.console_errors]
61
+ if self.error:
62
+ lines.append(f"[renderer] {self.error}")
63
+ return "\n".join(lines)
64
+
65
+
66
+ def dist_dir() -> Path:
67
+ """The vendored prismarine-viewer bundle shipped inside the package."""
68
+ return Path(str(files("benchkit").joinpath("mc_viewer/dist")))
69
+
70
+
71
+ def viewer_version() -> dict:
72
+ """The pinned Minecraft version and prismarine-viewer commit."""
73
+ try:
74
+ return json.loads((dist_dir() / "version.json").read_text(encoding="utf-8"))
75
+ except (OSError, ValueError):
76
+ return {}
77
+
78
+
79
+ class _QuietHandler(http.server.SimpleHTTPRequestHandler):
80
+ """The viewer's own asset requests are not run output."""
81
+
82
+ def log_message(self, *_args: object) -> None:
83
+ return
84
+
85
+ def do_GET(self) -> None:
86
+ # Chromium asks every page for a favicon; a 404 for one would be
87
+ # recorded as a console error against the build.
88
+ if self.path == "/favicon.ico":
89
+ self.send_response(204)
90
+ self.end_headers()
91
+ return
92
+ super().do_GET()
93
+
94
+
95
+ @dataclass
96
+ class _Site:
97
+ """One loopback web server over a copy of the viewer bundle.
98
+
99
+ prismarine-viewer meshes chunks in a web worker, and Chromium refuses to
100
+ start a worker from a ``file://`` page, so the bundle is served over HTTP
101
+ on 127.0.0.1 instead. Nothing outside this machine can reach it.
102
+ """
103
+
104
+ root: Path
105
+ port: int
106
+ server: socketserver.TCPServer
107
+
108
+ def url(self, build: str) -> str:
109
+ return f"http://127.0.0.1:{self.port}/viewer.html?build={build}"
110
+
111
+
112
+ _SITE: _Site | None = None
113
+ _SITE_LOCK = threading.Lock()
114
+ _BUILD_COUNTER = 0
115
+
116
+
117
+ def _site() -> _Site:
118
+ """Start this process's viewer server on first use."""
119
+ global _SITE
120
+ with _SITE_LOCK:
121
+ if _SITE is not None:
122
+ return _SITE
123
+ root = Path(tempfile.mkdtemp(prefix="benchkit-mc-viewer-"))
124
+ for asset in dist_dir().iterdir():
125
+ if asset.is_file():
126
+ shutil.copyfile(asset, root / asset.name)
127
+ handler = functools.partial(_QuietHandler, directory=str(root))
128
+ server = socketserver.ThreadingTCPServer(("127.0.0.1", 0), handler)
129
+ server.daemon_threads = True
130
+ threading.Thread(
131
+ target=server.serve_forever,
132
+ name="benchkit-mc-viewer",
133
+ daemon=True,
134
+ ).start()
135
+ _SITE = _Site(root=root, port=server.server_address[1], server=server)
136
+ atexit.register(shutdown)
137
+ return _SITE
138
+
139
+
140
+ def shutdown() -> None:
141
+ """Stop the viewer server and remove its copy of the bundle."""
142
+ global _SITE
143
+ with _SITE_LOCK:
144
+ site, _SITE = _SITE, None
145
+ if site is None:
146
+ return
147
+ site.server.shutdown()
148
+ site.server.server_close()
149
+ shutil.rmtree(site.root, ignore_errors=True)
150
+
151
+
152
+ def _stage_build(blocks: list[dict]) -> str:
153
+ """Write one build next to the viewer and return the name to fetch it by."""
154
+ global _BUILD_COUNTER
155
+ site = _site()
156
+ with _SITE_LOCK:
157
+ _BUILD_COUNTER += 1
158
+ name = f"build-{_BUILD_COUNTER:05d}.json"
159
+ (site.root / name).write_text(json.dumps(blocks), encoding="utf-8")
160
+ return name
161
+
162
+
163
+ def _render(
164
+ blocks: list[dict],
165
+ out_dir: Path,
166
+ timeout_s: float,
167
+ result: MCRenderResult,
168
+ ) -> None:
169
+ """Photograph one build and fill ``result`` in place."""
170
+ try:
171
+ from playwright.sync_api import Error as PlaywrightError
172
+ from playwright.sync_api import sync_playwright
173
+ except ImportError:
174
+ result.error = (
175
+ "playwright is not installed; install the browser extra "
176
+ "(uv sync --extra browser) and run 'playwright install chromium'"
177
+ )
178
+ return
179
+
180
+ site = _site()
181
+ staged = _stage_build(blocks)
182
+ url = site.url(staged)
183
+ timeout_ms = timeout_s * 1000
184
+ try:
185
+ with sync_playwright() as playwright:
186
+ browser = playwright.chromium.launch(
187
+ args=launch_args(),
188
+ executable_path=chromium_executable(),
189
+ )
190
+ try:
191
+ context = browser.new_context(
192
+ # Wide enough for the three views side by side.
193
+ viewport={"width": 2000, "height": 760},
194
+ accept_downloads=False,
195
+ service_workers="block",
196
+ )
197
+ context.set_default_timeout(timeout_ms)
198
+ page = context.new_page()
199
+ page.on(
200
+ "console",
201
+ lambda message: (
202
+ result.console_errors.append(message.text)
203
+ if message.type == "error"
204
+ else None
205
+ ),
206
+ )
207
+ page.on(
208
+ "pageerror", lambda error: result.page_errors.append(str(error))
209
+ )
210
+ page.goto(url, wait_until="load", timeout=timeout_ms)
211
+ page.wait_for_function(
212
+ "window.__mcarena !== undefined", timeout=timeout_ms
213
+ )
214
+ state = page.evaluate("window.__mcarena") or {}
215
+ if not state.get("ok"):
216
+ result.error = str(state.get("error") or "the viewer did not run")
217
+ return
218
+
219
+ result.placed = int(state.get("placed") or 0)
220
+ result.unresolved = int(state.get("unresolved") or 0)
221
+ out_dir.mkdir(parents=True, exist_ok=True)
222
+ for view in VIEWS:
223
+ shot = out_dir / f"{view}.png"
224
+ page.locator(f"#view-{view}").screenshot(
225
+ path=str(shot), timeout=timeout_ms
226
+ )
227
+ result.views[view] = str(shot)
228
+ sheet = out_dir / "views.png"
229
+ page.locator("#views").screenshot(path=str(sheet), timeout=timeout_ms)
230
+ result.contact_sheet = str(sheet)
231
+ result.rendered = True
232
+ finally:
233
+ browser.close()
234
+ except PlaywrightError as exc:
235
+ message = str(exc).strip().splitlines()[0] if str(exc).strip() else repr(exc)
236
+ if "executable doesn't exist" in message.lower():
237
+ result.error = (
238
+ "chromium is not installed for playwright (run "
239
+ "'playwright install chromium', or set BENCHKIT_RENDER_CHROMIUM "
240
+ f"to an existing build): {message}"
241
+ )
242
+ else:
243
+ result.error = message
244
+ except Exception as exc: # pragma: no cover - defensive
245
+ result.error = f"{type(exc).__name__}: {exc}"
246
+ finally:
247
+ # The viewer has read it by now, and a long run would otherwise leave
248
+ # one of these behind per task until the process exits.
249
+ with contextlib.suppress(OSError):
250
+ (site.root / staged).unlink()
251
+
252
+
253
+ def render_build(
254
+ blocks: list[dict],
255
+ out_dir: Path,
256
+ *,
257
+ timeout_s: float = DEFAULT_TIMEOUT_S,
258
+ ) -> MCRenderResult:
259
+ """Render one block list from three fixed cameras into ``out_dir``."""
260
+ result = MCRenderResult()
261
+ # Playwright's sync API refuses to run in a thread with a live asyncio
262
+ # loop, which is exactly where the TUI evaluates tasks.
263
+ worker = threading.Thread(
264
+ target=_render,
265
+ args=(blocks, out_dir, timeout_s, result),
266
+ name="benchkit-mc-render",
267
+ daemon=True,
268
+ )
269
+ worker.start()
270
+ worker.join(timeout=timeout_s + 60.0)
271
+ if worker.is_alive():
272
+ result.rendered = False
273
+ result.error = f"the build did not render within its {timeout_s:g}s budget"
274
+ return result
Binary file
@@ -0,0 +1,4 @@
1
+ {
2
+ "minecraft_version": "1.20.1",
3
+ "prismarine_viewer": "mc-bench/prismarine-viewer@c2c89a16fd65f05b017089f7b2246ec92218a963"
4
+ }
@@ -0,0 +1,23 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>BenchKit mc-arena viewer</title>
6
+ <style>
7
+ :root { color-scheme: light; }
8
+ body { margin: 0; background: #ffffff; font: 13px "Avenir Next", Avenir, sans-serif; color: #0b0b0b; }
9
+ #views { display: flex; gap: 0; align-items: flex-start; }
10
+ figure { margin: 0; }
11
+ img { display: block; }
12
+ figcaption { padding: 6px 8px; color: #6b7280; text-transform: uppercase; letter-spacing: .08em; font-size: 11px; }
13
+ /* Interactive mode: one canvas, filling the window, no captions. */
14
+ body.interactive { overflow: hidden; background: #dfe6ee; }
15
+ body.interactive #views { display: block; }
16
+ body.interactive canvas { display: block; width: 100vw; height: 100vh; }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <div id="views"></div>
21
+ <script src="viewer.js"></script>
22
+ </body>
23
+ </html>