pluginbench 0.5.1__tar.gz → 0.6.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.
- {pluginbench-0.5.1 → pluginbench-0.6.0}/PKG-INFO +8 -4
- {pluginbench-0.5.1 → pluginbench-0.6.0}/README.md +7 -3
- {pluginbench-0.5.1 → pluginbench-0.6.0}/pyproject.toml +1 -1
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/__init__.py +1 -1
- pluginbench-0.6.0/src/pluginbench/arms.py +6 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/assessment-charts.html +11 -10
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/assessment_charts.py +8 -1
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/cli.py +17 -3
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/codex.py +2 -1
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/codex_events.py +181 -58
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/compare.py +35 -6
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/components.py +2 -2
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/evaluation.py +37 -24
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/evaluation_report.py +30 -5
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/harbor_backend.py +34 -1
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/isolation.py +20 -10
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/judge.py +298 -33
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/parallel.py +20 -10
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/run_status.py +45 -15
- pluginbench-0.6.0/src/pluginbench/saved_usage.py +68 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/study.py +19 -2
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/study_review.py +5 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/.gitignore +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/LICENSE +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/__main__.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/assessment.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/assessment_metrics.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/assessment_models.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/baselines.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/claude.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/code_diff.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/collectors.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/coverage_report.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/datasets.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/evaluation.example.yaml +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/fallow_parse.mjs +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/language_analysis.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/ledger.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/measurement_profiles.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/native_treatment_runtime.js +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/native_treatments.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/observer_data/go-analysis-v1/go.mod +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/observer_data/go-analysis-v1/go.sum +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/observer_data/go-analysis-v1/main.go +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/observer_data/oxc-parser-0.126.0/package-lock.json +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/observer_data/oxc-parser-0.126.0/package.json +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/processes.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/remeasure.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/run_lock.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/submissions.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/task_sources.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/treatments.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/trial_containers.py +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/vendor/ECHARTS-LICENSE +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/vendor/ECHARTS-NOTICE +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/vendor/ZRENDER-LICENSE +0 -0
- {pluginbench-0.5.1 → pluginbench-0.6.0}/src/pluginbench/vendor/echarts.min.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pluginbench
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Test whether skills help coding agents make fewer mistakes.
|
|
5
5
|
Project-URL: Homepage, https://github.com/filipgutica/pluginbench
|
|
6
6
|
Project-URL: Documentation, https://github.com/filipgutica/pluginbench/blob/main/docs/running-evaluations.md
|
|
@@ -70,7 +70,11 @@ PluginBench runs on Linux or macOS. Every run needs:
|
|
|
70
70
|
| Python | 3.12 or later | Runs PluginBench |
|
|
71
71
|
| Git | Any recent | Task sources and patches |
|
|
72
72
|
| Docker | Linux containers on a local Unix socket, with Compose and Buildx | Each attempt and its verifier run in containers |
|
|
73
|
-
| Codex CLI |
|
|
73
|
+
| Codex CLI | Logged in; verified trace versions: 0.162.1, 0.167.0, 0.167.1 | Coding agent. PluginBench copies its login from `~/.codex/auth.json` to the pinned container CLI. Unverified versions warn and continue; incompatible traces or unverified inherited history leave tokens unavailable. |
|
|
74
|
+
|
|
75
|
+
Set `runner.cli_version: "0.167.0"` to select the exact coding CLI version installed in task containers.
|
|
76
|
+
When omitted, preparation pins the host CLI's version. Resume keeps that pin across host CLI updates.
|
|
77
|
+
Independent reviews use the host CLI. See [verified coding CLIs](docs/running-evaluations.md#verified-coding-clis) for credential compatibility.
|
|
74
78
|
|
|
75
79
|
Some measures and commands need more:
|
|
76
80
|
|
|
@@ -145,7 +149,7 @@ This run uses one public DeepSWE task, vanilla only, and one repeat. It needs no
|
|
|
145
149
|
pluginbench run evaluation.yaml
|
|
146
150
|
```
|
|
147
151
|
|
|
148
|
-
`run` prints the plan and asks `Start evaluation?` (`--yes` skips the question).
|
|
152
|
+
`run` prints the plan and asks `Start evaluation?` (`--yes` skips the question; in a script with no answer to give, pass `--yes`).
|
|
149
153
|
It then runs the same checks as `check`, starts the run in the background, and prints the run directory, for example `runs/20261110T101500123456Z`.
|
|
150
154
|
|
|
151
155
|
5. Follow it:
|
|
@@ -269,7 +273,7 @@ Every setting lives in the config. Flags only control how a command runs.
|
|
|
269
273
|
|
|
270
274
|
## Stop, resume, and reuse
|
|
271
275
|
|
|
272
|
-
- `pluginbench stop runs/<run>` stops new attempts.
|
|
276
|
+
- `pluginbench stop runs/<run>` stops new attempts. Attempts already coding finish and are kept, including with `jobs: 1`. Resume never codes complete attempts again.
|
|
273
277
|
- `pluginbench run runs/<run>` resumes. A run that was killed, for example by a host shutdown, shows `interrupted`, and `status` prints this command. Complete attempts are kept and never coded again. An attempt cut off mid-call is recorded as `discarded-interrupted` and a new attempt takes its slot. Containers the killed attempt left running are stopped before coding resumes, and `status` names them until then. Resume cannot repair a failing solution.
|
|
274
278
|
- If Codex fails before its first model turn, for example during a network outage, the run stops instead of scoring the attempt. Resume when Codex is reachable.
|
|
275
279
|
- Vanilla attempts are cached in `$XDG_CACHE_HOME/pluginbench` (default `~/.cache/pluginbench`) and reused by any later run with the same task, model, and settings. Grading and measures run again. Reports mark reused attempts. [Baselines and reports](docs/running-evaluations.md#baselines-and-reports).
|
|
@@ -34,7 +34,11 @@ PluginBench runs on Linux or macOS. Every run needs:
|
|
|
34
34
|
| Python | 3.12 or later | Runs PluginBench |
|
|
35
35
|
| Git | Any recent | Task sources and patches |
|
|
36
36
|
| Docker | Linux containers on a local Unix socket, with Compose and Buildx | Each attempt and its verifier run in containers |
|
|
37
|
-
| Codex CLI |
|
|
37
|
+
| Codex CLI | Logged in; verified trace versions: 0.162.1, 0.167.0, 0.167.1 | Coding agent. PluginBench copies its login from `~/.codex/auth.json` to the pinned container CLI. Unverified versions warn and continue; incompatible traces or unverified inherited history leave tokens unavailable. |
|
|
38
|
+
|
|
39
|
+
Set `runner.cli_version: "0.167.0"` to select the exact coding CLI version installed in task containers.
|
|
40
|
+
When omitted, preparation pins the host CLI's version. Resume keeps that pin across host CLI updates.
|
|
41
|
+
Independent reviews use the host CLI. See [verified coding CLIs](docs/running-evaluations.md#verified-coding-clis) for credential compatibility.
|
|
38
42
|
|
|
39
43
|
Some measures and commands need more:
|
|
40
44
|
|
|
@@ -109,7 +113,7 @@ This run uses one public DeepSWE task, vanilla only, and one repeat. It needs no
|
|
|
109
113
|
pluginbench run evaluation.yaml
|
|
110
114
|
```
|
|
111
115
|
|
|
112
|
-
`run` prints the plan and asks `Start evaluation?` (`--yes` skips the question).
|
|
116
|
+
`run` prints the plan and asks `Start evaluation?` (`--yes` skips the question; in a script with no answer to give, pass `--yes`).
|
|
113
117
|
It then runs the same checks as `check`, starts the run in the background, and prints the run directory, for example `runs/20261110T101500123456Z`.
|
|
114
118
|
|
|
115
119
|
5. Follow it:
|
|
@@ -233,7 +237,7 @@ Every setting lives in the config. Flags only control how a command runs.
|
|
|
233
237
|
|
|
234
238
|
## Stop, resume, and reuse
|
|
235
239
|
|
|
236
|
-
- `pluginbench stop runs/<run>` stops new attempts.
|
|
240
|
+
- `pluginbench stop runs/<run>` stops new attempts. Attempts already coding finish and are kept, including with `jobs: 1`. Resume never codes complete attempts again.
|
|
237
241
|
- `pluginbench run runs/<run>` resumes. A run that was killed, for example by a host shutdown, shows `interrupted`, and `status` prints this command. Complete attempts are kept and never coded again. An attempt cut off mid-call is recorded as `discarded-interrupted` and a new attempt takes its slot. Containers the killed attempt left running are stopped before coding resumes, and `status` names them until then. Resume cannot repair a failing solution.
|
|
238
242
|
- If Codex fails before its first model turn, for example during a network outage, the run stops instead of scoring the attempt. Resume when Codex is reachable.
|
|
239
243
|
- Vanilla attempts are cached in `$XDG_CACHE_HOME/pluginbench` (default `~/.cache/pluginbench`) and reused by any later run with the same task, model, and settings. Grading and measures run again. Reports mark reused attempts. [Baselines and reports](docs/running-evaluations.md#baselines-and-reports).
|
|
@@ -273,7 +273,8 @@ const formatTick = value => new Intl.NumberFormat(undefined, { notation: Math.ab
|
|
|
273
273
|
// Values below one keep two significant digits so a small real difference never reads as zero.
|
|
274
274
|
const formatCompact = value => value === null || value === undefined ? "Unknown" : new Intl.NumberFormat(undefined, Math.abs(value) >= 10000 ? { notation: "compact", maximumFractionDigits: 2 } : value !== 0 && Math.abs(value) < 1 ? { maximumSignificantDigits: 2 } : { maximumFractionDigits: 2 }).format(value);
|
|
275
275
|
const formatSigned = value => (value > 0 ? "+" : "") + formatCompact(value);
|
|
276
|
-
const
|
|
276
|
+
const vanillaArm = data.kind === "evaluation" ? "vanilla" : "baseline";
|
|
277
|
+
const armLabel = (arm) => arm === vanillaArm ? "Vanilla" : arm === "unknown" ? "Unlabelled skill set" : arm;
|
|
277
278
|
const choices = (select, values) => {
|
|
278
279
|
for (const value of values) create("option", value, select).value = value;
|
|
279
280
|
};
|
|
@@ -283,10 +284,10 @@ const knownTasks = unique(observations.map(row => row.task).filter(task => task
|
|
|
283
284
|
choices(taskSelect, knownTasks);
|
|
284
285
|
taskSelect.value = "";
|
|
285
286
|
// One package order and color per package everywhere: Vanilla first, then packages by name.
|
|
286
|
-
const packageNames = unique([...observations.map(row => row.arm), ...data.attempts.map(row => row.guidance)]).filter(arm => arm !==
|
|
287
|
-
const armRank = arm => arm ===
|
|
287
|
+
const packageNames = unique([...observations.map(row => row.arm), ...data.attempts.map(row => row.guidance)]).filter(arm => arm !== vanillaArm).sort();
|
|
288
|
+
const armRank = arm => arm === vanillaArm ? -1 : packageNames.indexOf(arm);
|
|
288
289
|
const byArm = (a, b) => armRank(a) - armRank(b);
|
|
289
|
-
const seriesVariable = arm => arm !==
|
|
290
|
+
const seriesVariable = arm => arm !== vanillaArm && armRank(arm) >= 0 ? "--series-" + (armRank(arm) % 8 + 1) : "--pb-muted";
|
|
290
291
|
const viewLabel = create("label", "Chart values", document.querySelector(".controls"));
|
|
291
292
|
const comparisonView = create("select", undefined, viewLabel);
|
|
292
293
|
for (const [value, label] of [["delta", "Delta vs vanilla"], ["raw", "Raw values"]]) create("option", label, comparisonView).value = value;
|
|
@@ -414,7 +415,7 @@ function showPackageSummary() {
|
|
|
414
415
|
const text = result?.value !== undefined && result.value !== null ? formatCompact(result.value) : "Unknown";
|
|
415
416
|
create("div", text + (groups.length > 1 && result?.analysis_group ? " · " + result.analysis_group : ""), cell);
|
|
416
417
|
if (!taskSelect.value && result) create("small", result.measured_tasks + "/" + result.assigned_tasks + " tasks", cell);
|
|
417
|
-
if (arm !==
|
|
418
|
+
if (arm !== vanillaArm) {
|
|
418
419
|
const deltaRows = taskSelect.value ? (data.comparisons || []).filter(row => row.task === taskSelect.value && row.metric === metric && row.guidance === arm) : [result];
|
|
419
420
|
const delta = deltaRows.length === 1 ? deltaRows[0] : null;
|
|
420
421
|
const value = delta?.absolute_delta_vs_vanilla ?? null;
|
|
@@ -496,7 +497,7 @@ function showCompleteness() {
|
|
|
496
497
|
// Task × measure × package: measured, unknown with its reason, or not configured for the task.
|
|
497
498
|
const matrix = (data.completeness_matrix || []).filter(row => !taskSelect.value || row.task === taskSelect.value);
|
|
498
499
|
if (!matrix.length) return;
|
|
499
|
-
const arms = unique(matrix.flatMap(row => Object.keys(row.cells)));
|
|
500
|
+
const arms = unique(matrix.flatMap(row => Object.keys(row.cells))).sort(byArm);
|
|
500
501
|
const grid = create("table", undefined, body);
|
|
501
502
|
const head = create("tr", undefined, grid);
|
|
502
503
|
for (const label of ["Task", "Measure", ...arms.map(armLabel)]) create("th", label, head);
|
|
@@ -626,10 +627,10 @@ function evaluationPlotRows({ rows, metric }) {
|
|
|
626
627
|
const raw = comparisonView.value === "raw", overview = !taskSelect.value;
|
|
627
628
|
if (overview && chartBreakdown.value === "tasks") {
|
|
628
629
|
const medians = raw ? (data.raw_medians || []) : (data.comparisons || []);
|
|
629
|
-
return medians.filter(row => row.metric === metric).map(row => ({ ...row, guidance: row.guidance || row.arm, label: row.task.replaceAll("-", " "), plotted: raw ? row.value : row.absolute_delta_vs_vanilla, status: raw ? "measured" : row.status, reason: raw ? "Raw task median for one measurement profile. Comparison eligibility is separate." : row.reason, population: raw ? row.measured + " runs" : "Vanilla " + row.
|
|
630
|
+
return medians.filter(row => row.metric === metric).map(row => ({ ...row, guidance: row.guidance || row.arm, label: row.task.replaceAll("-", " "), plotted: raw ? row.value : row.absolute_delta_vs_vanilla, status: raw ? "measured" : row.status, reason: raw ? "Raw task median for one measurement profile. Comparison eligibility is separate." : row.reason, population: raw ? row.measured + " runs" : "Vanilla " + row.vanilla_measured + "/" + row.vanilla_assigned + " · guidance " + row.guidance_measured + "/" + row.guidance_assigned + " runs", rawRuns: [] })).sort((a, b) => a.task.localeCompare(b.task) || Number(b.guidance === "vanilla") - Number(a.guidance === "vanilla") || a.guidance.localeCompare(b.guidance));
|
|
630
631
|
}
|
|
631
632
|
if (overview) return (data.aggregates || []).filter(row => row.metric === metric).map(row => ({ ...row, status: raw ? row.status : row.comparison_status, label: armLabel(row.guidance) + (row.analysis_group ? " · " + row.analysis_group : ""), plotted: raw ? row.value : row.absolute_delta_vs_vanilla, population: row.measured_tasks + "/" + row.assigned_tasks + " tasks", rawRuns: [] }));
|
|
632
|
-
if (!raw) return (data.comparisons || []).filter(row => row.metric === metric && row.task === taskSelect.value).map(row => ({ ...row, label: row.guidance, plotted: row.absolute_delta_vs_vanilla, population: "Vanilla " + row.
|
|
633
|
+
if (!raw) return (data.comparisons || []).filter(row => row.metric === metric && row.task === taskSelect.value).map(row => ({ ...row, label: row.guidance, plotted: row.absolute_delta_vs_vanilla, population: "Vanilla " + row.vanilla_measured + "/" + row.vanilla_assigned + " · guidance " + row.guidance_measured + "/" + row.guidance_assigned + " runs", rawRuns: [] }));
|
|
633
634
|
const medians = (data.raw_medians || []).filter(row => row.role === "handoff" && row.metric === metric && row.task === taskSelect.value);
|
|
634
635
|
const arms = unique(rows.map(row => row.arm));
|
|
635
636
|
return arms.flatMap(arm => {
|
|
@@ -652,9 +653,9 @@ function drawEvaluationPlot({ parent, rows, metric }) {
|
|
|
652
653
|
for (const label of ["Set", raw ? "Value" : "Delta vs vanilla", ...(!raw ? ["Vanilla reference", "% vs vanilla"] : []), "Status", "Measured / assigned"]) create("th", label, head);
|
|
653
654
|
for (const row of plotRows) {
|
|
654
655
|
const tr = create("tr", undefined, table);
|
|
655
|
-
for (const value of [byTask ? row.label + " · " + armLabel(row.guidance) : row.label, format(row.plotted), ...(!raw ? [format(row.
|
|
656
|
+
for (const value of [byTask ? row.label + " · " + armLabel(row.guidance) : row.label, format(row.plotted), ...(!raw ? [format(row.vanilla_value ?? row.vanilla_median ?? null), format(row.percentage_delta_vs_vanilla)] : []), row.status, row.population]) create("td", value, tr);
|
|
656
657
|
}
|
|
657
|
-
if (!plotRows.some(row => row.plotted !== null || row.rawRuns.length)) { create("p", raw ? "No defined values to plot. Complete empty scans and missing measurements are listed separately." : "No matching
|
|
658
|
+
if (!plotRows.some(row => row.plotted !== null || row.rawRuns.length)) { create("p", raw ? "No defined values to plot. Complete empty scans and missing measurements are listed separately." : "No matching vanilla values for a delta. Switch to Raw values to see the available measurements.", parent); return 0; }
|
|
658
659
|
const container = create("div", undefined, parent); container.className = "chart";
|
|
659
660
|
const arms = unique(plotRows.map(row => row.guidance));
|
|
660
661
|
const categories = grouped ? unique(plotRows.map(row => row.group)) : plotRows.map(row => byTask ? row.label + " · " + armLabel(row.guidance) : row.label);
|
|
@@ -623,6 +623,9 @@ def _observation(
|
|
|
623
623
|
eligible = isinstance(work.get("token_format"), str) and bool(work["token_format"])
|
|
624
624
|
identity.update(provenance)
|
|
625
625
|
reason = "native tokens at handoff; cached input and reasoning output subsets are not added"
|
|
626
|
+
validation = _map(work.get("usage_validation"))
|
|
627
|
+
if validation:
|
|
628
|
+
provenance["usage_validation"] = validation
|
|
626
629
|
elif kind == "loc_change":
|
|
627
630
|
classification = "source" if key.startswith("source_") else "test"
|
|
628
631
|
code_size = _map(stage.get("code_size" if classification == "source" else "test_code_size"))
|
|
@@ -897,6 +900,10 @@ def _observation(
|
|
|
897
900
|
if evidence.get("reason")
|
|
898
901
|
else "measurement unavailable; unknown is not zero",
|
|
899
902
|
)
|
|
903
|
+
if kind == "work":
|
|
904
|
+
validation_reasons = _map(work.get("usage_validation")).get("reasons")
|
|
905
|
+
if isinstance(validation_reasons, list) and validation_reasons:
|
|
906
|
+
reason += "; " + "; ".join(str(item) for item in validation_reasons)
|
|
900
907
|
else:
|
|
901
908
|
status = "measured" if compatibility else "not comparable"
|
|
902
909
|
change = _map(stage.get("change"))
|
|
@@ -1024,7 +1031,7 @@ def chart_data(report: dict[str, object]) -> dict[str, object]:
|
|
|
1024
1031
|
for row in observations:
|
|
1025
1032
|
if row.compatibility and not groups.get((row.role, row.compatibility, "baseline")):
|
|
1026
1033
|
row.status = "not comparable"
|
|
1027
|
-
row.reason += "; no matching explicit
|
|
1034
|
+
row.reason += "; no matching explicit vanilla observation"
|
|
1028
1035
|
medians = []
|
|
1029
1036
|
for (role, identity, arm), rows in groups.items():
|
|
1030
1037
|
baseline = groups.get((role, identity, "baseline"), [])
|
|
@@ -71,6 +71,17 @@ def pick(
|
|
|
71
71
|
_error(exc)
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
def _confirm(question: str) -> bool:
|
|
75
|
+
"""Ask before paid work; when no answer can be read, explain how to start instead."""
|
|
76
|
+
try:
|
|
77
|
+
return typer.confirm(question)
|
|
78
|
+
except typer.Abort:
|
|
79
|
+
if sys.stdin.isatty():
|
|
80
|
+
raise # Ctrl-C at the prompt.
|
|
81
|
+
typer.echo("No answer to confirm with. Pass --yes to start without confirmation.", err=True)
|
|
82
|
+
raise typer.Exit(1) from None
|
|
83
|
+
|
|
84
|
+
|
|
74
85
|
def _error(exc: Exception) -> None:
|
|
75
86
|
if isinstance(exc, CheckFailed):
|
|
76
87
|
typer.echo(_readiness_table(exc.report), err=True)
|
|
@@ -392,7 +403,7 @@ def run(
|
|
|
392
403
|
"no notification will be sent.",
|
|
393
404
|
err=True,
|
|
394
405
|
)
|
|
395
|
-
if not yes and not
|
|
406
|
+
if not yes and not _confirm("Start evaluation?"):
|
|
396
407
|
raise typer.Exit()
|
|
397
408
|
progress = _notifying_progress(plan["coding_calls"]) if notify else typer.echo
|
|
398
409
|
if evaluation is None:
|
|
@@ -563,7 +574,7 @@ def review(
|
|
|
563
574
|
with run_lock.hold(run):
|
|
564
575
|
plan = plan_review(run, revision)
|
|
565
576
|
typer.echo(f"{plan['review_calls']} review calls as review revision {revision}.")
|
|
566
|
-
if not yes and not
|
|
577
|
+
if not yes and not _confirm("Start review?"):
|
|
567
578
|
raise typer.Exit()
|
|
568
579
|
_review(run, revision)
|
|
569
580
|
except (OSError, ValueError, subprocess.SubprocessError) as exc:
|
|
@@ -658,12 +669,15 @@ def judge_run(
|
|
|
658
669
|
@judge_app.command("tally")
|
|
659
670
|
def judge_tally(
|
|
660
671
|
directory: Path = typer.Argument(..., help="A directory whose judge calls are complete."),
|
|
672
|
+
require_bound: bool = typer.Option(
|
|
673
|
+
False, help="Refuse legacy verdicts without input bindings."
|
|
674
|
+
),
|
|
661
675
|
) -> None:
|
|
662
676
|
"""Unblind with the key and score each pair; writes tally.json; no model calls."""
|
|
663
677
|
from pluginbench.judge import format_tally, tally
|
|
664
678
|
|
|
665
679
|
try:
|
|
666
|
-
result = tally(directory)
|
|
680
|
+
result = tally(directory, require_bound=require_bound)
|
|
667
681
|
(directory / "tally.json").write_text(json.dumps(result, indent=2) + "\n")
|
|
668
682
|
except (OSError, ValueError, KeyError) as exc:
|
|
669
683
|
_error(exc)
|
|
@@ -299,9 +299,10 @@ def invoke(
|
|
|
299
299
|
prompt: str,
|
|
300
300
|
timeout: int,
|
|
301
301
|
output_schema: Path | None = None,
|
|
302
|
+
artifacts: Path | None = None,
|
|
302
303
|
) -> dict[str, Any]:
|
|
303
304
|
"""Invoke a local reviewer with an OS-enforced read-only source packet."""
|
|
304
|
-
artifacts = area / "artifacts"
|
|
305
|
+
artifacts = area / "artifacts" if artifacts is None else artifacts
|
|
305
306
|
artifacts.mkdir(exist_ok=True)
|
|
306
307
|
if any((artifacts / name).exists() for name in ("invocation.json", "execution.json")):
|
|
307
308
|
raise ValueError("native invocation artifacts already exist; use a fresh trial area")
|