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
@@ -0,0 +1,115 @@
1
+ """MMVP - 150 pairs of look-alike images that CLIP-style encoders confuse.
2
+
3
+ Each pair asks the same two-option question about two images that differ in
4
+ one visual detail (orientation, count, state, colour), and the right answer
5
+ differs between them. The official score is pair accuracy: both questions of
6
+ a pair must be right, so a model that ignores the image sits at 25%, not 50%.
7
+ Plain per-question accuracy is the headline score so BenchKit's averages and
8
+ decision calibration stay comparable; pair accuracy is reported next to it.
9
+ Pairs are consecutive, so even-sized slices keep them whole.
10
+ """
11
+
12
+ import csv
13
+ import io
14
+ import re
15
+ from functools import cache
16
+
17
+ from benchkit.benchmarks.base import Task
18
+ from benchkit.benchmarks.mcq import extract_choice
19
+ from benchkit.benchmarks.utils import strip_think_tags
20
+ from benchkit.benchmarks.vision import load_files, vision_dataset
21
+
22
+ DATASET = "MMVP/MMVP"
23
+ MANIFEST = "Questions.csv"
24
+ REVISION = "37eafecab8a3940c50c2ade5b36de69dbc99a8cf"
25
+
26
+ SYSTEM = "Answer the question about the image. Reply with ONLY the letter (A or B)."
27
+
28
+ _OPTIONS = re.compile(r"\(a\)\s*(.+?)\s*\(b\)\s*(.+)", re.IGNORECASE)
29
+
30
+
31
+ def parse_manifest(data: bytes) -> list[dict]:
32
+ rows = []
33
+ for row in csv.DictReader(io.StringIO(data.decode("utf-8-sig"))):
34
+ index = int(row["Index"])
35
+ options = _OPTIONS.fullmatch(row["Options"].strip())
36
+ if options is None:
37
+ raise ValueError(f"MMVP row {index}: unexpected options {row['Options']}")
38
+ rows.append(
39
+ {
40
+ "task_id": f"mmvp/{index}",
41
+ "pair": (index + 1) // 2,
42
+ "question": row["Question"].strip(),
43
+ "choices": [options.group(1).strip(), options.group(2).strip()],
44
+ "answer": row["Correct Answer"].strip().strip("()").upper(),
45
+ "image": f"MMVP Images/{index}.jpg",
46
+ }
47
+ )
48
+ return rows
49
+
50
+
51
+ @cache
52
+ def _rows() -> tuple[dict, ...]:
53
+ return tuple(load_files("mmvp", DATASET, MANIFEST, REVISION, parse_manifest))
54
+
55
+
56
+ def pair_accuracy(records: list[object]) -> dict:
57
+ """Share of pairs with both questions right, over pairs fully scored."""
58
+ pair_of = {row["task_id"]: row["pair"] for row in _rows()}
59
+ pairs: dict[int, list[float]] = {}
60
+ for record in records:
61
+ pair = pair_of.get(record.task_id)
62
+ if pair is not None:
63
+ pairs.setdefault(pair, []).append(float(getattr(record, "score", 0.0)))
64
+ complete = [scores for scores in pairs.values() if len(scores) == 2]
65
+ if not complete:
66
+ return {}
67
+ passed = sum(all(score >= 1.0 for score in scores) for scores in complete)
68
+ return {
69
+ "pair_accuracy": round(100 * passed / len(complete), 1),
70
+ "pairs": len(complete),
71
+ }
72
+
73
+
74
+ class MMVP:
75
+ name = "mmvp"
76
+ vision = True
77
+ task_count = 300
78
+ data_source = vision_dataset(
79
+ "mmvp", REVISION, "MMVP image pairs from Hugging Face", _rows
80
+ )
81
+ decision_instructions = (
82
+ "Which option correctly answers the question about the image?"
83
+ )
84
+
85
+ def load_tasks(self) -> list[Task]:
86
+ return [
87
+ Task(
88
+ id=row["task_id"],
89
+ prompt=row["question"],
90
+ metadata={
91
+ "choices": row["choices"],
92
+ "answer": row["answer"],
93
+ "images": [row["image"]],
94
+ },
95
+ )
96
+ for row in _rows()
97
+ ]
98
+
99
+ def build_prompt(self, task: Task) -> str:
100
+ choices = "\n".join(
101
+ f"{letter}) {choice}"
102
+ for letter, choice in zip("AB", task.metadata["choices"], strict=True)
103
+ )
104
+ return f"{SYSTEM}\n\n{task.prompt}\n\n{choices}"
105
+
106
+ def result_metadata(self, variant: str | None = None) -> dict:
107
+ return {"dataset_revision": REVISION}
108
+
109
+ def evaluate(self, task: Task, response: str) -> bool:
110
+ text = strip_think_tags(response)
111
+ choice = extract_choice(text, task.metadata["choices"], letters="AB")
112
+ return choice == task.metadata["answer"]
113
+
114
+ def summary_fields(self, records: list[object]) -> dict:
115
+ return pair_accuracy(records)
@@ -0,0 +1,96 @@
1
+ """OCRBench - text recognition and text-centric VQA over 1,000 images.
2
+
3
+ Ten categories from scene text to handwritten math. Scoring follows the
4
+ official OCRBench evaluator: a task passes when any reference answer appears
5
+ in the response, case-insensitively. Handwritten math (HME100k) compares with
6
+ all whitespace removed and keeps case, since LaTeX is case-sensitive. The
7
+ question is sent as written, with no extra instructions, as upstream does.
8
+ """
9
+
10
+ from functools import cache
11
+
12
+ from benchkit.benchmarks.base import Task
13
+ from benchkit.benchmarks.utils import strip_think_tags
14
+ from benchkit.benchmarks.vision import (
15
+ category_scores,
16
+ load_rows,
17
+ spread,
18
+ vision_dataset,
19
+ )
20
+ from benchkit.evaluation import EvaluationResult
21
+
22
+ DATASET = "echo840/OCRBench"
23
+ PARQUET = "data/test-00000-of-00001.parquet"
24
+ REVISION = "92a54bd1384387c178d5a07140a2d85e0a3d12e1"
25
+
26
+
27
+ def _convert(index: int, row: dict) -> dict:
28
+ return {
29
+ "task_id": f"ocrbench/{index}",
30
+ "source": row["dataset"],
31
+ "category": row["question_type"],
32
+ "question": row["question"],
33
+ "answers": list(row["answer"]),
34
+ }
35
+
36
+
37
+ def matches(response: str, answers: list[str], source: str) -> bool:
38
+ predicted = strip_think_tags(response).strip().replace("\n", " ")
39
+ if source == "HME100k":
40
+ predicted = predicted.replace(" ", "")
41
+ return any(answer.strip().replace(" ", "") in predicted for answer in answers)
42
+ predicted = predicted.lower()
43
+ return any(
44
+ answer.lower().strip().replace("\n", " ") in predicted for answer in answers
45
+ )
46
+
47
+
48
+ @cache
49
+ def _rows() -> tuple[dict, ...]:
50
+ return tuple(load_rows("ocrbench", DATASET, PARQUET, REVISION, _convert))
51
+
52
+
53
+ class OCRBench:
54
+ name = "ocrbench"
55
+ vision = True
56
+ task_count = 1000
57
+ data_source = vision_dataset(
58
+ "ocrbench", REVISION, "OCRBench images from Hugging Face", _rows
59
+ )
60
+
61
+ def load_tasks(self) -> list[Task]:
62
+ tasks = [
63
+ Task(
64
+ id=row["task_id"],
65
+ prompt=row["question"],
66
+ metadata={
67
+ "answers": row["answers"],
68
+ "source": row["source"],
69
+ "category": row["category"],
70
+ "images": [row["image"]],
71
+ },
72
+ )
73
+ for row in _rows()
74
+ ]
75
+ return spread(tasks, lambda task: task.metadata["category"])
76
+
77
+ def build_prompt(self, task: Task) -> str:
78
+ return task.prompt
79
+
80
+ def result_metadata(self, variant: str | None = None) -> dict:
81
+ return {"dataset_revision": REVISION}
82
+
83
+ def evaluate_with_feedback(self, task: Task, response: str) -> EvaluationResult:
84
+ passed = matches(response, task.metadata["answers"], task.metadata["source"])
85
+ return EvaluationResult(
86
+ score=float(passed),
87
+ feedback="" if passed else "The answer does not match the image text.",
88
+ )
89
+
90
+ def evaluate(self, task: Task, response: str) -> bool:
91
+ return matches(response, task.metadata["answers"], task.metadata["source"])
92
+
93
+ def summary_fields(self, records: list[object]) -> dict:
94
+ categories = {row["task_id"]: row["category"] for row in _rows()}
95
+ scores = category_scores(records, categories)
96
+ return {"category_scores": scores} if scores else {}
@@ -0,0 +1,52 @@
1
+ """OpenBookQA benchmark - elementary science multiple choice reasoning."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from benchkit.benchmarks.base import Task
7
+ from benchkit.benchmarks.mcq import extract_choice
8
+ from benchkit.benchmarks.utils import strip_think_tags
9
+
10
+ DATASET = Path(__file__).parent.parent / "datasets" / "openbookqa.jsonl"
11
+
12
+ SYSTEM = (
13
+ "Answer the following multiple choice science question. "
14
+ "Reply with ONLY the letter (A, B, C, or D)."
15
+ )
16
+
17
+
18
+ def _format_choices(choices: list[str]) -> str:
19
+ return "\n".join(
20
+ f"{letter}) {choice}" for letter, choice in zip("ABCD", choices, strict=False)
21
+ )
22
+
23
+
24
+ class OpenBookQA:
25
+ name = "openbookqa"
26
+ decision_instructions = "Which option correctly answers this science question?"
27
+
28
+ def load_tasks(self) -> list[Task]:
29
+ tasks = []
30
+ with open(DATASET) as f:
31
+ for line in f:
32
+ d = json.loads(line)
33
+ tasks.append(
34
+ Task(
35
+ id=d["task_id"],
36
+ prompt=d["question"],
37
+ metadata={
38
+ "choices": d["choices"],
39
+ "answer": d["answer"],
40
+ },
41
+ )
42
+ )
43
+ return tasks
44
+
45
+ def build_prompt(self, task: Task) -> str:
46
+ choices = _format_choices(task.metadata["choices"])
47
+ return f"{SYSTEM}\n\n{task.prompt}\n\n{choices}"
48
+
49
+ def evaluate(self, task: Task, response: str) -> bool:
50
+ text = strip_think_tags(response)
51
+ choice = extract_choice(text, task.metadata["choices"])
52
+ return choice == task.metadata["answer"]