evalcore 2.4.0__tar.gz → 2.4.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.
- {evalcore-2.4.0 → evalcore-2.4.1}/CHANGELOG.md +32 -1
- {evalcore-2.4.0 → evalcore-2.4.1}/PKG-INFO +1 -1
- {evalcore-2.4.0 → evalcore-2.4.1}/pyproject.toml +1 -1
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/graders/judge.py +18 -4
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_judge.py +33 -2
- {evalcore-2.4.0 → evalcore-2.4.1}/uv.lock +1 -1
- {evalcore-2.4.0 → evalcore-2.4.1}/.github/workflows/ci.yml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/.github/workflows/publish.yml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/.gitignore +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/.pre-commit-config.yaml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/LICENSE +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/README.md +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/docs/design.md +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/__init__.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/README.md +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/__init__.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/adapter.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/datasets/support_reply/v1/cases/angry_complaint.yaml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/datasets/support_reply/v1/cases/billing_question.yaml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/datasets/support_reply/v1/cases/double_charge.yaml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/datasets/support_reply/v1/cases/refund_request.yaml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/fixtures/support_reply_judge.yaml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/fixtures/support_reply_pairwise.yaml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/fixtures/support_reply_replay.yaml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/graders.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/run_eval.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/suite.yaml +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/tests/__init__.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/examples/quickstart/tests/test_quickstart.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/justfile +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/pyrightconfig.json +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/__init__.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/adapters/__init__.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/adapters/base.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/adapters/env.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/adapters/http.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/adapters/replay.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/cli.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/compare.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/errors.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/graders/__init__.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/graders/base.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/graders/classification.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/graders/deterministic.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/graders/numeric.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/loader.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/models.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/pairwise.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/py.typed +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/rating.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/refs.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/report.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/reporters/__init__.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/reporters/base.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/reporters/html.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/reporters/markdown.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/retry.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/runner.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/store.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/src/evalcore/sweep.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/__init__.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_adapters.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_cli.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_edge_cases.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_judge_extra.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_live_clients.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_pairwise_extra.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_rating.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_rating_server.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_reporters.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_retry.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_runner.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_store.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_sweep_pairwise.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/tests/test_unit.py +0 -0
- {evalcore-2.4.0 → evalcore-2.4.1}/uv.toml +0 -0
|
@@ -6,6 +6,36 @@ All notable changes to this project are documented here. The format is based on
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.4.1] - 2026-08-09
|
|
10
|
+
|
|
11
|
+
The judge's identity is now in its provenance pin.
|
|
12
|
+
|
|
13
|
+
Shipped as a patch. Both changes below alter a published value, so the 1.0.0
|
|
14
|
+
policy would call this a minor at least; it goes out as 2.4.1 as a deliberate
|
|
15
|
+
exception, because the old pin was answering a provenance question wrongly
|
|
16
|
+
and the sooner it stops the fewer runs are affected.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- `Scorecard.judge_version` includes the judge's model:
|
|
20
|
+
`anthropic:claude-sonnet-4-6@v1` rather than `judge@v1`. It was
|
|
21
|
+
`key@judge_version`, so swapping the judge's model while leaving the
|
|
22
|
+
declared `judge_version` alone produced a byte-identical pin - and any
|
|
23
|
+
provenance check reading that field passed a comparison against a baseline
|
|
24
|
+
scored by a different model. The model is the thing most likely to change
|
|
25
|
+
and the thing a declared version is most likely to miss.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- A single judge's key defaults to its provider (`anthropic`) instead of the
|
|
29
|
+
literal `judge`. A panel already defaulted to `key or provider`, so the
|
|
30
|
+
one-judge case was the odd one out, and `judge` named nothing the `grader`
|
|
31
|
+
column did not already say. It appears in the `judges.name` column.
|
|
32
|
+
|
|
33
|
+
**Upgrading:** every suite with a judge re-baselines once, because
|
|
34
|
+
`judge_version` is part of what identifies a comparable run - which is the
|
|
35
|
+
intended behaviour, just paid all at once. Queries filtering
|
|
36
|
+
`judges.name = 'judge'` need the provider instead. Set `key:` on a `judges:`
|
|
37
|
+
entry to pin a name of your own.
|
|
38
|
+
|
|
9
39
|
## [2.4.0] - 2026-08-09
|
|
10
40
|
|
|
11
41
|
A run without a baseline can now say whether it passed.
|
|
@@ -248,7 +278,8 @@ by semantic versioning: a breaking change to either means a 2.0.
|
|
|
248
278
|
rating + ranking with judge agreement, Markdown/HTML reporters, JSON +
|
|
249
279
|
column-store outbox, and content-hash provenance.
|
|
250
280
|
|
|
251
|
-
[Unreleased]: https://github.com/scottpmiller/evalcore/compare/2.4.
|
|
281
|
+
[Unreleased]: https://github.com/scottpmiller/evalcore/compare/2.4.1...HEAD
|
|
282
|
+
[2.4.1]: https://github.com/scottpmiller/evalcore/compare/2.4.0...2.4.1
|
|
252
283
|
[2.4.0]: https://github.com/scottpmiller/evalcore/compare/2.3.0...2.4.0
|
|
253
284
|
[2.3.0]: https://github.com/scottpmiller/evalcore/compare/2.2.0...2.3.0
|
|
254
285
|
[2.2.0]: https://github.com/scottpmiller/evalcore/compare/2.1.0...2.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evalcore
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: A generic, consumer-agnostic evaluation engine for prompt, model, and API outputs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/scottpmiller/evalcore
|
|
6
6
|
Project-URL: Repository, https://github.com/scottpmiller/evalcore
|
|
@@ -358,7 +358,11 @@ class RubricJudge:
|
|
|
358
358
|
else:
|
|
359
359
|
self.judges = [
|
|
360
360
|
{
|
|
361
|
-
'
|
|
361
|
+
# The provider, not a literal 'judge'. A single judge is
|
|
362
|
+
# a panel of one, so it names itself the same way a panel
|
|
363
|
+
# member does; 'judge' carried no information and made
|
|
364
|
+
# the one-judge case the odd one out.
|
|
365
|
+
'key': 'anthropic',
|
|
362
366
|
'provider': 'anthropic',
|
|
363
367
|
'model': _env_expand(model),
|
|
364
368
|
'api_key_env': None,
|
|
@@ -394,17 +398,27 @@ class RubricJudge:
|
|
|
394
398
|
|
|
395
399
|
@property
|
|
396
400
|
def judge_version(self) -> str:
|
|
397
|
-
"""Provenance pin: ``key@version`` per configured judge (a
|
|
398
|
-
joins them, comma-separated).
|
|
401
|
+
"""Provenance pin: ``key:model@version`` per configured judge (a
|
|
402
|
+
panel joins them, comma-separated).
|
|
399
403
|
|
|
400
404
|
The runner reads this onto ``Scorecard.judge_version`` so a judge
|
|
401
405
|
model / prompt / scale change surfaces as a re-baseline event rather
|
|
402
406
|
than hiding in each score's ``detail``. Uses the configured judges,
|
|
403
407
|
not the mode-filtered active set, so the pin is stable across
|
|
404
408
|
environments.
|
|
409
|
+
|
|
410
|
+
The model is in the pin because it is the thing most likely to
|
|
411
|
+
change and the thing a declared version is most likely to miss.
|
|
412
|
+
Without it, swapping the judge's model while leaving
|
|
413
|
+
``judge_version`` alone produced a byte-identical pin - so a
|
|
414
|
+
comparison against a baseline scored by a different model passed
|
|
415
|
+
every provenance check that reads this field.
|
|
405
416
|
"""
|
|
406
417
|
return ','.join(
|
|
407
|
-
f'{j["key"]}@{j["judge_version"]}'
|
|
418
|
+
f'{j["key"]}:{j["model"]}@{j["judge_version"]}'
|
|
419
|
+
if j['model']
|
|
420
|
+
else f'{j["key"]}@{j["judge_version"]}'
|
|
421
|
+
for j in self.judges
|
|
408
422
|
)
|
|
409
423
|
|
|
410
424
|
def set_mode(self, mode: str) -> None:
|
|
@@ -125,7 +125,8 @@ class RubricJudgeTests(unittest.TestCase):
|
|
|
125
125
|
replay_path='f.yaml',
|
|
126
126
|
judge_version='v3',
|
|
127
127
|
)
|
|
128
|
-
|
|
128
|
+
# No model configured (replay-only), so the pin is key@version.
|
|
129
|
+
self.assertEqual(single.judge_version, 'anthropic@v3')
|
|
129
130
|
panel = judge.RubricJudge(
|
|
130
131
|
content_ref='output.text',
|
|
131
132
|
dimensions=DIMENSIONS,
|
|
@@ -139,7 +140,37 @@ class RubricJudgeTests(unittest.TestCase):
|
|
|
139
140
|
},
|
|
140
141
|
],
|
|
141
142
|
)
|
|
142
|
-
self.assertEqual(panel.judge_version, 'claude@v2,gpt@v5')
|
|
143
|
+
self.assertEqual(panel.judge_version, 'claude:m@v2,gpt:openai:g@v5')
|
|
144
|
+
|
|
145
|
+
def test_the_model_is_in_the_pin(self):
|
|
146
|
+
"""Swapping the judge's model has to move the pin.
|
|
147
|
+
|
|
148
|
+
Without this, a model swap that left `judge_version` alone produced
|
|
149
|
+
a byte-identical pin, so a comparison against a baseline scored by a
|
|
150
|
+
different model passed every provenance check reading this field.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
def _judge(model):
|
|
154
|
+
return judge.RubricJudge(
|
|
155
|
+
content_ref='output.text',
|
|
156
|
+
dimensions=DIMENSIONS,
|
|
157
|
+
model=model,
|
|
158
|
+
judge_version='v1',
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
before = _judge('claude-sonnet-4-6').judge_version
|
|
162
|
+
after = _judge('claude-opus-4-1').judge_version
|
|
163
|
+
self.assertEqual(before, 'anthropic:claude-sonnet-4-6@v1')
|
|
164
|
+
self.assertNotEqual(before, after)
|
|
165
|
+
|
|
166
|
+
def test_a_single_judge_names_itself_by_provider(self):
|
|
167
|
+
"""Not the literal 'judge', which carried no information."""
|
|
168
|
+
single = judge.RubricJudge(
|
|
169
|
+
content_ref='output.text',
|
|
170
|
+
dimensions=DIMENSIONS,
|
|
171
|
+
replay_path='f.yaml',
|
|
172
|
+
)
|
|
173
|
+
self.assertEqual(single.judges[0]['key'], 'anthropic')
|
|
143
174
|
|
|
144
175
|
def test_transient_client_error_retried_via_set_retry(self):
|
|
145
176
|
class _RateLimit(Exception):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|