Add bench generation mode with Harbor and SWE‑bench backends - #17
Draft
Quantumlyy wants to merge 8 commits into
Draft
Add bench generation mode with Harbor and SWE‑bench backends#17Quantumlyy wants to merge 8 commits into
Quantumlyy wants to merge 8 commits into
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a benchmark generation mode —
teich generate --mode bench— that runs reward-labeled benchmark tasks through a pluggable backend (harbororswe-bench), harvests each native trace routed by its verifier score intooutput/{passed,failed,borderline}/, and hardens the run for real Docker use (per-task image purge, working Ctrl-C). Everything is opt-in:--mode prompts(the default),github_repo, and all existing behavior are unchanged, bench lives behind theteich[harbor]/teich[swe]extras, and the only new core runtime dependency isjinja2>=3.1(previously dev-only).What's included
1.
teich generate --mode benchteich generategrows a--mode {prompts,bench}flag (defaultprompts) and routes bench through a shared driver + harvest so backends stay thin.src/teich/bench/runner.pyrun_bench(cfg, console, resume, refresh)is imported lazily fromcli.pysoharbor/swebenchstay optional extras; tasks run through a bounded pool sized bymax_concurrency(default1).src/teich/bench/backends/base.pydefines theBenchBackendprotocol (require/tasks/run, whererunreturns a native trace plus a rewards dict) plus reward routing (primary_score,route_split: score1->passed,0/unscored ->failed, anything else ->borderline) andharvest().output/{passed,failed,borderline}/with scores + provenance written tooutput/metadata/<stem>.json; filenames are namespacedbench-<source>-<task>.bench-<task>.jsonl,bench/**is added toUPLOAD_IGNORE_PATTERNS, and a mode guard warns against mixing bench and prompt rows.src/teich/converter.pyexcludes bench / verification / sandbox intermediates from the dataset view, while verifier sidecars undermetadata/are still uploaded as dataset provenance — this is what makes the dataset "verifiable": each row carries its task's reward outcome.src/teich/config.pyaddsbench.sources(per-sourcetype/source/split/instances/repo/version/backend) andoutput.bench_dir(defaults to a siblingbenchdir besidetraces_dir, outside the dataset);config.example.yamldocuments both.--resumeskips already-harvested bench tasks;--refreshre-downloads remoteharborsources only (swe-bench uses Hugging Face's own dataset cache).2. Harbor backend
src/teich/bench/backends/harbor.pyruns Harbor-format tasks by driving theharborpackage as a library (registry spec, local task dir, or git/HF registry) and reads the verifier's reward dict off the trial env.teich[harbor](harbor>=0.15.0, Python >= 3.12).3. SWE-bench backend
SWE-bench ships an evaluation harness, not runnable tasks, so teich runs the agent itself and grades what it produces.
src/teich/bench/backends/swebench.pychecks out the clean repo@ base_commitinside swebench's prebuilt instance image, renders a thin agent layer on top (src/teich/bench/templates/agent.dockerfile.j2), takes the agent'sgit diffas the candidate patch, and grades it withswebench.run_instance; theresolvedverdict becomes the reward (resolved->passed, unresolved ->failed).teich[swe](swebench>=4.1.0);teich[bench]=teich[harbor,swe].4. Ctrl-C actually stops a run
The driver ran tasks in a
ThreadPoolExecutor, butSIGINTonly reaches the main thread andwith ThreadPoolExecutordoesshutdown(wait=True), so Ctrl-C looked stopped while Docker ground on in the workers.max_concurrency == 1now runs each task inline on the main thread, so Ctrl-C propagates straight into the Docker call and stops cleanly (the smoke-test /--resumepath).max_concurrency > 1cancels queued tasks (cancel_futures) and lets the few already inside a Docker call finish before exit (threads can't be signalled).--resume" hint.5. Per-task Docker image purge
harbor's teardown runs
compose down --rmi local, which does not remove its custom-taggedhb__<task>image, so per-task images accumulated and filled the disk.finally, gated byoutput.keep_bench_images(defaultfalse= purge).hb__<task>read off the trial env, with a deterministicsanitize("hb__" + task)fallback covering the error path where the trial never returned.6. Templated dataset card
src/teich/trace_readme.pyis reworked to render the dataset card fromsrc/teich/templates/dataset_card.md.j2off a presentation-only context dict — byte-identical to the previous hand-built card, locked by a golden test.output.licenseadds an SPDX id to the frontmatter.output.card_extramerges arbitrary extra frontmatter keys (reserved keys dropped, dumped viayaml.safe_dump).output.readme_templatepoints at a user.j2to fully override the card (rendered with the same context); afield_validatorerrors at config load if the file is missing.size_categoriesis auto-populated from the dataset row count using HF's standard buckets (n<1K...n>1T).jinja2>=3.1is promoted from a dev-only to a core dependency; both templates ship in the wheel.Validation
uv run --extra dev python -m pytest -q-> 612 passed, 27 skippeduv run --extra dev python -m pytest -q tests/test_bench.py tests/test_swebench.py tests/test_trace_readme.py tests/test_cli.py tests/test_converter.py-> 168 passed, 2 skipped (bench 31, swe-bench 21, trace_readme 30, cli 29, converter 59)test_rewards_resolved,test_rewards_unresolved_routes_to_failed,test_rewards_resolved_routes_to_passedtest_rendered_card_matches_golden_byte_for_byte, plustest_size_category_bucket_boundaries,license,card_extra, and custom-template-override testsuv run --extra dev ruff check src/teich/bench src/teich/trace_readme.py src/teich/config.py src/teich/cli.py-> All checks passeduv build --wheel-> builtteich-0.2.8-py3-none-any.whl; confirmedteich/templates/dataset_card.md.j2andteich/bench/templates/agent.dockerfile.j2are packagedCompatibility
--mode prompts(the default),github_repo, and every existing config key behave exactly as before; bench is opt-in behindteich[harbor]/teich[swe]/teich[bench], the newoutputknobs all default to today's behavior (keep_bench_imagesdefaults to purge, the card renders byte-identically), and the only new core runtime dependency isjinja2>=3.1(previously dev-only).Note: the offline surface (dataset loading, Dockerfile render, reward mapping, backend selection/registration) is unit-tested above; the swe-bench Docker build -> run agent -> grade path is integration-validated only, since CI has no Docker.