benchkit-cli 0.1.2__tar.gz → 0.2.0__tar.gz
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_cli-0.1.2 → benchkit_cli-0.2.0}/PKG-INFO +3 -1
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/README.md +2 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/pyproject.toml +1 -1
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/__init__.py +14 -0
- benchkit_cli-0.2.0/src/benchkit/benchmarks/cruxeval.py +380 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/ruler_generator.py +47 -17
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/README.md +14 -0
- benchkit_cli-0.2.0/src/benchkit/datasets/cruxeval.jsonl +800 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/executor.py +12 -2
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit_cli.egg-info/PKG-INFO +3 -1
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit_cli.egg-info/SOURCES.txt +3 -0
- benchkit_cli-0.2.0/tests/test_cruxeval.py +139 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_ruler.py +34 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/LICENSE +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/setup.cfg +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/__init__.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/_pi_proxy.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/answer_key_guard.ts +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/artifacts.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/aider_polyglot.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/arc.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/banking77.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/base.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/boolq.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/chartqa.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/code_verifier.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/downloads.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/evalplus.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/git_surgery.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/gpqa.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/gsm8k.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/hellaswag.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/humaneval.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/ifeval.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/ifeval_instructions.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/mbpp.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/mc_arena.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/mcq.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/medfailbench.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/mmlu.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/mmlu_pro.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/mmvp.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/ocrbench.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/openbookqa.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/patcheval.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/piqa.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/ruler.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/sanity.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/treejs_arena.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/truthfulqa.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/utils.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/vision.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/vstar.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/winogrande.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/benchmarks/xstest.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/browser.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/catalog.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/cli.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/client.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/config.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/controls.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/MMLU_LICENSE.txt +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/arc_challenge.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/banking77.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/banking77_intents.json +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/boolq.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/code_verifier.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/gpqa_diamond.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/gsm8k.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/hellaswag.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/humaneval.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/ifeval.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/mbpp.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/mc_blocks_1_20_1.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/medfailbench_hard30.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/medfailbench_hard30.md +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/openbookqa.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/piqa.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/sanity_code.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/truthfulqa.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/winogrande.jsonl +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/datasets/xstest.csv +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/decision.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/demo.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/dockerfiles.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/engine.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/evaluation.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/events.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/backport-release-stack/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/backport-release-stack/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/bisect-the-regression/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/bisect-the-regression/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/rebase-conflict-chain/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/rebase-conflict-chain/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/recover-complex-stash/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/recover-complex-stash/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/recover-lost-work/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/recover-lost-work/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/repair-force-pushed-remote/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/repair-force-pushed-remote/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/revert-merge-with-followups/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/revert-merge-with-followups/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/secret-in-history/README.md +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/secret-in-history/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/secret-in-history/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/split-the-mega-commit/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/split-the-mega-commit/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/untangle-nested-submodules/setup.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/git_surgery/untangle-nested-submodules/verify.sh +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/history.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/jobs.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/leakage.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/looping.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/mc_render.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/mc_viewer/dist/atlas.png +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/mc_viewer/dist/blockStates.json.gz +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/mc_viewer/dist/version.json +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/mc_viewer/dist/viewer.html +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/mc_viewer/dist/viewer.js +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/mc_viewer/dist/viewer.js.LICENSE.txt +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/mc_viewer/dist/worker.js +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/mc_viewer/dist/worker.js.LICENSE.txt +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/metrics.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/ollama.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/perf.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/perf_report.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/perturbations.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/pi_agent.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/pi_package/package-lock.json +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/pi_package/package.json +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/report.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/report_markdown.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/results.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/retry.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/runner.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/sandbox.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/template_check.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/templates/arena.html +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/templates/history.html +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/templates/perf.html +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/templates/report.html +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/__init__.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/app.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/app.tcss +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/formatting.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/screens/__init__.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/screens/connect.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/screens/detail.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/screens/help.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/screens/modals.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/screens/results.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/screens/run.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/screens/setup.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/theme.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit/tui/widgets.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit_cli.egg-info/dependency_links.txt +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit_cli.egg-info/entry_points.txt +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit_cli.egg-info/requires.txt +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/src/benchkit_cli.egg-info/top_level.txt +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_aider_polyglot.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_banking77.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_code_verifier.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_concurrency.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_config.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_datasets.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_decision.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_evalplus.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_git_surgery.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_gsm8k.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_history.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_humaneval.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_looping.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_mc_arena.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_mcq.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_medfailbench.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_medfailbench_real_outputs.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_medfailbench_real_outputs_escalation.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_medfailbench_scoring_distortion.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_patcheval.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_perturbations.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_pi_harness.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_repair.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_report.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_retry.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_runner.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_sanity.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_treejs_arena.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_unload.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_utils.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_vision.py +0 -0
- {benchkit_cli-0.1.2 → benchkit_cli-0.2.0}/tests/test_xstest.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: benchkit-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Benchmark your local LLMs with real eval suites
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -139,6 +139,8 @@ uv run benchkit --help
|
|
|
139
139
|
| HumanEval+ TR | `humaneval-plus-tr` | 164 | HumanEval+ fully translated to Turkish, function and variable names included |
|
|
140
140
|
| MBPP | `mbpp` | 500 | Short Python programming tasks |
|
|
141
141
|
| MBPP+ | `mbpp-plus` | 378 | MBPP with expanded EvalPlus tests |
|
|
142
|
+
| CRUXEval-O | `cruxeval-o` | 800 | Predict what a short Python function returns (`-cot` for the step-by-step prompt) |
|
|
143
|
+
| CRUXEval-I | `cruxeval-i` | 800 | Find an input that produces a given output, checked by running it (`-cot` too) |
|
|
142
144
|
| GSM8K | `gsm8k` | 1,319 | Multi-step grade-school math |
|
|
143
145
|
| IFEval | `ifeval` | 541 | Verifiable instruction following |
|
|
144
146
|
| RULER | `ruler` | 39 × 6 | Practical long-context retrieval from 4K to 128K |
|
|
@@ -121,6 +121,8 @@ uv run benchkit --help
|
|
|
121
121
|
| HumanEval+ TR | `humaneval-plus-tr` | 164 | HumanEval+ fully translated to Turkish, function and variable names included |
|
|
122
122
|
| MBPP | `mbpp` | 500 | Short Python programming tasks |
|
|
123
123
|
| MBPP+ | `mbpp-plus` | 378 | MBPP with expanded EvalPlus tests |
|
|
124
|
+
| CRUXEval-O | `cruxeval-o` | 800 | Predict what a short Python function returns (`-cot` for the step-by-step prompt) |
|
|
125
|
+
| CRUXEval-I | `cruxeval-i` | 800 | Find an input that produces a given output, checked by running it (`-cot` too) |
|
|
124
126
|
| GSM8K | `gsm8k` | 1,319 | Multi-step grade-school math |
|
|
125
127
|
| IFEval | `ifeval` | 541 | Verifiable instruction following |
|
|
126
128
|
| RULER | `ruler` | 39 × 6 | Practical long-context retrieval from 4K to 128K |
|
|
@@ -6,6 +6,12 @@ from benchkit.benchmarks.banking77 import Banking77
|
|
|
6
6
|
from benchkit.benchmarks.boolq import BoolQ
|
|
7
7
|
from benchkit.benchmarks.chartqa import ChartQA
|
|
8
8
|
from benchkit.benchmarks.code_verifier import CodeVerifier
|
|
9
|
+
from benchkit.benchmarks.cruxeval import (
|
|
10
|
+
CRUXEvalInput,
|
|
11
|
+
CRUXEvalInputCoT,
|
|
12
|
+
CRUXEvalOutput,
|
|
13
|
+
CRUXEvalOutputCoT,
|
|
14
|
+
)
|
|
9
15
|
from benchkit.benchmarks.evalplus import HumanEvalPlus, HumanEvalPlusTR, MBPPPlus
|
|
10
16
|
from benchkit.benchmarks.git_surgery import GitSurgery
|
|
11
17
|
from benchkit.benchmarks.gpqa import GPQA
|
|
@@ -44,6 +50,10 @@ REGISTRY: dict[str, type] = {
|
|
|
44
50
|
"humaneval-plus-tr": HumanEvalPlusTR,
|
|
45
51
|
"mbpp": MBPP,
|
|
46
52
|
"mbpp-plus": MBPPPlus,
|
|
53
|
+
"cruxeval-o": CRUXEvalOutput,
|
|
54
|
+
"cruxeval-i": CRUXEvalInput,
|
|
55
|
+
"cruxeval-o-cot": CRUXEvalOutputCoT,
|
|
56
|
+
"cruxeval-i-cot": CRUXEvalInputCoT,
|
|
47
57
|
"gsm8k": GSM8K,
|
|
48
58
|
"ifeval": IFEval,
|
|
49
59
|
"ruler": RULER,
|
|
@@ -82,6 +92,10 @@ DESCRIPTIONS: dict[str, str] = {
|
|
|
82
92
|
"humaneval-plus-tr": "HumanEval+ fully translated to Turkish, names included",
|
|
83
93
|
"mbpp": "short Python functions from natural-language specifications",
|
|
84
94
|
"mbpp-plus": "sanitized MBPP tasks with 39k+ EvalPlus test inputs",
|
|
95
|
+
"cruxeval-o": "predict what a short Python function returns for a given input",
|
|
96
|
+
"cruxeval-i": "find an input that makes a short Python function return a given output",
|
|
97
|
+
"cruxeval-o-cot": "CRUXEval output prediction with the step-by-step prompt",
|
|
98
|
+
"cruxeval-i-cot": "CRUXEval input prediction with the step-by-step prompt",
|
|
85
99
|
"gsm8k": "multi-step grade-school math problems with exact numeric answers",
|
|
86
100
|
"ifeval": "prompts with code-checkable instruction-following constraints",
|
|
87
101
|
"ruler": "practical long-context retrieval across all 13 RULER task families",
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"""CRUXEval - predict what a short Python function returns, or what it was given.
|
|
2
|
+
|
|
3
|
+
800 functions, each with one input and the output running it produces. The
|
|
4
|
+
output task (``cruxeval-o``) shows the input and asks for the output; the input
|
|
5
|
+
task (``cruxeval-i``) shows the output and asks for any input that produces it.
|
|
6
|
+
Both come in the upstream direct and chain-of-thought prompts, as separate
|
|
7
|
+
registry keys so the gap between them shows up as two jobs side by side.
|
|
8
|
+
|
|
9
|
+
Scoring needs no judge. A predicted output is parsed with ``ast.literal_eval``
|
|
10
|
+
and compared with ``==`` against the recorded output, so ``'abc'`` and
|
|
11
|
+
``"abc"``, or ``(1, 2)`` written with odd spacing, all match. The one fallback:
|
|
12
|
+
when the recorded output is a string and the prediction is not a literal at
|
|
13
|
+
all, the bare text is compared with that string, so ``== abc`` counts for
|
|
14
|
+
``'abc'``. Anything else that is not a literal - an unsimplified expression, a
|
|
15
|
+
call - is wrong, which is stricter than upstream (it evaluates expressions)
|
|
16
|
+
and keeps model-written code out of output scoring entirely.
|
|
17
|
+
|
|
18
|
+
A predicted input is never string-matched, because many inputs can produce
|
|
19
|
+
the same output: the call is run against the function in the sandbox and
|
|
20
|
+
passes when it returns the recorded output.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import ast
|
|
24
|
+
import json
|
|
25
|
+
import re
|
|
26
|
+
import secrets
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
from benchkit.benchmarks.base import Task
|
|
30
|
+
from benchkit.benchmarks.utils import strip_think_tags
|
|
31
|
+
from benchkit.evaluation import EvaluationResult
|
|
32
|
+
from benchkit.executor import execute_with_feedback
|
|
33
|
+
|
|
34
|
+
DATASET = Path(__file__).parent.parent / "datasets" / "cruxeval.jsonl"
|
|
35
|
+
|
|
36
|
+
# Prompts are the upstream ones from facebookresearch/cruxeval (MIT), verbatim.
|
|
37
|
+
DIRECT_OUTPUT = """You are given a Python function and an assertion containing an input to the function. Complete the assertion with a literal (no unsimplified expressions, no function calls) containing the output when executing the provided code on the given input, even if the function is incorrect or incomplete. Do NOT output any extra information. Provide the full assertion with the correct output in [ANSWER] and [/ANSWER] tags, following the examples.
|
|
38
|
+
|
|
39
|
+
[PYTHON]
|
|
40
|
+
def f(n):
|
|
41
|
+
return n
|
|
42
|
+
assert f(17) == ??
|
|
43
|
+
[/PYTHON]
|
|
44
|
+
[ANSWER]
|
|
45
|
+
assert f(17) == 17
|
|
46
|
+
[/ANSWER]
|
|
47
|
+
|
|
48
|
+
[PYTHON]
|
|
49
|
+
def f(s):
|
|
50
|
+
return s + "a"
|
|
51
|
+
assert f("x9j") == ??
|
|
52
|
+
[/PYTHON]
|
|
53
|
+
[ANSWER]
|
|
54
|
+
assert f("x9j") == "x9ja"
|
|
55
|
+
[/ANSWER]
|
|
56
|
+
|
|
57
|
+
[PYTHON]
|
|
58
|
+
{code}
|
|
59
|
+
assert f({input}) == ??
|
|
60
|
+
[/PYTHON]
|
|
61
|
+
[ANSWER]
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
COT_OUTPUT = """You are given a Python function and an assertion containing an input to the function. Complete the assertion with a literal (no unsimplified expressions, no function calls) containing the output when executing the provided code on the given input, even if the function is incorrect or incomplete. Do NOT output any extra information. Execute the program step by step before arriving at an answer, and provide the full assertion with the correct output in [ANSWER] and [/ANSWER] tags, following the examples.
|
|
65
|
+
|
|
66
|
+
[PYTHON]
|
|
67
|
+
def f(s):
|
|
68
|
+
s = s + s
|
|
69
|
+
return "b" + s + "a"
|
|
70
|
+
assert f("hi") == ??
|
|
71
|
+
[/PYTHON]
|
|
72
|
+
[THOUGHT]
|
|
73
|
+
Let's execute the code step by step:
|
|
74
|
+
|
|
75
|
+
1. The function f is defined, which takes a single argument s.
|
|
76
|
+
2. The function is called with the argument "hi", so within the function, s is initially "hi".
|
|
77
|
+
3. Inside the function, s is concatenated with itself, so s becomes "hihi".
|
|
78
|
+
4. The function then returns a new string that starts with "b", followed by the value of s (which is now "hihi"), and ends with "a".
|
|
79
|
+
5. The return value of the function is therefore "bhihia".
|
|
80
|
+
[/THOUGHT]
|
|
81
|
+
[ANSWER]
|
|
82
|
+
assert f("hi") == "bhihia"
|
|
83
|
+
[/ANSWER]
|
|
84
|
+
|
|
85
|
+
[PYTHON]
|
|
86
|
+
{code}
|
|
87
|
+
assert f({input}) == ??
|
|
88
|
+
[/PYTHON]
|
|
89
|
+
[THOUGHT]
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
DIRECT_INPUT = """You will be given a function f and an output in the form f(??) == output. Find any input such that executing f on the input leads to the given output. There may be multiple answers, but you should only output one. In [ANSWER] and [/ANSWER] tags, complete the assertion with one such input that will produce the output when executing the function.
|
|
93
|
+
|
|
94
|
+
[PYTHON]
|
|
95
|
+
def f(my_list):
|
|
96
|
+
count = 0
|
|
97
|
+
for i in my_list:
|
|
98
|
+
if len(i) % 2 == 0:
|
|
99
|
+
count += 1
|
|
100
|
+
return count
|
|
101
|
+
assert f(??) == 3
|
|
102
|
+
[/PYTHON]
|
|
103
|
+
[ANSWER]
|
|
104
|
+
assert f(["mq", "px", "zy"]) == 3
|
|
105
|
+
[/ANSWER]
|
|
106
|
+
|
|
107
|
+
[PYTHON]
|
|
108
|
+
def f(s1, s2):
|
|
109
|
+
return s1 + s2
|
|
110
|
+
assert f(??) == "banana"
|
|
111
|
+
[/PYTHON]
|
|
112
|
+
[ANSWER]
|
|
113
|
+
assert f("ba", "nana") == "banana"
|
|
114
|
+
[/ANSWER]
|
|
115
|
+
|
|
116
|
+
[PYTHON]
|
|
117
|
+
{code}
|
|
118
|
+
assert f(??) == {output}
|
|
119
|
+
[/PYTHON]
|
|
120
|
+
[ANSWER]
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
COT_INPUT = """You will be given a function f and an output in the form f(??) == output. Your task is to find any input such that executing f on the input leads to the given output. There may be multiple answers, but only output one. First, think step by step. You MUST surround the answer with [ANSWER] and [/ANSWER] tags. Express your answer as a passing assertion containing the input and the given output.
|
|
124
|
+
|
|
125
|
+
[PYTHON]
|
|
126
|
+
def f(x):
|
|
127
|
+
return x + 1
|
|
128
|
+
assert f(??) == 17
|
|
129
|
+
[/PYTHON]
|
|
130
|
+
[THOUGHT]
|
|
131
|
+
To find an input such that executing f on the input leads to the given output, we can work backwards from the given assertion. We know that f(??) == 17.
|
|
132
|
+
|
|
133
|
+
Since the function f(x) returns x + 1, for f(??) to be equal to 17, the value of ?? should be 16.
|
|
134
|
+
[/THOUGHT]
|
|
135
|
+
[ANSWER]
|
|
136
|
+
assert f(16) == 17
|
|
137
|
+
[/ANSWER]
|
|
138
|
+
|
|
139
|
+
[PYTHON]
|
|
140
|
+
{code}
|
|
141
|
+
assert f(??) == {output}
|
|
142
|
+
[/PYTHON]
|
|
143
|
+
[THOUGHT]
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
TEMPLATES = {
|
|
147
|
+
("output", "direct"): DIRECT_OUTPUT,
|
|
148
|
+
("output", "cot"): COT_OUTPUT,
|
|
149
|
+
("input", "direct"): DIRECT_INPUT,
|
|
150
|
+
("input", "cot"): COT_INPUT,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
NO_ASSERTION = (
|
|
154
|
+
"No assertion of the form assert f(...) == ... was found in the answer. "
|
|
155
|
+
"Reply with one such assertion inside [ANSWER] and [/ANSWER] tags."
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _answer_block(response: str) -> str:
|
|
160
|
+
"""The text the model meant as its answer.
|
|
161
|
+
|
|
162
|
+
The prompt itself opens ``[ANSWER]`` (direct) or ``[THOUGHT]`` (CoT), so a
|
|
163
|
+
response may carry both tags, only the closing one, or neither. The last
|
|
164
|
+
complete block wins; a Markdown fence inside it is unwrapped.
|
|
165
|
+
"""
|
|
166
|
+
text = strip_think_tags(response)
|
|
167
|
+
if "[ANSWER]" in text:
|
|
168
|
+
text = text.rsplit("[ANSWER]", 1)[1].split("[/ANSWER]", 1)[0]
|
|
169
|
+
elif "[/ANSWER]" in text:
|
|
170
|
+
text = text.rsplit("[/ANSWER]", 1)[0]
|
|
171
|
+
text = text.rsplit("[/THOUGHT]", 1)[-1]
|
|
172
|
+
fences = re.findall(r"```[\w-]*\n?(.*?)```", text, re.DOTALL)
|
|
173
|
+
if fences:
|
|
174
|
+
text = fences[-1]
|
|
175
|
+
return text.strip()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _assertion(block: str) -> str | None:
|
|
179
|
+
"""The last ``assert`` statement in the block, if any."""
|
|
180
|
+
lines = block.splitlines()
|
|
181
|
+
for index in range(len(lines) - 1, -1, -1):
|
|
182
|
+
if lines[index].lstrip().startswith("assert"):
|
|
183
|
+
tail = "\n".join(line.strip() for line in lines[index:])
|
|
184
|
+
return tail if _parses(tail) else lines[index].strip()
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _parses(source: str) -> bool:
|
|
189
|
+
try:
|
|
190
|
+
ast.parse(source)
|
|
191
|
+
except SyntaxError:
|
|
192
|
+
return False
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _is_f_call(node: ast.expr) -> bool:
|
|
197
|
+
return (
|
|
198
|
+
isinstance(node, ast.Call)
|
|
199
|
+
and isinstance(node.func, ast.Name)
|
|
200
|
+
and node.func.id == "f"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _equality(source: str) -> tuple[str, ast.expr, ast.expr] | None:
|
|
205
|
+
"""Split ``[assert] left == right`` into its parsed sides."""
|
|
206
|
+
text = source.strip()
|
|
207
|
+
if text.startswith("assert"):
|
|
208
|
+
text = text[len("assert") :].strip()
|
|
209
|
+
try:
|
|
210
|
+
node = ast.parse(text, mode="eval").body
|
|
211
|
+
except SyntaxError:
|
|
212
|
+
return None
|
|
213
|
+
if (
|
|
214
|
+
isinstance(node, ast.Compare)
|
|
215
|
+
and len(node.ops) == 1
|
|
216
|
+
and isinstance(node.ops[0], ast.Eq)
|
|
217
|
+
):
|
|
218
|
+
return text, node.left, node.comparators[0]
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def predicted_output(response: str) -> str | None:
|
|
223
|
+
"""The output the model predicted, as source text."""
|
|
224
|
+
block = _answer_block(response)
|
|
225
|
+
candidate = _assertion(block) or block
|
|
226
|
+
split = _equality(candidate)
|
|
227
|
+
if split is not None:
|
|
228
|
+
text, left, right = split
|
|
229
|
+
side = left if _is_f_call(right) and not _is_f_call(left) else right
|
|
230
|
+
return ast.get_source_segment(text, side)
|
|
231
|
+
# Not parseable: a bare word after ``==`` is still a usable prediction.
|
|
232
|
+
if "==" in candidate:
|
|
233
|
+
candidate = candidate.rsplit("==", 1)[1]
|
|
234
|
+
candidate = candidate.split("#", 1)[0].strip()
|
|
235
|
+
return candidate or None
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def predicted_call(response: str) -> str | None:
|
|
239
|
+
"""The ``f(...)`` call the model proposed, as source text."""
|
|
240
|
+
block = _answer_block(response)
|
|
241
|
+
candidate = _assertion(block) or block
|
|
242
|
+
split = _equality(candidate)
|
|
243
|
+
if split is not None:
|
|
244
|
+
text, left, right = split
|
|
245
|
+
for side in (left, right):
|
|
246
|
+
if _is_f_call(side):
|
|
247
|
+
return ast.get_source_segment(text, side)
|
|
248
|
+
return None
|
|
249
|
+
try:
|
|
250
|
+
node = ast.parse(candidate, mode="eval").body
|
|
251
|
+
except SyntaxError:
|
|
252
|
+
return None
|
|
253
|
+
if _is_f_call(node):
|
|
254
|
+
return ast.get_source_segment(candidate, node)
|
|
255
|
+
# A bare input, such as ``"ba", "nana"``, is the argument list itself.
|
|
256
|
+
return f"f({candidate})"
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def outputs_match(predicted: str | None, expected: str) -> bool:
|
|
260
|
+
"""Literal comparison of a predicted output with the recorded one."""
|
|
261
|
+
if predicted is None:
|
|
262
|
+
return False
|
|
263
|
+
target = ast.literal_eval(expected)
|
|
264
|
+
try:
|
|
265
|
+
value = ast.literal_eval(predicted)
|
|
266
|
+
except (ValueError, TypeError, SyntaxError, MemoryError, RecursionError):
|
|
267
|
+
return isinstance(target, str) and predicted.strip() == target
|
|
268
|
+
try:
|
|
269
|
+
return bool(value == target)
|
|
270
|
+
except Exception:
|
|
271
|
+
return False
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class _CRUXEval:
|
|
275
|
+
name: str
|
|
276
|
+
task: str # "output" or "input"
|
|
277
|
+
style: str # "direct" or "cot"
|
|
278
|
+
task_count = 800
|
|
279
|
+
|
|
280
|
+
def load_tasks(self) -> list[Task]:
|
|
281
|
+
tasks = []
|
|
282
|
+
with open(DATASET, encoding="utf-8") as f:
|
|
283
|
+
for line in f:
|
|
284
|
+
d = json.loads(line)
|
|
285
|
+
tasks.append(
|
|
286
|
+
Task(
|
|
287
|
+
id=d["task_id"],
|
|
288
|
+
prompt=d["function"],
|
|
289
|
+
metadata={
|
|
290
|
+
"input": d["input"],
|
|
291
|
+
"output": d["output"],
|
|
292
|
+
# The demo client answers with this.
|
|
293
|
+
"canonical_solution": (
|
|
294
|
+
f"[ANSWER]\nassert f({d['input']}) == "
|
|
295
|
+
f"{d['output']}\n[/ANSWER]"
|
|
296
|
+
),
|
|
297
|
+
},
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
return tasks
|
|
301
|
+
|
|
302
|
+
def build_prompt(self, task: Task) -> str:
|
|
303
|
+
template = TEMPLATES[(self.task, self.style)]
|
|
304
|
+
return template.format(
|
|
305
|
+
code=task.prompt,
|
|
306
|
+
input=task.metadata["input"],
|
|
307
|
+
output=task.metadata["output"],
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def evaluate(self, task: Task, response: str) -> bool:
|
|
311
|
+
return self.evaluate_with_feedback(task, response).passed
|
|
312
|
+
|
|
313
|
+
def summary_fields(self, records: list[object]) -> dict:
|
|
314
|
+
"""Record which half and which prompt style a score came from."""
|
|
315
|
+
return {"cruxeval_task": self.task, "cruxeval_prompt": self.style}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class _OutputPrediction(_CRUXEval):
|
|
319
|
+
task = "output"
|
|
320
|
+
|
|
321
|
+
def evaluate_with_feedback(self, task: Task, response: str) -> EvaluationResult:
|
|
322
|
+
predicted = predicted_output(response)
|
|
323
|
+
if predicted is None:
|
|
324
|
+
return EvaluationResult(0.0, NO_ASSERTION)
|
|
325
|
+
if outputs_match(predicted, task.metadata["output"]):
|
|
326
|
+
return EvaluationResult(1.0)
|
|
327
|
+
return EvaluationResult(
|
|
328
|
+
0.0,
|
|
329
|
+
"The predicted output does not equal the value the function returns. "
|
|
330
|
+
"Trace the code again; the answer must be a Python literal.",
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class _InputPrediction(_CRUXEval):
|
|
335
|
+
task = "input"
|
|
336
|
+
evaluation_activity = "running the predicted input"
|
|
337
|
+
|
|
338
|
+
def evaluate_with_feedback(self, task: Task, response: str) -> EvaluationResult:
|
|
339
|
+
call = predicted_call(response)
|
|
340
|
+
if call is None:
|
|
341
|
+
return EvaluationResult(0.0, NO_ASSERTION)
|
|
342
|
+
# Same check as upstream, ``assert output == f(input)``. The input is
|
|
343
|
+
# model-written code, so a pass also needs a fresh sentinel printed
|
|
344
|
+
# after the assertion: an input that exits early cannot fake one.
|
|
345
|
+
sentinel = secrets.token_hex(16)
|
|
346
|
+
program = (
|
|
347
|
+
f"{task.prompt}\n\n"
|
|
348
|
+
f"assert {task.metadata['output']} == {call}\n"
|
|
349
|
+
f"print({sentinel!r})\n"
|
|
350
|
+
)
|
|
351
|
+
result = execute_with_feedback(program, timeout=5, sentinel=sentinel)
|
|
352
|
+
if result.passed:
|
|
353
|
+
return EvaluationResult(1.0)
|
|
354
|
+
if "assertion failed" in result.feedback:
|
|
355
|
+
return EvaluationResult(
|
|
356
|
+
0.0,
|
|
357
|
+
"The proposed input ran, but the function returned a different "
|
|
358
|
+
"value than the given output.",
|
|
359
|
+
)
|
|
360
|
+
return EvaluationResult(0.0, result.feedback)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class CRUXEvalOutput(_OutputPrediction):
|
|
364
|
+
name = "cruxeval-o"
|
|
365
|
+
style = "direct"
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class CRUXEvalOutputCoT(_OutputPrediction):
|
|
369
|
+
name = "cruxeval-o-cot"
|
|
370
|
+
style = "cot"
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class CRUXEvalInput(_InputPrediction):
|
|
374
|
+
name = "cruxeval-i"
|
|
375
|
+
style = "direct"
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
class CRUXEvalInputCoT(_InputPrediction):
|
|
379
|
+
name = "cruxeval-i-cot"
|
|
380
|
+
style = "cot"
|
|
@@ -9,6 +9,7 @@ import string
|
|
|
9
9
|
import urllib.request
|
|
10
10
|
import uuid
|
|
11
11
|
from collections.abc import Callable
|
|
12
|
+
from functools import lru_cache
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
|
|
14
15
|
from benchkit.benchmarks.base import Task
|
|
@@ -45,6 +46,14 @@ TASKS_PER_BUCKET = DEFAULT_SAMPLES_PER_KIND * len(TASK_KINDS)
|
|
|
45
46
|
OUTPUT_RESERVE_TOKENS = 256
|
|
46
47
|
FALLBACK_CHARS_PER_TOKEN = 4
|
|
47
48
|
FIT_TOLERANCE = 0.01
|
|
49
|
+
# cwe and fwe share a 2,000-word vocabulary. cwe draws one new uncommon word
|
|
50
|
+
# per unit (~40 chars), so its vocabulary grows with the bucket; 2,000 words
|
|
51
|
+
# stop at about 18k tokens.
|
|
52
|
+
VOCABULARY_SIZE = 2000
|
|
53
|
+
# Every renderer has a floor below which extra units change nothing (cwe and
|
|
54
|
+
# qa at one, fwe at ten, qa_2's ten gold documents), so the fallback sizes a
|
|
55
|
+
# unit between two counts above all of them.
|
|
56
|
+
_PROBE_UNITS = (16, 80)
|
|
48
57
|
|
|
49
58
|
_VARIABLE_NOISE = (
|
|
50
59
|
"The grass is green. The sky is blue. The sun is yellow. "
|
|
@@ -195,8 +204,9 @@ def _aggregation_task(bucket: int, sample: int, kind: str) -> Task:
|
|
|
195
204
|
}
|
|
196
205
|
if kind in {"cwe", "fwe"}:
|
|
197
206
|
rng = random.Random(seed)
|
|
207
|
+
size = max(VOCABULARY_SIZE, bucket // 8) if kind == "cwe" else VOCABULARY_SIZE
|
|
198
208
|
vocabulary = [
|
|
199
|
-
"".join(rng.choices(string.ascii_lowercase, k=6)) for _ in range(
|
|
209
|
+
"".join(rng.choices(string.ascii_lowercase, k=6)) for _ in range(size)
|
|
200
210
|
]
|
|
201
211
|
metadata["vocabulary"] = list(dict.fromkeys(vocabulary))
|
|
202
212
|
metadata["answers"] = metadata["vocabulary"][: 10 if kind == "cwe" else 3]
|
|
@@ -266,6 +276,7 @@ def _essay_words() -> list[str]:
|
|
|
266
276
|
return text.split()
|
|
267
277
|
|
|
268
278
|
|
|
279
|
+
@lru_cache(maxsize=len(QA_FILES))
|
|
269
280
|
def _qa_records(kind: str) -> tuple[list[dict], list[str]]:
|
|
270
281
|
raw = json.loads(_qa_path(kind).read_text(encoding="utf-8"))
|
|
271
282
|
if kind == "qa_1":
|
|
@@ -410,7 +421,19 @@ def _render_niah(task: Task, filler_units: int) -> str:
|
|
|
410
421
|
target_values=list(metadata["answers"]),
|
|
411
422
|
distractor_values=[p[1] for p in pairs],
|
|
412
423
|
)
|
|
413
|
-
|
|
424
|
+
lines = _insert_at_positions(filler, needles, seed ^ 0xA51CE)
|
|
425
|
+
if metadata["haystack"] == "essay":
|
|
426
|
+
# Running prose, not one word per line: a newline per word costs far
|
|
427
|
+
# more tokens than the character estimate assumes.
|
|
428
|
+
needle_set = set(needles)
|
|
429
|
+
chunks: list[list[str]] = [[]]
|
|
430
|
+
for line in lines:
|
|
431
|
+
if line in needle_set:
|
|
432
|
+
chunks.extend([[line], []])
|
|
433
|
+
else:
|
|
434
|
+
chunks[-1].append(line)
|
|
435
|
+
lines = [" ".join(chunk) for chunk in chunks if chunk]
|
|
436
|
+
context = "\n".join(lines)
|
|
414
437
|
return (
|
|
415
438
|
"Some special magic numbers are hidden within the following text. "
|
|
416
439
|
"Memorize the records because you will be asked for one key afterward.\n\n"
|
|
@@ -463,11 +486,17 @@ def render_prompt(task: Task, filler_units: int) -> str:
|
|
|
463
486
|
|
|
464
487
|
|
|
465
488
|
def _fallback_units(task: Task, target_tokens: int) -> int:
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
chars_per_unit = max(
|
|
489
|
+
low, high = _PROBE_UNITS
|
|
490
|
+
low_chars = len(render_prompt(task, low))
|
|
491
|
+
chars_per_unit = max(
|
|
492
|
+
1.0, (len(render_prompt(task, high)) - low_chars) / (high - low)
|
|
493
|
+
)
|
|
469
494
|
target_chars = target_tokens * FALLBACK_CHARS_PER_TOKEN
|
|
470
|
-
return max(0, (target_chars -
|
|
495
|
+
return max(0, low + math.floor((target_chars - low_chars) / chars_per_unit))
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def _estimate_tokens(prompt: str) -> int:
|
|
499
|
+
return math.ceil(len(prompt) / FALLBACK_CHARS_PER_TOKEN)
|
|
471
500
|
|
|
472
501
|
|
|
473
502
|
def fit_prompt(
|
|
@@ -479,9 +508,12 @@ def fit_prompt(
|
|
|
479
508
|
"""Fit a generated prompt just below its context bucket.
|
|
480
509
|
|
|
481
510
|
When the server exposes a tokenizer, a short secant-style calibration keeps
|
|
482
|
-
the prompt within one percent of the target. Generic servers
|
|
483
|
-
conservative character-to-token estimate
|
|
511
|
+
the prompt within one percent of the target. Generic servers run the same
|
|
512
|
+
calibration against a conservative character-to-token estimate, and the
|
|
513
|
+
measured count comes back as ``None``.
|
|
484
514
|
"""
|
|
515
|
+
estimated = token_count is None
|
|
516
|
+
count = _estimate_tokens if token_count is None else token_count
|
|
485
517
|
bucket = int(task.metadata["context_tokens"])
|
|
486
518
|
target = max(1, bucket - OUTPUT_RESERVE_TOKENS)
|
|
487
519
|
units = (
|
|
@@ -490,10 +522,7 @@ def fit_prompt(
|
|
|
490
522
|
else _fallback_units(task, target)
|
|
491
523
|
)
|
|
492
524
|
prompt = render_prompt(task, units)
|
|
493
|
-
|
|
494
|
-
return prompt, units, None
|
|
495
|
-
|
|
496
|
-
measured = token_count(prompt)
|
|
525
|
+
measured = count(prompt)
|
|
497
526
|
if not isinstance(measured, int) or measured <= 0:
|
|
498
527
|
return prompt, units, None
|
|
499
528
|
|
|
@@ -502,7 +531,7 @@ def fit_prompt(
|
|
|
502
531
|
if measured <= target and measured >= target * (1 - FIT_TOLERANCE):
|
|
503
532
|
break
|
|
504
533
|
if base_tokens is None:
|
|
505
|
-
base_tokens =
|
|
534
|
+
base_tokens = count(render_prompt(task, 0))
|
|
506
535
|
if not isinstance(base_tokens, int) or base_tokens < 0:
|
|
507
536
|
base_tokens = 0
|
|
508
537
|
|
|
@@ -513,23 +542,24 @@ def fit_prompt(
|
|
|
513
542
|
next_units = max(0, units - 1) if measured > target else units + 1
|
|
514
543
|
units = next_units
|
|
515
544
|
prompt = render_prompt(task, units)
|
|
516
|
-
next_measured =
|
|
545
|
+
next_measured = count(prompt)
|
|
517
546
|
if not isinstance(next_measured, int) or next_measured <= 0:
|
|
518
547
|
return prompt, units, None
|
|
519
548
|
measured = next_measured
|
|
520
549
|
|
|
521
550
|
# Never knowingly send a prompt above the bucket. The linear adjustment is
|
|
522
551
|
# normally exact; this final correction handles unusual tokenizer merges.
|
|
523
|
-
|
|
552
|
+
# Without a tokenizer it is cheap, so keep going until the estimate fits.
|
|
553
|
+
for _ in range(16 if estimated else 2):
|
|
524
554
|
if measured <= target or units == 0:
|
|
525
555
|
break
|
|
526
556
|
excess = measured - target
|
|
527
557
|
tokens_per_unit = max(1.0, measured / max(units, 1))
|
|
528
558
|
units = max(0, units - math.ceil(excess / tokens_per_unit) - 1)
|
|
529
559
|
prompt = render_prompt(task, units)
|
|
530
|
-
next_measured =
|
|
560
|
+
next_measured = count(prompt)
|
|
531
561
|
if not isinstance(next_measured, int) or next_measured <= 0:
|
|
532
562
|
return prompt, units, None
|
|
533
563
|
measured = next_measured
|
|
534
564
|
|
|
535
|
-
return prompt, units, measured
|
|
565
|
+
return prompt, units, None if estimated else measured
|
|
@@ -79,6 +79,20 @@ them evenly. Regenerate both with `uv run python scripts/build_banking77.py`.
|
|
|
79
79
|
Encoders" (2020), <https://arxiv.org/abs/2003.04807>
|
|
80
80
|
- Dataset license: [Creative Commons Attribution 4.0 International][cc-by-4.0]
|
|
81
81
|
|
|
82
|
+
## CRUXEval
|
|
83
|
+
|
|
84
|
+
`cruxeval.jsonl` is a transformed copy of the 800 functions in CRUXEval, each
|
|
85
|
+
with one input and the output running the function produces. Only the schema
|
|
86
|
+
changed - `id` became a namespaced `task_id` and `code` became `function`.
|
|
87
|
+
Regenerate it with `uv run python scripts/build_cruxeval.py`, which pins the
|
|
88
|
+
upstream commit. The prompts in `benchmarks/cruxeval.py` are the upstream
|
|
89
|
+
direct and chain-of-thought prompts.
|
|
90
|
+
|
|
91
|
+
- Source: <https://github.com/facebookresearch/cruxeval>
|
|
92
|
+
- Paper: Gu et al., "CRUXEval: A Benchmark for Code Reasoning, Understanding
|
|
93
|
+
and Execution" (2024), <https://arxiv.org/abs/2401.03065>
|
|
94
|
+
- Dataset license: MIT
|
|
95
|
+
|
|
82
96
|
## OpenBookQA
|
|
83
97
|
|
|
84
98
|
`openbookqa.jsonl` is a transformed copy of the 500-question OpenBookQA test
|