Add scripts/export_openeval.py: optional EvalPort ResultSet export - #336
Open
adhabnr-ux wants to merge 2 commits into
Open
Add scripts/export_openeval.py: optional EvalPort ResultSet export#336adhabnr-ux wants to merge 2 commits into
adhabnr-ux wants to merge 2 commits into
Conversation
Adds an optional, additive script that converts a batch's rescore-summary.json (+ each run's own run-meta.json) into an EvalPort (https://github.com/adhabnr-ux/evalport) ResultSet -- a small open interchange format for portable LLM/agent evaluation results. Scoped in TIGER-AI-Lab#322: maintainer Perry2004 confirmed this is the results side (complementing clawbench-harbor-adapt/clawbench-edgebench-adapt, which import test-case definitions the other direction) and suggested it land as "a simple script inside script/" rather than a new adapters/ package or a core dependency -- this follows that shape exactly. The script has no hard dependency on evalport-sdk: the conversion itself is pure stdlib, and evalport-sdk is only used, if installed, to validate the produced ResultSet against the real EvalPort schema. Grounded in the current real source, not just docs/scoring.md: src/clawbench/runner/run_support/metadata.py (make_run_meta()), src/clawbench/eval/rescore.py (aggregate_batch()/rescore_one()), and src/clawbench/runner/judge_llm.py (judge_request()'s match/reason shape). Result.passed is `intercepted AND judge_match is True`, per docs/scoring.md's final_pass rule; the interception and judge stages each become their own GraderResult (gr_interception, gr_judge_match) rather than being collapsed into one opaque score. Testing: 11 tests (tests/test_export_openeval.py), covering run_to_result() and to_openeval() directly plus CLI smoke/error-path tests via subprocess, all passing against the real, installed evalport-sdk 1.3.1's openeval.validate.validate_result_set() -- not a mock. Also clean under this repo's own ruff and pyright configuration. Signed-off-by: adhabnr-ux <adhabnr@gmail.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RtocdH3tKifGdkiCZxxV3F
adhabnr-ux
added a commit
to adhabnr-ux/evalport
that referenced
this pull request
Sep 2, 2026
…/ClawBench#336 is open The README's Status section described this package as an interim home pending fork/PR access to TIGER-AI-Lab/ClawBench. That's now resolved -- TIGER-AI-Lab/ClawBench#336 ports this same tested logic into scripts/export_openeval.py, the shape the maintainer actually asked for on #322. Update the doc so it doesn't read as still-blocked. Signed-off-by: adhabnr-ux <adhabnr@gmail.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RtocdH3tKifGdkiCZxxV3F
…d import
test_to_openeval_builds_valid_result_set_and_summary() unconditionally
imported openeval.validate, but evalport-sdk is intentionally NOT a
project dependency (per this PR's own design: export_openeval.py
validates against the real OpenEval schema only when evalport-sdk
happens to be installed, and skips that step otherwise). CI's `uv run
--frozen pytest` doesn't install it, so the test hard-failed with
ModuleNotFoundError: No module named 'openeval'.
Fixed by gating that one validation step behind
pytest.importorskip("openeval.validate"), mirroring the script's own
graceful-degradation behavior in the test suite. Confirmed both paths
locally:
- uv sync --frozen && uv run --frozen pytest -q
-> 228 passed, 1 skipped (openeval not installed, matches real CI)
- same venv + `pip install evalport-sdk`
-> 229 passed (real schema validation actually runs and passes)
Also fixes the static-check job: `uv run --frozen ruff format --check .`
was failing on both touched files (pre-existing formatting drift from
how I originally wrote them, not from this fix) - reformatted with the
exact pinned ruff==0.15.12 from uv.lock. Re-verified clean:
- uv run --frozen ruff format --check . -> 88 files already formatted
- uv run --frozen ruff check . -> All checks passed!
- uv run --frozen pyright src/clawbench tests -> 0 errors, 0 warnings
No behavioral change to export_openeval.py; only formatting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RtocdH3tKifGdkiCZxxV3F
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
Resolves #322.
What this adds
An optional, additive script (
scripts/export_openeval.py, + a thinscripts/export_openeval.shwrapper matchingscripts/rescore.sh's shape) that converts a batch'srescore-summary.json(enriched with each run's ownrun-meta.json) into an EvalPortResultSet— a small open interchange format for portable LLM/agent evaluation results.Per the discussion on #322: @Perry2004 confirmed this is the results side (complementing
clawbench-harbor-adapt/clawbench-edgebench-adapt, which import test-case definitions the other direction) and suggested it land as "a simple script insidescript/" rather than a newadapters/-style package or a core dependency — this PR follows that shape exactly. There's a longer story behind why this took a couple of tries to land in the right place, laid out honestly below.No new dependency
The script does not require
evalport-sdkto run — the conversion itself is pure stdlib (argparse/json/pathlib). Ifevalport-sdkhappens to be installed, the script uses it to validate theResultSetit produces against the real EvalPort JSON Schema before writing it out (and prints a clear error if validation fails); if it isn't installed, the script says so and skips that step.pyproject.toml's dependency list is untouched either way.Grounded in the real, current source — not just
docs/scoring.mddocs/scoring.mddescribes an idealized/older shape (judge_match/final_passmerged into one per-run record). I read the actual current code instead:run-meta.json(make_run_meta()insrc/clawbench/runner/run_support/metadata.py) carriestest_case,instruction,model,harness,intercepted,result_category,failure_category,adjusted_eligible,duration_seconds— but notjudge_matchorfinal_pass.judge_llm.jsonfor the default "lenient" rubric,judge.jsonfor "strict" —JUDGE_FILEinsrc/clawbench/eval/rescore.py) carriesmatch/reason(src/clawbench/runner/judge_llm.py'sjudge_request()).rescore-summary.json(aggregate_batch()) rolls a batch inton_total,n_intercepted,judge_model,rubrics, and atasks[]list shaped{"task_id", "test_case", "intercepted", "match_<rubric>", "reason_<rubric>"}.tasks[]rows don't carryinstruction/model/harness— only each run's ownrun-meta.jsondoes, so the script walks the batch dir for those to enrich eachResult.Result.passedisintercepted AND judge_match is True, matchingdocs/scoring.md'sfinal_passrule. The interception and judge stages each become their ownGraderResult(gr_interception,gr_judge_match) rather than being collapsed into one opaque score, so the two-stage pipeline is still visible in the exported result.Testing
tests/test_export_openeval.py— 11 tests, all passing locally: direct unit tests ofrun_to_result()/to_openeval()(interception/judge grader shapes, the never-intercepted case, an inconclusive judge verdict, thetask_id-fallback path, rejecting an unknown--rubric, and — importantly — that a stalematch_<rubric>key on a task row that was never actually intercepted doesn't produce a fabricated judge grader), plus CLI smoke tests viasubprocess(matching the patterntests/test_harbor_adapter.py::test_harbor_adapter_cli_smokealready uses) covering a full run, a clean error whenrescore-summary.jsonis missing, and--help. All validated against the real, installedevalport-sdk1.3.1'sopeneval.validate.validate_result_set()— not a mock. Also clean under this repo's ownruff/pyrightconfig.CHANGELOG.mdupdated under[Unreleased]perCONTRIBUTING.md.Honest background on where this landed
I built and tested this adapter's logic once already, then hit a real tooling wall: at the time, I had no way to fork this repo or open a branch directly against it, so — rather than sit on tested work — I shipped the logic as a standalone package in EvalPort's own repo (
adapters/clawbench-openeval-adapter/) as an interim home, while saying plainly in that package's README that the intent was still to land it here once that was possible. That's now resolved, so this PR ports the same tested logic into the shape you actually asked for on #322 (scripts/, not a new package) rather than leaving the interim version as the final answer.— Sahi, independent contributor (not affiliated with TIGER-AI-Lab)
🤖 Generated with Claude Code
https://claude.ai/code/session_01RtocdH3tKifGdkiCZxxV3F