whetstone-envs 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.
- whetstone_envs-0.1.1/.gitignore +25 -0
- whetstone_envs-0.1.1/LICENSE +21 -0
- whetstone_envs-0.1.1/PKG-INFO +252 -0
- whetstone_envs-0.1.1/README.md +226 -0
- whetstone_envs-0.1.1/pyproject.toml +115 -0
- whetstone_envs-0.1.1/src/whetstone_envs/__init__.py +1 -0
- whetstone_envs-0.1.1/src/whetstone_envs/instances/__init__.py +4 -0
- whetstone_envs-0.1.1/src/whetstone_envs/instances/identity.py +9 -0
- whetstone_envs-0.1.1/src/whetstone_envs/instances/instance.py +106 -0
- whetstone_envs-0.1.1/src/whetstone_envs/manifests/__init__.py +5 -0
- whetstone_envs-0.1.1/src/whetstone_envs/manifests/hashing.py +40 -0
- whetstone_envs-0.1.1/src/whetstone_envs/manifests/manifest.py +186 -0
- whetstone_envs-0.1.1/src/whetstone_envs/manifests/validation.py +81 -0
- whetstone_envs-0.1.1/src/whetstone_envs/pools/__init__.py +4 -0
- whetstone_envs-0.1.1/src/whetstone_envs/pools/pool.py +96 -0
- whetstone_envs-0.1.1/src/whetstone_envs/pools/splitting.py +197 -0
- whetstone_envs-0.1.1/src/whetstone_envs/probes/__init__.py +7 -0
- whetstone_envs-0.1.1/src/whetstone_envs/probes/normalization.py +29 -0
- whetstone_envs-0.1.1/src/whetstone_envs/probes/rendering.py +29 -0
- whetstone_envs-0.1.1/src/whetstone_envs/py.typed +0 -0
- whetstone_envs-0.1.1/src/whetstone_envs/scoring/__init__.py +29 -0
- whetstone_envs-0.1.1/src/whetstone_envs/scoring/aggregation.py +282 -0
- whetstone_envs-0.1.1/src/whetstone_envs/scoring/exact_match.py +5 -0
- whetstone_envs-0.1.1/src/whetstone_envs/scoring/observations.py +59 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.worktrees
|
|
2
|
+
.DS_Store
|
|
3
|
+
*.swp
|
|
4
|
+
.env
|
|
5
|
+
.env.*
|
|
6
|
+
!.env.example
|
|
7
|
+
.cache/
|
|
8
|
+
.ruff_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.pyre/
|
|
12
|
+
.pyright/
|
|
13
|
+
.venv/
|
|
14
|
+
build/
|
|
15
|
+
dist/
|
|
16
|
+
*.egg-info/
|
|
17
|
+
htmlcov/
|
|
18
|
+
.coverage
|
|
19
|
+
.coverage.*
|
|
20
|
+
.hypothesis/
|
|
21
|
+
.nox/
|
|
22
|
+
.tox/
|
|
23
|
+
.docs/
|
|
24
|
+
**/__pycache__/
|
|
25
|
+
*.py[cod]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 danielle rothermel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: whetstone-envs
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Task-family-agnostic harness for reproducible quick-test environments.
|
|
5
|
+
Project-URL: Repository, https://github.com/danielle-rothermel/whetstone-envs
|
|
6
|
+
Project-URL: Issues, https://github.com/danielle-rothermel/whetstone-envs/issues
|
|
7
|
+
Project-URL: Documentation, https://danielle-rothermel.github.io/whetstone-envs/
|
|
8
|
+
Project-URL: Changelog, https://github.com/danielle-rothermel/whetstone-envs/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Danielle Rothermel <danielle.rothermel@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: dr-graph==0.1.2
|
|
22
|
+
Requires-Dist: dr-serialize==0.1.2
|
|
23
|
+
Requires-Dist: dr-store==0.1.4
|
|
24
|
+
Requires-Dist: pydantic==2.13.4
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# whetstone-envs
|
|
28
|
+
|
|
29
|
+
[](https://github.com/danielle-rothermel/whetstone-envs/actions/workflows/ci.yml)
|
|
30
|
+
|
|
31
|
+
Task-family-agnostic contracts for reproducible quick-test environments.
|
|
32
|
+
|
|
33
|
+
## Scope
|
|
34
|
+
|
|
35
|
+
This repo owns the environment data and evaluation rules shared by Whetstone's
|
|
36
|
+
quick-test task families, with no dependency on optimizer or execution-contract
|
|
37
|
+
code:
|
|
38
|
+
|
|
39
|
+
- [**Instances**][instances-source] define immutable task inputs,
|
|
40
|
+
private gold data, generation seeds, task strata, and public prompt identity.
|
|
41
|
+
- [**Pools and splits**][pools-source] validate ordered instance
|
|
42
|
+
collections and allocate deterministic internal, official, and held-out
|
|
43
|
+
cohorts.
|
|
44
|
+
- [**Probes**][probes-source] pair naive and ceiling templates,
|
|
45
|
+
render public prompt inputs, and normalize predictions for evaluation.
|
|
46
|
+
- [**Scoring**][scoring-source] represents scored, failed, and
|
|
47
|
+
missing observations and aggregates complete repeat matrices through task,
|
|
48
|
+
stratum, and overall levels.
|
|
49
|
+
- [**Manifests**][manifests-source] pin generated pools with
|
|
50
|
+
versioned identities and bounded canonical persistence.
|
|
51
|
+
|
|
52
|
+
The task-family implementations and the adapter to Whetstone's optimizer live
|
|
53
|
+
above this shared harness rather than inside its contracts.
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
uv add whetstone-envs
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Instances
|
|
62
|
+
|
|
63
|
+
[`whetstone_envs.instances`][instances-source] owns the immutable
|
|
64
|
+
unit passed through generation, prompting, scoring, splitting, and persistence.
|
|
65
|
+
Prompt inputs are public; `gold` remains private evaluation data.
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
@dataclass(frozen=True, slots=True)
|
|
69
|
+
class Instance:
|
|
70
|
+
id: str
|
|
71
|
+
seed: int
|
|
72
|
+
strata: tuple[str, ...]
|
|
73
|
+
prompt_inputs: Mapping[str, str] = field(default_factory=lambda: ...)
|
|
74
|
+
gold: str = ""
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
def make_instance(
|
|
79
|
+
*,
|
|
80
|
+
id: str,
|
|
81
|
+
seed: int,
|
|
82
|
+
strata: tuple[str, ...] | str,
|
|
83
|
+
prompt_inputs: Mapping[str, str] | None = None,
|
|
84
|
+
gold: str = "",
|
|
85
|
+
) -> Instance: ...
|
|
86
|
+
|
|
87
|
+
def public_prompt_identity(
|
|
88
|
+
instance: Instance,
|
|
89
|
+
) -> tuple[tuple[str, str], ...]: ...
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Pools and splits
|
|
93
|
+
|
|
94
|
+
[`whetstone_envs.pools`][pools-source] owns validated ordered pools
|
|
95
|
+
and the deterministic policy for selecting three disjoint evaluation cohorts.
|
|
96
|
+
Split optimization is delegated to `dr-graph`; returned instances preserve pool
|
|
97
|
+
order.
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
@dataclass(frozen=True, slots=True)
|
|
101
|
+
class PoolSplit:
|
|
102
|
+
internal_eval: tuple[Instance, ...]
|
|
103
|
+
official: tuple[Instance, ...]
|
|
104
|
+
held_out: tuple[Instance, ...]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
@dataclass(frozen=True, slots=True)
|
|
109
|
+
class TaskPool:
|
|
110
|
+
instances: tuple[Instance, ...]
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def strata(self) -> tuple[str, ...]: ...
|
|
114
|
+
|
|
115
|
+
def stratum_counts(self) -> dict[str, int]: ...
|
|
116
|
+
def in_stratum(self, label: str) -> tuple[Instance, ...]: ...
|
|
117
|
+
def split(
|
|
118
|
+
self,
|
|
119
|
+
internal_eval_n: int,
|
|
120
|
+
official_n: int,
|
|
121
|
+
held_out_n: int,
|
|
122
|
+
) -> PoolSplit: ...
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Probes
|
|
126
|
+
|
|
127
|
+
[`whetstone_envs.probes`][probes-source] owns the floor/ceiling
|
|
128
|
+
prompt pair and the default renderer that can see only public prompt inputs.
|
|
129
|
+
Normalization strips whitespace and complete outer triple-backtick fences.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
def render_with_prompt_inputs(template: str, instance: Instance) -> str: ...
|
|
133
|
+
def normalize(prediction: str) -> str: ...
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
@dataclass(frozen=True, slots=True)
|
|
138
|
+
class ProbePair:
|
|
139
|
+
naive_template: str
|
|
140
|
+
ceiling_template: str
|
|
141
|
+
render: Callable[[str, Instance], str] = render_with_prompt_inputs
|
|
142
|
+
|
|
143
|
+
def render_naive(self, instance: Instance) -> str: ...
|
|
144
|
+
def render_ceiling(self, instance: Instance) -> str: ...
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Scoring
|
|
148
|
+
|
|
149
|
+
[`whetstone_envs.scoring`][scoring-source] keeps failures and absent
|
|
150
|
+
results distinct from binary scores. Aggregation exposes a mean only when the
|
|
151
|
+
complete planned task/repeat matrix is present and scored.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
@verify(UNIQUE)
|
|
155
|
+
class Outcome(StrEnum):
|
|
156
|
+
SCORED = "scored"
|
|
157
|
+
FAILED = "failed"
|
|
158
|
+
MISSING = "missing"
|
|
159
|
+
|
|
160
|
+
@dataclass(frozen=True, slots=True)
|
|
161
|
+
class Observation:
|
|
162
|
+
task_id: str
|
|
163
|
+
repeat_id: int
|
|
164
|
+
outcome: Outcome = Outcome.SCORED
|
|
165
|
+
score: int | None = None
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
@dataclass(frozen=True, slots=True)
|
|
170
|
+
class Aggregate:
|
|
171
|
+
mean: float | None
|
|
172
|
+
usable: int
|
|
173
|
+
failed_count: int
|
|
174
|
+
missing_count: int
|
|
175
|
+
label: str | None = None
|
|
176
|
+
children: tuple["Aggregate", ...] = field(default_factory=tuple)
|
|
177
|
+
|
|
178
|
+
def aggregate(
|
|
179
|
+
observations: Iterable[Observation],
|
|
180
|
+
task_strata: Mapping[str, tuple[str, ...]],
|
|
181
|
+
*,
|
|
182
|
+
expected_repeat_ids: Iterable[int],
|
|
183
|
+
) -> Aggregate: ...
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`exact_match`, `scored`, `failed`, and `missing` provide the primary leaf-level
|
|
187
|
+
constructors. `aggregate_task`, `aggregate_stratum`, and `aggregate_overall`
|
|
188
|
+
expose the individual aggregation steps when callers already own the hierarchy.
|
|
189
|
+
|
|
190
|
+
## Manifests
|
|
191
|
+
|
|
192
|
+
[`whetstone_envs.manifests`][manifests-source] owns the serialized
|
|
193
|
+
boundary for regenerated pool identity. Manifests use a closed Pydantic schema,
|
|
194
|
+
`dr-serialize` identities, and `dr-store` canonical files.
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
class Manifest(BaseModel):
|
|
198
|
+
generator_version: str
|
|
199
|
+
seed_range: tuple[int, int]
|
|
200
|
+
stratum_counts: Mapping[str, int]
|
|
201
|
+
content_hash: Sha256Digest
|
|
202
|
+
schema_version: int = MANIFEST_SCHEMA_VERSION
|
|
203
|
+
|
|
204
|
+
@classmethod
|
|
205
|
+
def from_pool(
|
|
206
|
+
cls,
|
|
207
|
+
pool: TaskPool,
|
|
208
|
+
*,
|
|
209
|
+
generator_version: str,
|
|
210
|
+
seed_range: tuple[int, int],
|
|
211
|
+
) -> "Manifest": ...
|
|
212
|
+
|
|
213
|
+
def write(self, path: Path) -> None: ...
|
|
214
|
+
@classmethod
|
|
215
|
+
def read(cls, path: Path) -> "Manifest": ...
|
|
216
|
+
def matches_pool(self, pool: TaskPool) -> bool: ...
|
|
217
|
+
|
|
218
|
+
def content_hash(pool: TaskPool) -> Sha256Digest: ...
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Terms and contracts
|
|
222
|
+
|
|
223
|
+
The [published terms and contracts](https://danielle-rothermel.github.io/whetstone-envs/)
|
|
224
|
+
render the authoritative
|
|
225
|
+
[vocabulary](https://github.com/danielle-rothermel/whetstone-envs/blob/main/.defs/terms.toml)
|
|
226
|
+
and
|
|
227
|
+
[binding contracts](https://github.com/danielle-rothermel/whetstone-envs/blob/main/.defs/contracts.toml)
|
|
228
|
+
directly from their TOML sources. The
|
|
229
|
+
[changelog](https://github.com/danielle-rothermel/whetstone-envs/blob/main/CHANGELOG.md)
|
|
230
|
+
records notable changes.
|
|
231
|
+
|
|
232
|
+
## Development
|
|
233
|
+
|
|
234
|
+
Install the locked development environment and commit hook once per clone:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
uv sync --locked
|
|
238
|
+
uv run pre-commit install
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The hook runs the same formatting, lint, type, definitions, test, and package
|
|
242
|
+
build gate used by CI. Run it directly at any time:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
scripts/pre-check.sh
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
[instances-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/instances
|
|
249
|
+
[manifests-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/manifests
|
|
250
|
+
[pools-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/pools
|
|
251
|
+
[probes-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/probes
|
|
252
|
+
[scoring-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/scoring
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# whetstone-envs
|
|
2
|
+
|
|
3
|
+
[](https://github.com/danielle-rothermel/whetstone-envs/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
Task-family-agnostic contracts for reproducible quick-test environments.
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
This repo owns the environment data and evaluation rules shared by Whetstone's
|
|
10
|
+
quick-test task families, with no dependency on optimizer or execution-contract
|
|
11
|
+
code:
|
|
12
|
+
|
|
13
|
+
- [**Instances**][instances-source] define immutable task inputs,
|
|
14
|
+
private gold data, generation seeds, task strata, and public prompt identity.
|
|
15
|
+
- [**Pools and splits**][pools-source] validate ordered instance
|
|
16
|
+
collections and allocate deterministic internal, official, and held-out
|
|
17
|
+
cohorts.
|
|
18
|
+
- [**Probes**][probes-source] pair naive and ceiling templates,
|
|
19
|
+
render public prompt inputs, and normalize predictions for evaluation.
|
|
20
|
+
- [**Scoring**][scoring-source] represents scored, failed, and
|
|
21
|
+
missing observations and aggregates complete repeat matrices through task,
|
|
22
|
+
stratum, and overall levels.
|
|
23
|
+
- [**Manifests**][manifests-source] pin generated pools with
|
|
24
|
+
versioned identities and bounded canonical persistence.
|
|
25
|
+
|
|
26
|
+
The task-family implementations and the adapter to Whetstone's optimizer live
|
|
27
|
+
above this shared harness rather than inside its contracts.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv add whetstone-envs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Instances
|
|
36
|
+
|
|
37
|
+
[`whetstone_envs.instances`][instances-source] owns the immutable
|
|
38
|
+
unit passed through generation, prompting, scoring, splitting, and persistence.
|
|
39
|
+
Prompt inputs are public; `gold` remains private evaluation data.
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
@dataclass(frozen=True, slots=True)
|
|
43
|
+
class Instance:
|
|
44
|
+
id: str
|
|
45
|
+
seed: int
|
|
46
|
+
strata: tuple[str, ...]
|
|
47
|
+
prompt_inputs: Mapping[str, str] = field(default_factory=lambda: ...)
|
|
48
|
+
gold: str = ""
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
def make_instance(
|
|
53
|
+
*,
|
|
54
|
+
id: str,
|
|
55
|
+
seed: int,
|
|
56
|
+
strata: tuple[str, ...] | str,
|
|
57
|
+
prompt_inputs: Mapping[str, str] | None = None,
|
|
58
|
+
gold: str = "",
|
|
59
|
+
) -> Instance: ...
|
|
60
|
+
|
|
61
|
+
def public_prompt_identity(
|
|
62
|
+
instance: Instance,
|
|
63
|
+
) -> tuple[tuple[str, str], ...]: ...
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Pools and splits
|
|
67
|
+
|
|
68
|
+
[`whetstone_envs.pools`][pools-source] owns validated ordered pools
|
|
69
|
+
and the deterministic policy for selecting three disjoint evaluation cohorts.
|
|
70
|
+
Split optimization is delegated to `dr-graph`; returned instances preserve pool
|
|
71
|
+
order.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
@dataclass(frozen=True, slots=True)
|
|
75
|
+
class PoolSplit:
|
|
76
|
+
internal_eval: tuple[Instance, ...]
|
|
77
|
+
official: tuple[Instance, ...]
|
|
78
|
+
held_out: tuple[Instance, ...]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
@dataclass(frozen=True, slots=True)
|
|
83
|
+
class TaskPool:
|
|
84
|
+
instances: tuple[Instance, ...]
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def strata(self) -> tuple[str, ...]: ...
|
|
88
|
+
|
|
89
|
+
def stratum_counts(self) -> dict[str, int]: ...
|
|
90
|
+
def in_stratum(self, label: str) -> tuple[Instance, ...]: ...
|
|
91
|
+
def split(
|
|
92
|
+
self,
|
|
93
|
+
internal_eval_n: int,
|
|
94
|
+
official_n: int,
|
|
95
|
+
held_out_n: int,
|
|
96
|
+
) -> PoolSplit: ...
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Probes
|
|
100
|
+
|
|
101
|
+
[`whetstone_envs.probes`][probes-source] owns the floor/ceiling
|
|
102
|
+
prompt pair and the default renderer that can see only public prompt inputs.
|
|
103
|
+
Normalization strips whitespace and complete outer triple-backtick fences.
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
def render_with_prompt_inputs(template: str, instance: Instance) -> str: ...
|
|
107
|
+
def normalize(prediction: str) -> str: ...
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
@dataclass(frozen=True, slots=True)
|
|
112
|
+
class ProbePair:
|
|
113
|
+
naive_template: str
|
|
114
|
+
ceiling_template: str
|
|
115
|
+
render: Callable[[str, Instance], str] = render_with_prompt_inputs
|
|
116
|
+
|
|
117
|
+
def render_naive(self, instance: Instance) -> str: ...
|
|
118
|
+
def render_ceiling(self, instance: Instance) -> str: ...
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Scoring
|
|
122
|
+
|
|
123
|
+
[`whetstone_envs.scoring`][scoring-source] keeps failures and absent
|
|
124
|
+
results distinct from binary scores. Aggregation exposes a mean only when the
|
|
125
|
+
complete planned task/repeat matrix is present and scored.
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
@verify(UNIQUE)
|
|
129
|
+
class Outcome(StrEnum):
|
|
130
|
+
SCORED = "scored"
|
|
131
|
+
FAILED = "failed"
|
|
132
|
+
MISSING = "missing"
|
|
133
|
+
|
|
134
|
+
@dataclass(frozen=True, slots=True)
|
|
135
|
+
class Observation:
|
|
136
|
+
task_id: str
|
|
137
|
+
repeat_id: int
|
|
138
|
+
outcome: Outcome = Outcome.SCORED
|
|
139
|
+
score: int | None = None
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
@dataclass(frozen=True, slots=True)
|
|
144
|
+
class Aggregate:
|
|
145
|
+
mean: float | None
|
|
146
|
+
usable: int
|
|
147
|
+
failed_count: int
|
|
148
|
+
missing_count: int
|
|
149
|
+
label: str | None = None
|
|
150
|
+
children: tuple["Aggregate", ...] = field(default_factory=tuple)
|
|
151
|
+
|
|
152
|
+
def aggregate(
|
|
153
|
+
observations: Iterable[Observation],
|
|
154
|
+
task_strata: Mapping[str, tuple[str, ...]],
|
|
155
|
+
*,
|
|
156
|
+
expected_repeat_ids: Iterable[int],
|
|
157
|
+
) -> Aggregate: ...
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`exact_match`, `scored`, `failed`, and `missing` provide the primary leaf-level
|
|
161
|
+
constructors. `aggregate_task`, `aggregate_stratum`, and `aggregate_overall`
|
|
162
|
+
expose the individual aggregation steps when callers already own the hierarchy.
|
|
163
|
+
|
|
164
|
+
## Manifests
|
|
165
|
+
|
|
166
|
+
[`whetstone_envs.manifests`][manifests-source] owns the serialized
|
|
167
|
+
boundary for regenerated pool identity. Manifests use a closed Pydantic schema,
|
|
168
|
+
`dr-serialize` identities, and `dr-store` canonical files.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
class Manifest(BaseModel):
|
|
172
|
+
generator_version: str
|
|
173
|
+
seed_range: tuple[int, int]
|
|
174
|
+
stratum_counts: Mapping[str, int]
|
|
175
|
+
content_hash: Sha256Digest
|
|
176
|
+
schema_version: int = MANIFEST_SCHEMA_VERSION
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def from_pool(
|
|
180
|
+
cls,
|
|
181
|
+
pool: TaskPool,
|
|
182
|
+
*,
|
|
183
|
+
generator_version: str,
|
|
184
|
+
seed_range: tuple[int, int],
|
|
185
|
+
) -> "Manifest": ...
|
|
186
|
+
|
|
187
|
+
def write(self, path: Path) -> None: ...
|
|
188
|
+
@classmethod
|
|
189
|
+
def read(cls, path: Path) -> "Manifest": ...
|
|
190
|
+
def matches_pool(self, pool: TaskPool) -> bool: ...
|
|
191
|
+
|
|
192
|
+
def content_hash(pool: TaskPool) -> Sha256Digest: ...
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Terms and contracts
|
|
196
|
+
|
|
197
|
+
The [published terms and contracts](https://danielle-rothermel.github.io/whetstone-envs/)
|
|
198
|
+
render the authoritative
|
|
199
|
+
[vocabulary](https://github.com/danielle-rothermel/whetstone-envs/blob/main/.defs/terms.toml)
|
|
200
|
+
and
|
|
201
|
+
[binding contracts](https://github.com/danielle-rothermel/whetstone-envs/blob/main/.defs/contracts.toml)
|
|
202
|
+
directly from their TOML sources. The
|
|
203
|
+
[changelog](https://github.com/danielle-rothermel/whetstone-envs/blob/main/CHANGELOG.md)
|
|
204
|
+
records notable changes.
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
Install the locked development environment and commit hook once per clone:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
uv sync --locked
|
|
212
|
+
uv run pre-commit install
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The hook runs the same formatting, lint, type, definitions, test, and package
|
|
216
|
+
build gate used by CI. Run it directly at any time:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
scripts/pre-check.sh
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
[instances-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/instances
|
|
223
|
+
[manifests-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/manifests
|
|
224
|
+
[pools-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/pools
|
|
225
|
+
[probes-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/probes
|
|
226
|
+
[scoring-source]: https://github.com/danielle-rothermel/whetstone-envs/tree/main/src/whetstone_envs/scoring
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "whetstone-envs"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "Task-family-agnostic harness for reproducible quick-test environments."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Danielle Rothermel", email = "danielle.rothermel@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Programming Language :: Python :: 3.14",
|
|
19
|
+
"Typing :: Typed",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"dr-graph==0.1.2",
|
|
23
|
+
"dr-serialize==0.1.2",
|
|
24
|
+
"dr-store==0.1.4",
|
|
25
|
+
"pydantic==2.13.4",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Repository = "https://github.com/danielle-rothermel/whetstone-envs"
|
|
30
|
+
Issues = "https://github.com/danielle-rothermel/whetstone-envs/issues"
|
|
31
|
+
Documentation = "https://danielle-rothermel.github.io/whetstone-envs/"
|
|
32
|
+
Changelog = "https://github.com/danielle-rothermel/whetstone-envs/blob/main/CHANGELOG.md"
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["hatchling==1.31.0"]
|
|
36
|
+
build-backend = "hatchling.build"
|
|
37
|
+
|
|
38
|
+
[dependency-groups]
|
|
39
|
+
dev = [
|
|
40
|
+
"actionlint-py==1.7.12.24",
|
|
41
|
+
"pre-commit>=4.6.0",
|
|
42
|
+
"pytest>=9.1.1",
|
|
43
|
+
"ruff>=0.15.18",
|
|
44
|
+
"ty>=0.0.51",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[tool.uv]
|
|
48
|
+
required-version = "==0.11.25"
|
|
49
|
+
|
|
50
|
+
[tool.hatch.build.targets.sdist]
|
|
51
|
+
only-include = ["src"]
|
|
52
|
+
|
|
53
|
+
[tool.pytest.ini_options]
|
|
54
|
+
testpaths = ["tests"]
|
|
55
|
+
|
|
56
|
+
[tool.pyright]
|
|
57
|
+
venvPath = "."
|
|
58
|
+
venv = ".venv"
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
include = ["src/**/*.py", "tests/**/*.py"]
|
|
62
|
+
line-length = 79
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = [
|
|
66
|
+
"A",
|
|
67
|
+
"ARG",
|
|
68
|
+
"ASYNC",
|
|
69
|
+
"B",
|
|
70
|
+
"BLE",
|
|
71
|
+
"C4",
|
|
72
|
+
"DTZ",
|
|
73
|
+
"E",
|
|
74
|
+
"F",
|
|
75
|
+
"FA",
|
|
76
|
+
"FBT",
|
|
77
|
+
"FLY",
|
|
78
|
+
"FURB",
|
|
79
|
+
"G",
|
|
80
|
+
"I",
|
|
81
|
+
"ICN",
|
|
82
|
+
"ISC",
|
|
83
|
+
"LOG",
|
|
84
|
+
"N",
|
|
85
|
+
"NPY",
|
|
86
|
+
"PD",
|
|
87
|
+
"PERF",
|
|
88
|
+
"PIE",
|
|
89
|
+
"PL",
|
|
90
|
+
"PTH",
|
|
91
|
+
"PT",
|
|
92
|
+
"RET",
|
|
93
|
+
"RSE",
|
|
94
|
+
"RUF",
|
|
95
|
+
"S",
|
|
96
|
+
"SIM",
|
|
97
|
+
"SLOT",
|
|
98
|
+
"T10",
|
|
99
|
+
"TC",
|
|
100
|
+
"TID",
|
|
101
|
+
"TRY",
|
|
102
|
+
"UP",
|
|
103
|
+
"W",
|
|
104
|
+
"YTT",
|
|
105
|
+
]
|
|
106
|
+
ignore = ["PLR1711", "S101", "TRY003"]
|
|
107
|
+
|
|
108
|
+
[tool.ruff.lint.per-file-ignores]
|
|
109
|
+
"tests/**/*.py" = ["PLC0415", "PLR2004", "PT011"]
|
|
110
|
+
|
|
111
|
+
[tool.ruff.lint.flake8-type-checking]
|
|
112
|
+
runtime-evaluated-decorators = ["dataclasses.dataclass"]
|
|
113
|
+
|
|
114
|
+
[tool.ty.src]
|
|
115
|
+
include = ["src", "tests"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__all__: list[str] = []
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from whetstone_envs.instances.instance import Instance
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def public_prompt_identity(instance: Instance) -> tuple[tuple[str, str], ...]:
|
|
5
|
+
"""Return the canonical identity of sorted ``prompt_inputs``.
|
|
6
|
+
|
|
7
|
+
This identity does not necessarily identify rendered text.
|
|
8
|
+
"""
|
|
9
|
+
return tuple(sorted(instance.prompt_inputs.items()))
|