pluginbench 0.5.0__py3-none-any.whl
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/__init__.py +3 -0
- pluginbench/__main__.py +3 -0
- pluginbench/assessment-charts.html +790 -0
- pluginbench/assessment.py +316 -0
- pluginbench/assessment_charts.py +1225 -0
- pluginbench/assessment_metrics.py +1956 -0
- pluginbench/assessment_models.py +127 -0
- pluginbench/baselines.py +792 -0
- pluginbench/claude.py +374 -0
- pluginbench/cli.py +672 -0
- pluginbench/code_diff.py +207 -0
- pluginbench/codex.py +451 -0
- pluginbench/codex_events.py +585 -0
- pluginbench/collectors.py +591 -0
- pluginbench/compare.py +927 -0
- pluginbench/components.py +129 -0
- pluginbench/coverage_report.py +610 -0
- pluginbench/datasets.py +207 -0
- pluginbench/evaluation.example.yaml +45 -0
- pluginbench/evaluation.py +1651 -0
- pluginbench/evaluation_report.py +1004 -0
- pluginbench/fallow_parse.mjs +101 -0
- pluginbench/harbor_backend.py +2946 -0
- pluginbench/isolation.py +314 -0
- pluginbench/judge.py +683 -0
- pluginbench/language_analysis.py +915 -0
- pluginbench/ledger.py +217 -0
- pluginbench/measurement_profiles.py +616 -0
- pluginbench/native_treatment_runtime.js +221 -0
- pluginbench/native_treatments.py +155 -0
- pluginbench/observer_data/go-analysis-v1/go.mod +10 -0
- pluginbench/observer_data/go-analysis-v1/go.sum +10 -0
- pluginbench/observer_data/go-analysis-v1/main.go +64 -0
- pluginbench/observer_data/oxc-parser-0.126.0/package-lock.json +449 -0
- pluginbench/observer_data/oxc-parser-0.126.0/package.json +5 -0
- pluginbench/parallel.py +101 -0
- pluginbench/processes.py +100 -0
- pluginbench/remeasure.py +992 -0
- pluginbench/run_lock.py +227 -0
- pluginbench/run_status.py +518 -0
- pluginbench/study.py +1858 -0
- pluginbench/study_review.py +924 -0
- pluginbench/submissions.py +486 -0
- pluginbench/task_sources.py +403 -0
- pluginbench/treatments.py +388 -0
- pluginbench/trial_containers.py +94 -0
- pluginbench/vendor/ECHARTS-LICENSE +222 -0
- pluginbench/vendor/ECHARTS-NOTICE +5 -0
- pluginbench/vendor/ZRENDER-LICENSE +29 -0
- pluginbench/vendor/echarts.min.js +48 -0
- pluginbench-0.5.0.dist-info/METADATA +294 -0
- pluginbench-0.5.0.dist-info/RECORD +58 -0
- pluginbench-0.5.0.dist-info/WHEEL +4 -0
- pluginbench-0.5.0.dist-info/entry_points.txt +2 -0
- pluginbench-0.5.0.dist-info/licenses/LICENSE +201 -0
- pluginbench-0.5.0.dist-info/licenses/src/pluginbench/vendor/ECHARTS-LICENSE +222 -0
- pluginbench-0.5.0.dist-info/licenses/src/pluginbench/vendor/ECHARTS-NOTICE +5 -0
- pluginbench-0.5.0.dist-info/licenses/src/pluginbench/vendor/ZRENDER-LICENSE +29 -0
pluginbench/datasets.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""Frozen source and standard Harbor task contracts for evaluation studies."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Literal
|
|
10
|
+
|
|
11
|
+
import yaml
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
|
|
14
|
+
from pluginbench import harbor_backend, submissions
|
|
15
|
+
from pluginbench.assessment_metrics import inspect_snapshot
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CatalogModel(BaseModel):
|
|
19
|
+
model_config = ConfigDict(extra="forbid")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TaskEntry(CatalogModel):
|
|
23
|
+
id: str = Field(min_length=1)
|
|
24
|
+
prompt: str = Field(min_length=1)
|
|
25
|
+
fixture: Path
|
|
26
|
+
native_task: Path
|
|
27
|
+
provenance: dict[str, str]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TaskCatalog(CatalogModel):
|
|
31
|
+
schema_version: Literal[1]
|
|
32
|
+
tasks: list[TaskEntry] = Field(min_length=1)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class TaskSpec:
|
|
37
|
+
task_id: str
|
|
38
|
+
prompt: str
|
|
39
|
+
fixture: Path
|
|
40
|
+
native_task: Path
|
|
41
|
+
digest: str
|
|
42
|
+
provenance: dict[str, str]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def native_identity(task: TaskSpec | TaskEntry) -> dict[str, object]:
|
|
46
|
+
"""Read the prepared runtime payload without hiding incomplete preparation."""
|
|
47
|
+
value = json.loads(task.provenance.get("native_task_identity", "null"))
|
|
48
|
+
if not isinstance(value, dict) or not all(isinstance(key, str) for key in value):
|
|
49
|
+
raise ValueError("task requires a prepared native_task_identity")
|
|
50
|
+
cleanup = value.get("cleanup")
|
|
51
|
+
if (
|
|
52
|
+
value.get("status") != "complete"
|
|
53
|
+
or not isinstance(cleanup, dict)
|
|
54
|
+
or cleanup.get("status") != "complete"
|
|
55
|
+
):
|
|
56
|
+
raise ValueError("native task preparation or cleanup is incomplete")
|
|
57
|
+
return value
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def portable_native_identity(identity: dict[str, object]) -> dict[str, object]:
|
|
61
|
+
"""Run-folder locations are not part of a reusable coding identity."""
|
|
62
|
+
keys = {
|
|
63
|
+
"task_assets_sha256",
|
|
64
|
+
"original_task_assets_sha256",
|
|
65
|
+
"source_digest",
|
|
66
|
+
"workdir",
|
|
67
|
+
"image_identity",
|
|
68
|
+
"toolchain_identity",
|
|
69
|
+
"grader_image_identity",
|
|
70
|
+
"resources",
|
|
71
|
+
"isolation",
|
|
72
|
+
"trusted_tests_sha256",
|
|
73
|
+
"verifier_test_delivery",
|
|
74
|
+
"transport_policy",
|
|
75
|
+
"preparation_key",
|
|
76
|
+
"preparation_policy",
|
|
77
|
+
}
|
|
78
|
+
missing = keys - identity.keys()
|
|
79
|
+
if missing:
|
|
80
|
+
raise ValueError("native coding identity is incomplete: " + ", ".join(sorted(missing)))
|
|
81
|
+
portable = {
|
|
82
|
+
**{key: identity[key] for key in sorted(keys)},
|
|
83
|
+
"source_policy": identity.get("source_policy", "legacy-unqualified"),
|
|
84
|
+
"source_qualification": submissions.portable_qualification(
|
|
85
|
+
identity.get("source_qualification")
|
|
86
|
+
),
|
|
87
|
+
}
|
|
88
|
+
# Absent when the task sets its own git identity, so identities saved before it still match.
|
|
89
|
+
if "supplied_git_identity" in identity:
|
|
90
|
+
portable["supplied_git_identity"] = identity["supplied_git_identity"]
|
|
91
|
+
return portable
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _catalog_path(source: Path, item: Path) -> Path:
|
|
95
|
+
if item.is_absolute() or ".." in item.parts:
|
|
96
|
+
raise ValueError("task source and native assets must be relative to their catalog")
|
|
97
|
+
raw = source.parent / item
|
|
98
|
+
if any(candidate.is_symlink() for candidate in (raw, *raw.parents)):
|
|
99
|
+
raise ValueError(f"task input must not be a symlink: {raw}")
|
|
100
|
+
resolved = raw.resolve()
|
|
101
|
+
if not resolved.is_dir():
|
|
102
|
+
raise ValueError(f"task input is not a directory: {resolved}")
|
|
103
|
+
return resolved
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def source_metadata(source: Path) -> dict[str, dict[str, object]]:
|
|
107
|
+
return {
|
|
108
|
+
path.relative_to(source).as_posix(): {
|
|
109
|
+
"mode": path.stat().st_mode & 0o111,
|
|
110
|
+
"directory": path.is_dir(),
|
|
111
|
+
}
|
|
112
|
+
for path in sorted(source.rglob("*"))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _task_digest(entry: TaskEntry, fixture: Path, native_task: Path) -> str:
|
|
117
|
+
identity = native_identity(entry)
|
|
118
|
+
source_digest = inspect_snapshot(fixture)["digest"]
|
|
119
|
+
asset_digest = harbor_backend.native_task_assets(native_task)
|
|
120
|
+
if source_digest != identity.get("source_digest"):
|
|
121
|
+
raise ValueError("prepared native source changed")
|
|
122
|
+
if asset_digest != identity.get("task_assets_sha256"):
|
|
123
|
+
raise ValueError("prepared native task assets changed")
|
|
124
|
+
return _contract_digest(
|
|
125
|
+
task_id=entry.id,
|
|
126
|
+
prompt=entry.prompt,
|
|
127
|
+
provenance=entry.provenance,
|
|
128
|
+
fixture=fixture,
|
|
129
|
+
source_digest=source_digest,
|
|
130
|
+
native=portable_native_identity(identity),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def task_digest(task: TaskSpec, native: dict[str, object]) -> str:
|
|
135
|
+
"""The task's digest as it would be with `native` as its portable native identity.
|
|
136
|
+
|
|
137
|
+
A saved vanilla cohort's digest covers its own native identity, so it is checked against
|
|
138
|
+
the cohort's saved identity, not this run's.
|
|
139
|
+
"""
|
|
140
|
+
return _contract_digest(
|
|
141
|
+
task_id=task.task_id,
|
|
142
|
+
prompt=task.prompt,
|
|
143
|
+
provenance=task.provenance,
|
|
144
|
+
fixture=task.fixture,
|
|
145
|
+
source_digest=inspect_snapshot(task.fixture)["digest"],
|
|
146
|
+
native=native,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _contract_digest(
|
|
151
|
+
*,
|
|
152
|
+
task_id: str,
|
|
153
|
+
prompt: str,
|
|
154
|
+
provenance: dict[str, str],
|
|
155
|
+
fixture: Path,
|
|
156
|
+
source_digest: object,
|
|
157
|
+
native: dict[str, object],
|
|
158
|
+
) -> str:
|
|
159
|
+
contract = {
|
|
160
|
+
"id": task_id,
|
|
161
|
+
"prompt": prompt,
|
|
162
|
+
"provenance": {
|
|
163
|
+
key: value for key, value in provenance.items() if key != "native_task_identity"
|
|
164
|
+
},
|
|
165
|
+
"source_digest": source_digest,
|
|
166
|
+
"source_metadata": source_metadata(fixture),
|
|
167
|
+
"native_task_identity": native,
|
|
168
|
+
}
|
|
169
|
+
encoded = json.dumps(contract, sort_keys=True, separators=(",", ":")).encode()
|
|
170
|
+
return "sha256:" + hashlib.sha256(encoded).hexdigest()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def load_tasks(source: Path, task_ids: list[str] | None = None) -> list[TaskSpec]:
|
|
174
|
+
try:
|
|
175
|
+
loaded = yaml.safe_load(source.read_text())
|
|
176
|
+
except (OSError, yaml.YAMLError) as exc:
|
|
177
|
+
raise ValueError(f"cannot read task catalog {source}: {exc}") from exc
|
|
178
|
+
catalog = TaskCatalog.model_validate(loaded)
|
|
179
|
+
by_id = {entry.id: entry for entry in catalog.tasks}
|
|
180
|
+
if len(by_id) != len(catalog.tasks):
|
|
181
|
+
raise ValueError("task catalog contains duplicate task IDs")
|
|
182
|
+
selected = list(by_id) if task_ids is None else task_ids
|
|
183
|
+
if not selected or any(not task_id.strip() for task_id in selected):
|
|
184
|
+
raise ValueError("selected task IDs must be nonempty")
|
|
185
|
+
if len(set(selected)) != len(selected):
|
|
186
|
+
raise ValueError("selected task IDs must be unique")
|
|
187
|
+
missing = [task_id for task_id in selected if task_id not in by_id]
|
|
188
|
+
if missing:
|
|
189
|
+
raise ValueError("selected task IDs are missing from catalog: " + ", ".join(missing))
|
|
190
|
+
tasks = []
|
|
191
|
+
for task_id in selected:
|
|
192
|
+
entry = by_id[task_id]
|
|
193
|
+
fixture = _catalog_path(source, entry.fixture)
|
|
194
|
+
native_task = _catalog_path(source, entry.native_task)
|
|
195
|
+
if native_task.is_relative_to(fixture) or fixture.is_relative_to(native_task):
|
|
196
|
+
raise ValueError("native task assets must be outside the writable source fixture")
|
|
197
|
+
tasks.append(
|
|
198
|
+
TaskSpec(
|
|
199
|
+
task_id,
|
|
200
|
+
entry.prompt,
|
|
201
|
+
fixture,
|
|
202
|
+
native_task,
|
|
203
|
+
_task_digest(entry, fixture, native_task),
|
|
204
|
+
entry.provenance,
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
return tasks
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Workflow versus Superpowers
|
|
2
|
+
runner:
|
|
3
|
+
harness: codex # Native treatment coding currently requires Codex.
|
|
4
|
+
model: gpt-6.1-sol
|
|
5
|
+
reasoning: medium
|
|
6
|
+
|
|
7
|
+
treatments:
|
|
8
|
+
workflow:
|
|
9
|
+
plugins: [/path/to/workflow-plugin]
|
|
10
|
+
superpowers:
|
|
11
|
+
skills: [/path/to/superpowers/skills]
|
|
12
|
+
# Optional guidance: [/path/to/core.md] uses native global instructions.
|
|
13
|
+
# Vanilla is included automatically; every arm keeps the same task prompt.
|
|
14
|
+
capabilities:
|
|
15
|
+
subagents: true
|
|
16
|
+
max_threads: 4 # Concurrent children, not a lifetime call/spend cap.
|
|
17
|
+
|
|
18
|
+
tasks:
|
|
19
|
+
dataset: datacurve/deep-swe-1-1
|
|
20
|
+
version: sha256:5affcd534fd90ac85d202d4c63f8b35ddc942140afdd5d60014a21365440a2f5
|
|
21
|
+
ids:
|
|
22
|
+
- ofetch-per-origin-circuit-breaker
|
|
23
|
+
- testem-per-launcher-reports
|
|
24
|
+
- happy-dom-abort-pending-body-reads
|
|
25
|
+
|
|
26
|
+
measurements: [tokens, lines, cyclomatic, cognitive, dead_code, review]
|
|
27
|
+
# Unsupported measurements remain unknown with a reason.
|
|
28
|
+
# Add coverage after configuring a command and supported coverage output supported by every selected task image:
|
|
29
|
+
# measurement_settings:
|
|
30
|
+
# coverage:
|
|
31
|
+
# command: [npm, run, coverage]
|
|
32
|
+
# report_path: coverage/lcov.info
|
|
33
|
+
# tool_version: "the exact installed coverage tool version"
|
|
34
|
+
reviewer:
|
|
35
|
+
harness: codex
|
|
36
|
+
model: gpt-6-astra
|
|
37
|
+
reasoning: high
|
|
38
|
+
guidance: [/path/to/fixed-review-standards.md] # Same criteria for all outputs.
|
|
39
|
+
|
|
40
|
+
repeats: 3
|
|
41
|
+
# jobs: 2 # Optional, 1 to 4 (default 2); fails if it exceeds Docker capacity.
|
|
42
|
+
# CPU and memory default to task.toml; Docker must fit them before any model call.
|
|
43
|
+
# container_cpus: 2
|
|
44
|
+
# container_memory_mb: 8192
|
|
45
|
+
# timeout_seconds: 1800 # Separate agent budget; shorter than these tasks declare.
|