benchkit-cli 0.1.0__tar.gz → 0.1.1__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.0/src/benchkit_cli.egg-info → benchkit_cli-0.1.1}/PKG-INFO +7 -3
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/README.md +6 -2
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/pyproject.toml +1 -1
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/evalplus.py +23 -1
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/cli.py +52 -9
- benchkit_cli-0.1.1/src/benchkit/config.py +251 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/executor.py +48 -21
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/screens/connect.py +28 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1/src/benchkit_cli.egg-info}/PKG-INFO +7 -3
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit_cli.egg-info/SOURCES.txt +2 -0
- benchkit_cli-0.1.1/tests/test_config.py +198 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/LICENSE +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/setup.cfg +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/__init__.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/_pi_proxy.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/answer_key_guard.ts +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/artifacts.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/__init__.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/aider_polyglot.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/arc.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/banking77.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/base.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/boolq.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/chartqa.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/code_verifier.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/downloads.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/git_surgery.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/gpqa.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/gsm8k.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/hellaswag.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/humaneval.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/ifeval.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/ifeval_instructions.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/mbpp.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/mc_arena.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/mcq.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/medfailbench.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/mmlu.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/mmlu_pro.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/mmvp.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/ocrbench.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/openbookqa.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/patcheval.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/piqa.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/ruler.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/ruler_generator.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/sanity.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/treejs_arena.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/truthfulqa.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/utils.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/vision.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/vstar.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/winogrande.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/benchmarks/xstest.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/browser.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/catalog.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/client.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/controls.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/MMLU_LICENSE.txt +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/README.md +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/arc_challenge.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/banking77.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/banking77_intents.json +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/boolq.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/code_verifier.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/gpqa_diamond.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/gsm8k.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/hellaswag.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/humaneval.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/ifeval.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/mbpp.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/mc_blocks_1_20_1.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/medfailbench_hard30.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/medfailbench_hard30.md +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/openbookqa.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/piqa.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/sanity_code.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/truthfulqa.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/winogrande.jsonl +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/datasets/xstest.csv +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/decision.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/demo.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/dockerfiles.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/engine.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/evaluation.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/events.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/backport-release-stack/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/backport-release-stack/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/bisect-the-regression/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/bisect-the-regression/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/rebase-conflict-chain/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/rebase-conflict-chain/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/recover-complex-stash/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/recover-complex-stash/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/recover-lost-work/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/recover-lost-work/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/repair-force-pushed-remote/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/repair-force-pushed-remote/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/revert-merge-with-followups/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/revert-merge-with-followups/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/secret-in-history/README.md +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/secret-in-history/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/secret-in-history/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/split-the-mega-commit/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/split-the-mega-commit/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/untangle-nested-submodules/setup.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/git_surgery/untangle-nested-submodules/verify.sh +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/history.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/jobs.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/leakage.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/looping.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/mc_render.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/mc_viewer/dist/atlas.png +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/mc_viewer/dist/blockStates.json.gz +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/mc_viewer/dist/version.json +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/mc_viewer/dist/viewer.html +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/mc_viewer/dist/viewer.js +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/mc_viewer/dist/viewer.js.LICENSE.txt +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/mc_viewer/dist/worker.js +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/mc_viewer/dist/worker.js.LICENSE.txt +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/metrics.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/ollama.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/perf.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/perf_report.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/perturbations.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/pi_agent.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/pi_package/package-lock.json +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/pi_package/package.json +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/report.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/report_markdown.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/results.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/retry.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/runner.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/sandbox.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/template_check.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/templates/arena.html +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/templates/history.html +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/templates/perf.html +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/templates/report.html +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/__init__.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/app.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/app.tcss +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/formatting.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/screens/__init__.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/screens/detail.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/screens/help.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/screens/modals.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/screens/results.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/screens/run.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/screens/setup.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/theme.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit/tui/widgets.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit_cli.egg-info/dependency_links.txt +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit_cli.egg-info/entry_points.txt +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit_cli.egg-info/requires.txt +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/src/benchkit_cli.egg-info/top_level.txt +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_aider_polyglot.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_banking77.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_code_verifier.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_concurrency.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_datasets.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_decision.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_evalplus.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_git_surgery.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_gsm8k.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_history.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_humaneval.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_looping.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_mc_arena.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_mcq.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_medfailbench.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_medfailbench_real_outputs.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_medfailbench_real_outputs_escalation.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_medfailbench_scoring_distortion.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_patcheval.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_perturbations.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_pi_harness.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_repair.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_report.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_retry.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_ruler.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_runner.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_sanity.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_treejs_arena.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_unload.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_utils.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_vision.py +0 -0
- {benchkit_cli-0.1.0 → benchkit_cli-0.1.1}/tests/test_xstest.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: benchkit-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Benchmark your local LLMs with real eval suites
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -69,8 +69,10 @@ cp .env.example .env
|
|
|
69
69
|
uv run benchkit
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
Set your server in
|
|
73
|
-
|
|
72
|
+
Set your server in `~/.config/benchkit/.env` to use it from any folder, in a
|
|
73
|
+
`.env` in the directory you run `benchkit` from (parent directories are not
|
|
74
|
+
searched), or on the Connect screen, where "Save as default" writes the user
|
|
75
|
+
config for you:
|
|
74
76
|
|
|
75
77
|
```env
|
|
76
78
|
BENCHKIT_PROVIDER=openai
|
|
@@ -85,6 +87,8 @@ BENCHKIT_PROVIDER=ollama
|
|
|
85
87
|
OLLAMA_HOST=http://localhost:11434
|
|
86
88
|
```
|
|
87
89
|
|
|
90
|
+
`benchkit config` shows every setting and where it came from.
|
|
91
|
+
|
|
88
92
|
No inference server available? Explore the complete UI offline:
|
|
89
93
|
|
|
90
94
|
```bash
|
|
@@ -51,8 +51,10 @@ cp .env.example .env
|
|
|
51
51
|
uv run benchkit
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
Set your server in
|
|
55
|
-
|
|
54
|
+
Set your server in `~/.config/benchkit/.env` to use it from any folder, in a
|
|
55
|
+
`.env` in the directory you run `benchkit` from (parent directories are not
|
|
56
|
+
searched), or on the Connect screen, where "Save as default" writes the user
|
|
57
|
+
config for you:
|
|
56
58
|
|
|
57
59
|
```env
|
|
58
60
|
BENCHKIT_PROVIDER=openai
|
|
@@ -67,6 +69,8 @@ BENCHKIT_PROVIDER=ollama
|
|
|
67
69
|
OLLAMA_HOST=http://localhost:11434
|
|
68
70
|
```
|
|
69
71
|
|
|
72
|
+
`benchkit config` shows every setting and where it came from.
|
|
73
|
+
|
|
70
74
|
No inference server available? Explore the complete UI offline:
|
|
71
75
|
|
|
72
76
|
```bash
|
|
@@ -6,6 +6,8 @@ import contextlib
|
|
|
6
6
|
import multiprocessing
|
|
7
7
|
import os
|
|
8
8
|
import platform
|
|
9
|
+
import shutil
|
|
10
|
+
import tempfile
|
|
9
11
|
import textwrap
|
|
10
12
|
from functools import cache, lru_cache
|
|
11
13
|
from pathlib import Path
|
|
@@ -14,6 +16,7 @@ from typing import Any
|
|
|
14
16
|
from benchkit.benchmarks.base import Task
|
|
15
17
|
from benchkit.benchmarks.downloads import Dataset
|
|
16
18
|
from benchkit.benchmarks.utils import strip_think_tags
|
|
19
|
+
from benchkit.executor import sandbox_env
|
|
17
20
|
|
|
18
21
|
HUMANEVAL_SYSTEM = (
|
|
19
22
|
"Complete the following Python function. "
|
|
@@ -43,10 +46,29 @@ def _fork_multiprocessing():
|
|
|
43
46
|
|
|
44
47
|
EvalPlus's ``evalplus.eval`` does ``from multiprocessing import Array, Value``
|
|
45
48
|
so those names are bound on that module and must be patched there as well.
|
|
49
|
+
|
|
50
|
+
The forked child runs model-written code, so it swaps its inherited
|
|
51
|
+
environment for a minimal one before running: API keys stay with the
|
|
52
|
+
parent. ``EVALPLUS_*`` is kept because the child reads its memory limit.
|
|
46
53
|
"""
|
|
47
54
|
import evalplus.eval as _ev
|
|
48
55
|
|
|
49
56
|
fork_ctx = multiprocessing.get_context("fork")
|
|
57
|
+
|
|
58
|
+
class _ScrubbedProcess(fork_ctx.Process):
|
|
59
|
+
def run(self) -> None:
|
|
60
|
+
# EvalPlus's reliability guard disables shutil.rmtree in the child.
|
|
61
|
+
rmtree = shutil.rmtree
|
|
62
|
+
home = tempfile.mkdtemp(prefix="benchkit-evalplus-")
|
|
63
|
+
env = sandbox_env(home, keep_prefixes=("EVALPLUS_",))
|
|
64
|
+
os.environ.clear()
|
|
65
|
+
os.environ.update(env)
|
|
66
|
+
tempfile.tempdir = None
|
|
67
|
+
try:
|
|
68
|
+
super().run()
|
|
69
|
+
finally:
|
|
70
|
+
rmtree(home, ignore_errors=True)
|
|
71
|
+
|
|
50
72
|
mp_originals = (
|
|
51
73
|
multiprocessing.Process,
|
|
52
74
|
multiprocessing.Value,
|
|
@@ -54,7 +76,7 @@ def _fork_multiprocessing():
|
|
|
54
76
|
)
|
|
55
77
|
ev_originals = (_ev.Value, _ev.Array)
|
|
56
78
|
try:
|
|
57
|
-
multiprocessing.Process =
|
|
79
|
+
multiprocessing.Process = _ScrubbedProcess
|
|
58
80
|
multiprocessing.Value = fork_ctx.Value
|
|
59
81
|
multiprocessing.Array = fork_ctx.Array
|
|
60
82
|
_ev.Value = fork_ctx.Value
|
|
@@ -9,13 +9,13 @@ from __future__ import annotations
|
|
|
9
9
|
import argparse
|
|
10
10
|
import sys
|
|
11
11
|
|
|
12
|
-
from dotenv import load_dotenv
|
|
13
12
|
from rich import box
|
|
14
13
|
from rich.console import Console
|
|
14
|
+
from rich.markup import escape
|
|
15
15
|
from rich.table import Table
|
|
16
16
|
from rich.text import Text
|
|
17
17
|
|
|
18
|
-
from benchkit import catalog
|
|
18
|
+
from benchkit import catalog, config
|
|
19
19
|
from benchkit.benchmarks import DESCRIPTIONS, REGISTRY
|
|
20
20
|
from benchkit.client import InferenceClient
|
|
21
21
|
from benchkit.engine import (
|
|
@@ -60,11 +60,11 @@ def _parse_args(argv: list[str]) -> argparse.Namespace:
|
|
|
60
60
|
parser.add_argument(
|
|
61
61
|
"command",
|
|
62
62
|
nargs="?",
|
|
63
|
-
choices=("perf", "history", "render-check", "datasets"),
|
|
63
|
+
choices=("perf", "history", "render-check", "datasets", "config"),
|
|
64
64
|
help=(
|
|
65
65
|
"Run a performance profile, browse historical results, check "
|
|
66
|
-
"that headless rendering works on this machine,
|
|
67
|
-
"install benchmark datasets"
|
|
66
|
+
"that headless rendering works on this machine, list and "
|
|
67
|
+
"install benchmark datasets, or show the effective configuration"
|
|
68
68
|
),
|
|
69
69
|
)
|
|
70
70
|
parser.add_argument(
|
|
@@ -73,7 +73,8 @@ def _parse_args(argv: list[str]) -> argparse.Namespace:
|
|
|
73
73
|
metavar="MODEL",
|
|
74
74
|
help=(
|
|
75
75
|
"Model name for `benchkit perf MODEL`; `install` for "
|
|
76
|
-
"`benchkit datasets install [BENCHMARK ...]`"
|
|
76
|
+
"`benchkit datasets install [BENCHMARK ...]`; `path` for "
|
|
77
|
+
"`benchkit config path`"
|
|
77
78
|
),
|
|
78
79
|
)
|
|
79
80
|
parser.add_argument(
|
|
@@ -83,6 +84,15 @@ def _parse_args(argv: list[str]) -> argparse.Namespace:
|
|
|
83
84
|
help="Datasets: benchmarks to install (default: every missing one)",
|
|
84
85
|
)
|
|
85
86
|
parser.add_argument("--host", help="Inference host, overrides BENCHKIT_HOST")
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--env-file",
|
|
89
|
+
metavar="PATH",
|
|
90
|
+
help=(
|
|
91
|
+
"Read settings from PATH instead of ./.env (also BENCHKIT_ENV_FILE). "
|
|
92
|
+
"Precedence: flags > environment > --env-file or ./.env > "
|
|
93
|
+
"~/.config/benchkit/.env ($XDG_CONFIG_HOME) > defaults"
|
|
94
|
+
),
|
|
95
|
+
)
|
|
86
96
|
parser.add_argument(
|
|
87
97
|
"--demo",
|
|
88
98
|
action="store_true",
|
|
@@ -956,11 +966,40 @@ def _perf(args: argparse.Namespace) -> None:
|
|
|
956
966
|
)
|
|
957
967
|
|
|
958
968
|
|
|
969
|
+
def _config(args: argparse.Namespace) -> None:
|
|
970
|
+
if args.perf_model == "path":
|
|
971
|
+
for entry in config.loaded().files:
|
|
972
|
+
if entry.skipped:
|
|
973
|
+
state = f"[dim]{entry.skipped}[/dim]"
|
|
974
|
+
elif entry.exists:
|
|
975
|
+
state = "[green]found[/green]"
|
|
976
|
+
else:
|
|
977
|
+
state = "[dim]missing[/dim]"
|
|
978
|
+
console.print(f"{entry.role:<18} {escape(str(entry.path))} {state}")
|
|
979
|
+
return
|
|
980
|
+
if args.perf_model is not None:
|
|
981
|
+
console.print(
|
|
982
|
+
f"[red]Unknown config action {escape(repr(args.perf_model))}.[/red]"
|
|
983
|
+
)
|
|
984
|
+
sys.exit(1)
|
|
985
|
+
|
|
986
|
+
table = Table(box=box.SIMPLE_HEAD, header_style="bold")
|
|
987
|
+
table.add_column("Setting")
|
|
988
|
+
table.add_column("Value")
|
|
989
|
+
table.add_column("Origin", style="dim")
|
|
990
|
+
for key, value, origin in config.resolve({"BENCHKIT_HOST": args.host}):
|
|
991
|
+
shown = Text(value) if value else Text("-", style="dim")
|
|
992
|
+
table.add_row(key, shown, Text(origin))
|
|
993
|
+
console.print(table)
|
|
994
|
+
|
|
995
|
+
|
|
959
996
|
def main(argv: list[str] | None = None) -> None:
|
|
960
|
-
# Only the working directory's .env: walking up to parent directories would
|
|
961
|
-
# let a file in a shared parent such as /tmp redirect the host and API key.
|
|
962
|
-
load_dotenv(".env")
|
|
963
997
|
args = _parse_args(sys.argv[1:] if argv is None else argv)
|
|
998
|
+
try:
|
|
999
|
+
config.load(args.env_file)
|
|
1000
|
+
except config.ConfigError as exc:
|
|
1001
|
+
console.print(f"[red]{exc}[/red]")
|
|
1002
|
+
sys.exit(2)
|
|
964
1003
|
|
|
965
1004
|
if args.list:
|
|
966
1005
|
_list_benchmarks()
|
|
@@ -974,6 +1013,10 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
974
1013
|
_datasets(args)
|
|
975
1014
|
return
|
|
976
1015
|
|
|
1016
|
+
if args.command == "config":
|
|
1017
|
+
_config(args)
|
|
1018
|
+
return
|
|
1019
|
+
|
|
977
1020
|
if args.command == "perf":
|
|
978
1021
|
_perf(args)
|
|
979
1022
|
return
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""Layered configuration: which settings apply, and where each one came from.
|
|
2
|
+
|
|
3
|
+
Precedence, highest first: CLI flags, real environment variables,
|
|
4
|
+
``--env-file PATH`` (or ``BENCHKIT_ENV_FILE``), ``./.env`` in the working
|
|
5
|
+
directory, the user config ``$XDG_CONFIG_HOME/benchkit/.env`` (default
|
|
6
|
+
``~/.config/benchkit/.env``), then built-in defaults. An explicit env file
|
|
7
|
+
replaces ``./.env`` rather than stacking on it.
|
|
8
|
+
|
|
9
|
+
Only BenchKit's own keys are applied from any file, so running inside another
|
|
10
|
+
project never imports that project's secrets. Parent directories are never
|
|
11
|
+
searched: a ``.env`` in a shared parent such as ``/tmp`` could otherwise
|
|
12
|
+
redirect the host and API key.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
from collections.abc import MutableMapping
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from dotenv import dotenv_values, set_key, unset_key
|
|
23
|
+
|
|
24
|
+
from benchkit.client import DEFAULT_HOST
|
|
25
|
+
|
|
26
|
+
ENV_FILE_VAR = "BENCHKIT_ENV_FILE"
|
|
27
|
+
_PREFIXES = ("BENCHKIT_", "EVALPLUS_")
|
|
28
|
+
_NAMES = frozenset({"OLLAMA_HOST", "OPENAI_API_KEY", "OPENAI_BASE_URL"})
|
|
29
|
+
_SECRET_SUFFIXES = ("_API_KEY", "_TOKEN", "_SECRET", "_PASSWORD")
|
|
30
|
+
|
|
31
|
+
# Settings `benchkit config` always lists, with the default each falls back to
|
|
32
|
+
# ("" when unset simply means "not used"). Internal variables the harness sets
|
|
33
|
+
# for its own child processes are left out.
|
|
34
|
+
# fmt: off
|
|
35
|
+
SETTINGS: tuple[tuple[str, str], ...] = (
|
|
36
|
+
("BENCHKIT_PROVIDER", "auto"),
|
|
37
|
+
("BENCHKIT_HOST", DEFAULT_HOST),
|
|
38
|
+
("OPENAI_BASE_URL", ""),
|
|
39
|
+
("OLLAMA_HOST", ""),
|
|
40
|
+
("BENCHKIT_API_KEY", ""),
|
|
41
|
+
("OPENAI_API_KEY", ""),
|
|
42
|
+
("BENCHKIT_TIMEOUT", "300"),
|
|
43
|
+
("BENCHKIT_CONCURRENCY", ""),
|
|
44
|
+
("BENCHKIT_HARDWARE", ""),
|
|
45
|
+
("BENCHKIT_PERF_CONFIG", ""),
|
|
46
|
+
("BENCHKIT_RETRIES", "3"),
|
|
47
|
+
("BENCHKIT_CONNECT_RETRIES", "6"),
|
|
48
|
+
("BENCHKIT_RETRY_BASE_DELAY", "0.5"),
|
|
49
|
+
("BENCHKIT_RETRY_MAX_DELAY", "8"),
|
|
50
|
+
("BENCHKIT_RETRY_MAX_WAIT", "60"),
|
|
51
|
+
("BENCHKIT_LOOP_KILL", "true"),
|
|
52
|
+
("BENCHKIT_LOOP_KILL_PERCENT", "80"),
|
|
53
|
+
("BENCHKIT_LOOP_KILL_SECONDS", "10"),
|
|
54
|
+
("BENCHKIT_PI_TIMEOUT", "900"),
|
|
55
|
+
("BENCHKIT_DOCKER", "docker"),
|
|
56
|
+
("BENCHKIT_SANDBOX_MEMORY", "2g"),
|
|
57
|
+
("BENCHKIT_SANDBOX_CPUS", "2"),
|
|
58
|
+
("BENCHKIT_SANDBOX_PIDS", ""),
|
|
59
|
+
("BENCHKIT_MC_ARENA_MEMORY", "512m"),
|
|
60
|
+
("BENCHKIT_MC_ARENA_CPUS", "1"),
|
|
61
|
+
("BENCHKIT_MC_ARENA_PIDS", "64"),
|
|
62
|
+
("BENCHKIT_MC_ARENA_BASE_IMAGE", ""),
|
|
63
|
+
("BENCHKIT_MC_ARENA_IMAGE", ""),
|
|
64
|
+
("BENCHKIT_RENDER_SETTLE", "4"),
|
|
65
|
+
("BENCHKIT_RENDER_TIMEOUT", "30"),
|
|
66
|
+
("BENCHKIT_RENDER_ALLOWED_HOSTS", ""),
|
|
67
|
+
("BENCHKIT_RENDER_OFFLINE", "false"),
|
|
68
|
+
("BENCHKIT_RENDER_CHROMIUM", ""),
|
|
69
|
+
("BENCHKIT_RENDER_NO_SANDBOX", "false"),
|
|
70
|
+
("BENCHKIT_DATASET_CACHE", ""),
|
|
71
|
+
("BENCHKIT_VISION_CACHE", ""),
|
|
72
|
+
("BENCHKIT_PATCHEVAL_DATASET", ""),
|
|
73
|
+
("BENCHKIT_AIDER_DATASET", ""),
|
|
74
|
+
)
|
|
75
|
+
# fmt: on
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ConfigError(Exception):
|
|
79
|
+
"""A configuration source was named but cannot be used."""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(frozen=True)
|
|
83
|
+
class EnvFile:
|
|
84
|
+
"""One file BenchKit considered, in precedence order."""
|
|
85
|
+
|
|
86
|
+
path: Path
|
|
87
|
+
role: str
|
|
88
|
+
exists: bool
|
|
89
|
+
skipped: str = ""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class Loaded:
|
|
94
|
+
"""The files considered and the origin of every BenchKit key applied."""
|
|
95
|
+
|
|
96
|
+
files: list[EnvFile] = field(default_factory=list)
|
|
97
|
+
origins: dict[str, str] = field(default_factory=dict)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
_loaded = Loaded()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def is_benchkit_key(key: str) -> bool:
|
|
104
|
+
return key.startswith(_PREFIXES) or key in _NAMES
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def is_secret(key: str) -> bool:
|
|
108
|
+
return key.endswith(_SECRET_SUFFIXES)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def mask(value: str) -> str:
|
|
112
|
+
"""Hide a secret, keeping the last four characters of a long one."""
|
|
113
|
+
if not value:
|
|
114
|
+
return ""
|
|
115
|
+
if len(value) <= 8:
|
|
116
|
+
return "****"
|
|
117
|
+
return "****" + value[-4:]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def user_config_dir(environ: MutableMapping[str, str] | None = None) -> Path:
|
|
121
|
+
environ = os.environ if environ is None else environ
|
|
122
|
+
base = environ.get("XDG_CONFIG_HOME") or ""
|
|
123
|
+
root = Path(base).expanduser() if base else Path("~/.config").expanduser()
|
|
124
|
+
return root / "benchkit"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def user_env_path(environ: MutableMapping[str, str] | None = None) -> Path:
|
|
128
|
+
return user_config_dir(environ) / ".env"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def load(
|
|
132
|
+
env_file: str | None = None,
|
|
133
|
+
*,
|
|
134
|
+
environ: MutableMapping[str, str] | None = None,
|
|
135
|
+
cwd: Path | None = None,
|
|
136
|
+
) -> Loaded:
|
|
137
|
+
"""Apply BenchKit keys from the config files into ``environ``.
|
|
138
|
+
|
|
139
|
+
A key already in the environment is never overwritten, and a file only
|
|
140
|
+
fills keys that no higher file set. Raises ``ConfigError`` when an
|
|
141
|
+
explicit env file does not exist.
|
|
142
|
+
"""
|
|
143
|
+
global _loaded
|
|
144
|
+
environ = os.environ if environ is None else environ
|
|
145
|
+
cwd = Path.cwd() if cwd is None else cwd
|
|
146
|
+
|
|
147
|
+
files: list[EnvFile] = []
|
|
148
|
+
project = cwd / ".env"
|
|
149
|
+
explicit, role = env_file, "--env-file"
|
|
150
|
+
if not explicit and environ.get(ENV_FILE_VAR):
|
|
151
|
+
explicit, role = environ[ENV_FILE_VAR], ENV_FILE_VAR
|
|
152
|
+
if explicit:
|
|
153
|
+
path = Path(explicit).expanduser()
|
|
154
|
+
if not path.is_absolute():
|
|
155
|
+
path = cwd / path
|
|
156
|
+
if not path.is_file():
|
|
157
|
+
raise ConfigError(f"{role}: no such file: {path}")
|
|
158
|
+
files.append(EnvFile(path, role, True))
|
|
159
|
+
files.append(
|
|
160
|
+
EnvFile(
|
|
161
|
+
project, "project", project.is_file(), skipped=f"replaced by {role}"
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
else:
|
|
165
|
+
files.append(EnvFile(project, "project", project.is_file()))
|
|
166
|
+
user = user_env_path(environ)
|
|
167
|
+
files.append(EnvFile(user, "user", user.is_file()))
|
|
168
|
+
|
|
169
|
+
origins = {key: "env" for key in environ if is_benchkit_key(key)}
|
|
170
|
+
for entry in files:
|
|
171
|
+
if not entry.exists or entry.skipped:
|
|
172
|
+
continue
|
|
173
|
+
for key, value in dotenv_values(entry.path).items():
|
|
174
|
+
if value is None or key == ENV_FILE_VAR or not is_benchkit_key(key):
|
|
175
|
+
continue
|
|
176
|
+
if key in origins:
|
|
177
|
+
continue
|
|
178
|
+
environ[key] = value
|
|
179
|
+
origins[key] = str(entry.path)
|
|
180
|
+
|
|
181
|
+
_loaded = Loaded(files, origins)
|
|
182
|
+
return _loaded
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def loaded() -> Loaded:
|
|
186
|
+
"""What the last ``load`` applied (empty before the first call)."""
|
|
187
|
+
return _loaded
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def resolve(
|
|
191
|
+
flags: dict[str, str | None] | None = None,
|
|
192
|
+
*,
|
|
193
|
+
environ: MutableMapping[str, str] | None = None,
|
|
194
|
+
) -> list[tuple[str, str, str]]:
|
|
195
|
+
"""Every known or set BenchKit key as ``(key, value, origin)``.
|
|
196
|
+
|
|
197
|
+
``value`` is masked for secrets. ``origin`` is ``flag``, ``env``, the file
|
|
198
|
+
path the value came from, or ``default``.
|
|
199
|
+
"""
|
|
200
|
+
environ = os.environ if environ is None else environ
|
|
201
|
+
flags = {key: value for key, value in (flags or {}).items() if value}
|
|
202
|
+
defaults = dict(SETTINGS)
|
|
203
|
+
extra = sorted(
|
|
204
|
+
key
|
|
205
|
+
for key in environ
|
|
206
|
+
if is_benchkit_key(key) and key not in defaults and key != ENV_FILE_VAR
|
|
207
|
+
)
|
|
208
|
+
rows = []
|
|
209
|
+
for key in [*defaults, *extra]:
|
|
210
|
+
if key in flags:
|
|
211
|
+
value, origin = flags[key], "flag"
|
|
212
|
+
elif key in environ:
|
|
213
|
+
value, origin = environ[key], _loaded.origins.get(key, "env")
|
|
214
|
+
else:
|
|
215
|
+
value, origin = defaults.get(key, ""), "default"
|
|
216
|
+
rows.append((key, mask(value) if is_secret(key) else value, origin))
|
|
217
|
+
return rows
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def save_user(
|
|
221
|
+
values: dict[str, str | None],
|
|
222
|
+
*,
|
|
223
|
+
environ: MutableMapping[str, str] | None = None,
|
|
224
|
+
) -> Path:
|
|
225
|
+
"""Write ``values`` to the user config; ``None`` or ``""`` removes a key.
|
|
226
|
+
|
|
227
|
+
The file can hold an API key, so it is created readable by its owner only.
|
|
228
|
+
"""
|
|
229
|
+
path = user_env_path(environ)
|
|
230
|
+
path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
231
|
+
if not path.exists():
|
|
232
|
+
path.touch(mode=0o600)
|
|
233
|
+
present = dotenv_values(path)
|
|
234
|
+
for key, value in values.items():
|
|
235
|
+
if not is_benchkit_key(key):
|
|
236
|
+
raise ValueError(f"not a BenchKit setting: {key}")
|
|
237
|
+
if value:
|
|
238
|
+
set_key(path, key, value, quote_mode="auto")
|
|
239
|
+
elif key in present:
|
|
240
|
+
unset_key(path, key, quote_mode="auto")
|
|
241
|
+
return path
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def overridden(keys: list[str]) -> dict[str, str]:
|
|
245
|
+
"""Keys whose current value comes from somewhere above the user config."""
|
|
246
|
+
user = str(user_env_path())
|
|
247
|
+
return {
|
|
248
|
+
key: origin
|
|
249
|
+
for key in keys
|
|
250
|
+
if (origin := _loaded.origins.get(key)) is not None and origin != user
|
|
251
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Sandboxed code execution via subprocess."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
import subprocess
|
|
4
5
|
import sys
|
|
5
6
|
import tempfile
|
|
@@ -32,6 +33,33 @@ _b.__import__ = _safe
|
|
|
32
33
|
"""
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
def sandbox_env(home: str, *, keep_prefixes: tuple[str, ...] = ()) -> dict[str, str]:
|
|
37
|
+
"""A minimal environment for running model-written code.
|
|
38
|
+
|
|
39
|
+
The import guard is easy to bypass, so the child must not inherit API keys
|
|
40
|
+
or anything else from BenchKit's own environment. ``HOME`` and the temp dir
|
|
41
|
+
point at ``home``; ``keep_prefixes`` keeps evaluator knobs such as
|
|
42
|
+
``EVALPLUS_``.
|
|
43
|
+
"""
|
|
44
|
+
env = {
|
|
45
|
+
"PATH": os.environ.get("PATH") or os.defpath,
|
|
46
|
+
"HOME": home,
|
|
47
|
+
"TMPDIR": home,
|
|
48
|
+
"LANG": os.environ.get("LANG") or "C.UTF-8",
|
|
49
|
+
}
|
|
50
|
+
# Windows Python cannot start without these.
|
|
51
|
+
for name in ("SYSTEMROOT", "COMSPEC"):
|
|
52
|
+
if name in os.environ:
|
|
53
|
+
env[name] = os.environ[name]
|
|
54
|
+
if keep_prefixes:
|
|
55
|
+
env.update(
|
|
56
|
+
(key, value)
|
|
57
|
+
for key, value in os.environ.items()
|
|
58
|
+
if key.startswith(keep_prefixes)
|
|
59
|
+
)
|
|
60
|
+
return env
|
|
61
|
+
|
|
62
|
+
|
|
35
63
|
@dataclass(frozen=True)
|
|
36
64
|
class ExecutionResult:
|
|
37
65
|
"""Code-verifier result with answer-safe diagnostic feedback."""
|
|
@@ -68,27 +96,26 @@ def execute_with_feedback(code: str, timeout: int = 10) -> ExecutionResult:
|
|
|
68
96
|
"""Run code and retain only a sanitized failure category."""
|
|
69
97
|
full = SANDBOX_HEADER + "\n" + code
|
|
70
98
|
|
|
71
|
-
with tempfile.
|
|
72
|
-
|
|
73
|
-
path
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
path.unlink(missing_ok=True)
|
|
99
|
+
with tempfile.TemporaryDirectory(prefix="benchkit-exec-") as home:
|
|
100
|
+
path = Path(home) / "solution.py"
|
|
101
|
+
path.write_text(full)
|
|
102
|
+
try:
|
|
103
|
+
result = subprocess.run(
|
|
104
|
+
[sys.executable, str(path)],
|
|
105
|
+
capture_output=True,
|
|
106
|
+
text=True,
|
|
107
|
+
timeout=timeout,
|
|
108
|
+
cwd=home,
|
|
109
|
+
env=sandbox_env(home),
|
|
110
|
+
)
|
|
111
|
+
except subprocess.TimeoutExpired:
|
|
112
|
+
return ExecutionResult(
|
|
113
|
+
False,
|
|
114
|
+
f"The submitted Python exceeded the {timeout}s verifier timeout.",
|
|
115
|
+
)
|
|
116
|
+
if result.returncode == 0:
|
|
117
|
+
return ExecutionResult(True)
|
|
118
|
+
return ExecutionResult(False, _failure_feedback(result.stderr or ""))
|
|
92
119
|
|
|
93
120
|
|
|
94
121
|
def execute(code: str, timeout: int = 10) -> bool:
|
|
@@ -11,6 +11,7 @@ from textual.containers import Horizontal, Vertical, VerticalScroll
|
|
|
11
11
|
from textual.screen import Screen
|
|
12
12
|
from textual.widgets import Button, Footer, Header, Input, Label, Select, Static
|
|
13
13
|
|
|
14
|
+
from benchkit import config
|
|
14
15
|
from benchkit.client import DEFAULT_HOST, InferenceClient
|
|
15
16
|
from benchkit.demo import DemoClient
|
|
16
17
|
from benchkit.tui.widgets import Banner
|
|
@@ -34,6 +35,7 @@ class ConnectScreen(Screen[None]):
|
|
|
34
35
|
Binding("ctrl+r", "connect", "Connect"),
|
|
35
36
|
Binding("f5", "connect", "Connect", show=False),
|
|
36
37
|
Binding("f3", "demo", "Demo mode"),
|
|
38
|
+
Binding("ctrl+s", "save_default", "Save as default"),
|
|
37
39
|
Binding("escape", "app.quit", "Quit", show=False),
|
|
38
40
|
]
|
|
39
41
|
|
|
@@ -72,6 +74,7 @@ class ConnectScreen(Screen[None]):
|
|
|
72
74
|
yield Input(placeholder="300", id="timeout", compact=True)
|
|
73
75
|
with Horizontal(id="connect-actions"):
|
|
74
76
|
yield Button("Connect", id="connect", variant="primary")
|
|
77
|
+
yield Button("Save as default", id="save-default")
|
|
75
78
|
yield Button("Quit", id="quit", variant="error")
|
|
76
79
|
yield Static(HINT, id="connect-status")
|
|
77
80
|
yield Footer()
|
|
@@ -111,6 +114,29 @@ class ConnectScreen(Screen[None]):
|
|
|
111
114
|
self._status(f"Connecting to {host} …", "working")
|
|
112
115
|
self._connect(host.rstrip("/"), str(provider), api_key, timeout)
|
|
113
116
|
|
|
117
|
+
def action_save_default(self) -> None:
|
|
118
|
+
"""Write host, provider and key to the user config (~/.config/benchkit)."""
|
|
119
|
+
values = {
|
|
120
|
+
"BENCHKIT_HOST": self.query_one("#host", Input).value.strip().rstrip("/"),
|
|
121
|
+
"BENCHKIT_PROVIDER": str(self.query_one("#provider", Select).value),
|
|
122
|
+
"BENCHKIT_API_KEY": self.query_one("#api-key", Input).value.strip(),
|
|
123
|
+
}
|
|
124
|
+
try:
|
|
125
|
+
path = config.save_user(values)
|
|
126
|
+
except OSError as exc:
|
|
127
|
+
self._status(f"Could not save defaults: {exc}", "error")
|
|
128
|
+
return
|
|
129
|
+
message = f"Saved host, provider and key to {path}"
|
|
130
|
+
if shadowed := config.overridden(list(values)):
|
|
131
|
+
sources = ", ".join(sorted(set(shadowed.values())))
|
|
132
|
+
message += (
|
|
133
|
+
f"\n{', '.join(sorted(shadowed))} still come from {sources}, "
|
|
134
|
+
"which take precedence here."
|
|
135
|
+
)
|
|
136
|
+
self._status(message, "working")
|
|
137
|
+
else:
|
|
138
|
+
self._status(message, "ok")
|
|
139
|
+
|
|
114
140
|
def action_demo(self) -> None:
|
|
115
141
|
client = DemoClient()
|
|
116
142
|
self._status("Demo mode — offline models, no server required.", "ok")
|
|
@@ -119,6 +145,8 @@ class ConnectScreen(Screen[None]):
|
|
|
119
145
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
120
146
|
if event.button.id == "connect":
|
|
121
147
|
self.action_connect()
|
|
148
|
+
elif event.button.id == "save-default":
|
|
149
|
+
self.action_save_default()
|
|
122
150
|
elif event.button.id == "quit":
|
|
123
151
|
self.app.exit()
|
|
124
152
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: benchkit-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Benchmark your local LLMs with real eval suites
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -69,8 +69,10 @@ cp .env.example .env
|
|
|
69
69
|
uv run benchkit
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
Set your server in
|
|
73
|
-
|
|
72
|
+
Set your server in `~/.config/benchkit/.env` to use it from any folder, in a
|
|
73
|
+
`.env` in the directory you run `benchkit` from (parent directories are not
|
|
74
|
+
searched), or on the Connect screen, where "Save as default" writes the user
|
|
75
|
+
config for you:
|
|
74
76
|
|
|
75
77
|
```env
|
|
76
78
|
BENCHKIT_PROVIDER=openai
|
|
@@ -85,6 +87,8 @@ BENCHKIT_PROVIDER=ollama
|
|
|
85
87
|
OLLAMA_HOST=http://localhost:11434
|
|
86
88
|
```
|
|
87
89
|
|
|
90
|
+
`benchkit config` shows every setting and where it came from.
|
|
91
|
+
|
|
88
92
|
No inference server available? Explore the complete UI offline:
|
|
89
93
|
|
|
90
94
|
```bash
|
|
@@ -9,6 +9,7 @@ src/benchkit/browser.py
|
|
|
9
9
|
src/benchkit/catalog.py
|
|
10
10
|
src/benchkit/cli.py
|
|
11
11
|
src/benchkit/client.py
|
|
12
|
+
src/benchkit/config.py
|
|
12
13
|
src/benchkit/controls.py
|
|
13
14
|
src/benchkit/decision.py
|
|
14
15
|
src/benchkit/demo.py
|
|
@@ -154,6 +155,7 @@ tests/test_aider_polyglot.py
|
|
|
154
155
|
tests/test_banking77.py
|
|
155
156
|
tests/test_code_verifier.py
|
|
156
157
|
tests/test_concurrency.py
|
|
158
|
+
tests/test_config.py
|
|
157
159
|
tests/test_datasets.py
|
|
158
160
|
tests/test_decision.py
|
|
159
161
|
tests/test_evalplus.py
|