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.
- benchkit/__init__.py +1 -0
- benchkit/_pi_proxy.py +219 -0
- benchkit/answer_key_guard.ts +49 -0
- benchkit/artifacts.py +87 -0
- benchkit/benchmarks/__init__.py +105 -0
- benchkit/benchmarks/aider_polyglot.py +405 -0
- benchkit/benchmarks/arc.py +52 -0
- benchkit/benchmarks/banking77.py +149 -0
- benchkit/benchmarks/base.py +19 -0
- benchkit/benchmarks/boolq.py +49 -0
- benchkit/benchmarks/chartqa.py +133 -0
- benchkit/benchmarks/code_verifier.py +77 -0
- benchkit/benchmarks/downloads.py +166 -0
- benchkit/benchmarks/evalplus.py +288 -0
- benchkit/benchmarks/git_surgery.py +485 -0
- benchkit/benchmarks/gpqa.py +56 -0
- benchkit/benchmarks/gsm8k.py +97 -0
- benchkit/benchmarks/hellaswag.py +49 -0
- benchkit/benchmarks/humaneval.py +103 -0
- benchkit/benchmarks/ifeval.py +56 -0
- benchkit/benchmarks/ifeval_instructions.py +477 -0
- benchkit/benchmarks/mbpp.py +60 -0
- benchkit/benchmarks/mc_arena.py +479 -0
- benchkit/benchmarks/mcq.py +94 -0
- benchkit/benchmarks/medfailbench.py +537 -0
- benchkit/benchmarks/mmlu.py +54 -0
- benchkit/benchmarks/mmlu_pro.py +62 -0
- benchkit/benchmarks/mmvp.py +115 -0
- benchkit/benchmarks/ocrbench.py +96 -0
- benchkit/benchmarks/openbookqa.py +52 -0
- benchkit/benchmarks/patcheval.py +929 -0
- benchkit/benchmarks/piqa.py +52 -0
- benchkit/benchmarks/ruler.py +172 -0
- benchkit/benchmarks/ruler_generator.py +535 -0
- benchkit/benchmarks/sanity.py +174 -0
- benchkit/benchmarks/treejs_arena.py +268 -0
- benchkit/benchmarks/truthfulqa.py +52 -0
- benchkit/benchmarks/utils.py +25 -0
- benchkit/benchmarks/vision.py +230 -0
- benchkit/benchmarks/vstar.py +108 -0
- benchkit/benchmarks/winogrande.py +52 -0
- benchkit/benchmarks/xstest.py +116 -0
- benchkit/browser.py +429 -0
- benchkit/catalog.py +121 -0
- benchkit/cli.py +1000 -0
- benchkit/client.py +1413 -0
- benchkit/controls.py +87 -0
- benchkit/datasets/MMLU_LICENSE.txt +21 -0
- benchkit/datasets/README.md +142 -0
- benchkit/datasets/arc_challenge.jsonl +1172 -0
- benchkit/datasets/banking77.jsonl +3080 -0
- benchkit/datasets/banking77_intents.json +79 -0
- benchkit/datasets/boolq.jsonl +3270 -0
- benchkit/datasets/code_verifier.jsonl +500 -0
- benchkit/datasets/gpqa_diamond.jsonl +198 -0
- benchkit/datasets/gsm8k.jsonl +1319 -0
- benchkit/datasets/hellaswag.jsonl +1000 -0
- benchkit/datasets/humaneval.jsonl +164 -0
- benchkit/datasets/ifeval.jsonl +541 -0
- benchkit/datasets/mbpp.jsonl +500 -0
- benchkit/datasets/mc_blocks_1_20_1.jsonl +1003 -0
- benchkit/datasets/medfailbench_hard30.jsonl +30 -0
- benchkit/datasets/medfailbench_hard30.md +13 -0
- benchkit/datasets/openbookqa.jsonl +500 -0
- benchkit/datasets/piqa.jsonl +1838 -0
- benchkit/datasets/sanity_code.jsonl +6 -0
- benchkit/datasets/truthfulqa.jsonl +817 -0
- benchkit/datasets/winogrande.jsonl +1267 -0
- benchkit/datasets/xstest.csv +451 -0
- benchkit/decision.py +194 -0
- benchkit/demo.py +451 -0
- benchkit/dockerfiles.py +247 -0
- benchkit/engine.py +1270 -0
- benchkit/evaluation.py +195 -0
- benchkit/events.py +141 -0
- benchkit/executor.py +96 -0
- benchkit/git_surgery/backport-release-stack/setup.sh +328 -0
- benchkit/git_surgery/backport-release-stack/verify.sh +119 -0
- benchkit/git_surgery/bisect-the-regression/setup.sh +49 -0
- benchkit/git_surgery/bisect-the-regression/verify.sh +34 -0
- benchkit/git_surgery/rebase-conflict-chain/setup.sh +65 -0
- benchkit/git_surgery/rebase-conflict-chain/verify.sh +43 -0
- benchkit/git_surgery/recover-complex-stash/setup.sh +152 -0
- benchkit/git_surgery/recover-complex-stash/verify.sh +95 -0
- benchkit/git_surgery/recover-lost-work/setup.sh +40 -0
- benchkit/git_surgery/recover-lost-work/verify.sh +36 -0
- benchkit/git_surgery/repair-force-pushed-remote/setup.sh +171 -0
- benchkit/git_surgery/repair-force-pushed-remote/verify.sh +83 -0
- benchkit/git_surgery/revert-merge-with-followups/setup.sh +198 -0
- benchkit/git_surgery/revert-merge-with-followups/verify.sh +98 -0
- benchkit/git_surgery/secret-in-history/README.md +27 -0
- benchkit/git_surgery/secret-in-history/setup.sh +107 -0
- benchkit/git_surgery/secret-in-history/verify.sh +106 -0
- benchkit/git_surgery/split-the-mega-commit/setup.sh +50 -0
- benchkit/git_surgery/split-the-mega-commit/verify.sh +48 -0
- benchkit/git_surgery/untangle-nested-submodules/setup.sh +193 -0
- benchkit/git_surgery/untangle-nested-submodules/verify.sh +90 -0
- benchkit/history.py +474 -0
- benchkit/jobs.py +332 -0
- benchkit/leakage.py +51 -0
- benchkit/looping.py +873 -0
- benchkit/mc_render.py +274 -0
- benchkit/mc_viewer/dist/atlas.png +0 -0
- benchkit/mc_viewer/dist/blockStates.json.gz +0 -0
- benchkit/mc_viewer/dist/version.json +4 -0
- benchkit/mc_viewer/dist/viewer.html +23 -0
- benchkit/mc_viewer/dist/viewer.js +2 -0
- benchkit/mc_viewer/dist/viewer.js.LICENSE.txt +25 -0
- benchkit/mc_viewer/dist/worker.js +2 -0
- benchkit/mc_viewer/dist/worker.js.LICENSE.txt +19 -0
- benchkit/metrics.py +100 -0
- benchkit/ollama.py +60 -0
- benchkit/perf.py +353 -0
- benchkit/perf_report.py +207 -0
- benchkit/perturbations.py +208 -0
- benchkit/pi_agent.py +476 -0
- benchkit/pi_package/package-lock.json +1827 -0
- benchkit/pi_package/package.json +8 -0
- benchkit/report.py +142 -0
- benchkit/report_markdown.py +645 -0
- benchkit/results.py +688 -0
- benchkit/retry.py +163 -0
- benchkit/runner.py +769 -0
- benchkit/sandbox.py +1167 -0
- benchkit/template_check.py +291 -0
- benchkit/templates/arena.html +504 -0
- benchkit/templates/history.html +58 -0
- benchkit/templates/perf.html +509 -0
- benchkit/templates/report.html +769 -0
- benchkit/tui/__init__.py +5 -0
- benchkit/tui/app.py +87 -0
- benchkit/tui/app.tcss +512 -0
- benchkit/tui/formatting.py +84 -0
- benchkit/tui/screens/__init__.py +1 -0
- benchkit/tui/screens/connect.py +191 -0
- benchkit/tui/screens/detail.py +288 -0
- benchkit/tui/screens/help.py +84 -0
- benchkit/tui/screens/modals.py +544 -0
- benchkit/tui/screens/results.py +361 -0
- benchkit/tui/screens/run.py +988 -0
- benchkit/tui/screens/setup.py +733 -0
- benchkit/tui/theme.py +135 -0
- benchkit/tui/widgets.py +120 -0
- benchkit_cli-0.1.0.dist-info/METADATA +294 -0
- benchkit_cli-0.1.0.dist-info/RECORD +149 -0
- benchkit_cli-0.1.0.dist-info/WHEEL +5 -0
- benchkit_cli-0.1.0.dist-info/entry_points.txt +3 -0
- benchkit_cli-0.1.0.dist-info/licenses/LICENSE +201 -0
- 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"]
|