evalstone 0.2.2__tar.gz → 0.2.3__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.
- {evalstone-0.2.2 → evalstone-0.2.3}/CHANGELOG.md +16 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/PKG-INFO +1 -1
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/__init__.py +1 -1
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/checks/comparison.py +2 -1
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/checks/judge.py +10 -2
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_judge.py +17 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_rag_and_comparison.py +8 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/.gitignore +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/LICENSE +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/README.md +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/justfile +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/pyproject.toml +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/__main__.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/_git.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/_json.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/center.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/checks/__init__.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/checks/_base.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/checks/deterministic.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/checks/human.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/checks/rag.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/checks/safety.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/checks/trajectory.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/cli.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/config.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/dataset.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/gate.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/mcp_server.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/output.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/providers/__init__.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/providers/_http.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/providers/anthropic_.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/providers/gemini.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/providers/openai_compat.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/pytest_plugin.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/run.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/score.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/suite.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/src/evalstone/target.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/conftest.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_action.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_center.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_cli.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_config.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_dataset.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_deterministic.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_docs.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_human.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_judge_switching.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_mcp.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_providers.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_pytest_plugin.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_safety.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_score_and_checks.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_suite_run_gate.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_target.py +0 -0
- {evalstone-0.2.2 → evalstone-0.2.3}/tests/test_trajectory.py +0 -0
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `python/evalstone`. Semantic versioning; tags `lib-vX.Y.Z`.
|
|
4
4
|
|
|
5
|
+
## 0.2.3 — 2026-10-14
|
|
6
|
+
|
|
7
|
+
Promote pins the model the verdicts were routed through, found on the demo project's first
|
|
8
|
+
real calibration.
|
|
9
|
+
|
|
10
|
+
- `judge` and `comparison.pairwise` record `raw.judge_model`: the `<provider>/<model>` the
|
|
11
|
+
verdict was routed through (for example `openrouter/anthropic/claude-haiku-5.5`), next to
|
|
12
|
+
`raw.model`, the id the provider reported (`anthropic/claude-haiku-5.5`). Verdicts served
|
|
13
|
+
from the cache carry it too.
|
|
14
|
+
- The center stamps only `raw.judge_model`, so the Judges page, calibrations and **Promote**
|
|
15
|
+
use the routed model. Before, it stamped `raw.model`, and Promote sent that
|
|
16
|
+
provider-reported id, which judge settings reject (`no provider named 'anthropic'`) or,
|
|
17
|
+
when a provider of that name existed, route elsewhere. Results pushed by an older library
|
|
18
|
+
are no longer stamped (a migration clears the old stamps): a version seen only from them
|
|
19
|
+
shows no model and cannot be promoted until a 0.2.3+ run pushes it.
|
|
20
|
+
|
|
5
21
|
## 0.2.2 — 2026-10-14
|
|
6
22
|
|
|
7
23
|
Judge replies cut off by the token cap, found by the first run against a real
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: evalstone
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: The plug-and-play evals layer for LLM-based projects: datasets, checks, suites, runs and gates.
|
|
5
5
|
Project-URL: Homepage, https://evalstone.com
|
|
6
6
|
Project-URL: Documentation, https://evalstone.com/docs
|
|
@@ -8,7 +8,7 @@ import types
|
|
|
8
8
|
from importlib import import_module
|
|
9
9
|
from typing import TYPE_CHECKING, Any
|
|
10
10
|
|
|
11
|
-
__version__ = "0.2.
|
|
11
|
+
__version__ = "0.2.3"
|
|
12
12
|
|
|
13
13
|
# Exports resolve on first access (PEP 562) so that importing the package — which
|
|
14
14
|
# the pytest plugin does in every pytest session on the interpreter — costs
|
|
@@ -115,7 +115,7 @@ def pairwise(
|
|
|
115
115
|
cached = ctx.cache.get(cache_key) if ctx.cache is not None else None
|
|
116
116
|
if cached is not None:
|
|
117
117
|
# The judge verdict is cached; the baseline was still produced (its cost counts).
|
|
118
|
-
raw = {**(cached.raw or {}), "cached": True}
|
|
118
|
+
raw = {**(cached.raw or {}), "judge_model": resolved, "cached": True}
|
|
119
119
|
return cached.model_copy(
|
|
120
120
|
update={"cost_usd": base.cost_usd, "latency_ms": base.latency_ms, "raw": raw}
|
|
121
121
|
)
|
|
@@ -161,6 +161,7 @@ def pairwise(
|
|
|
161
161
|
"points": points,
|
|
162
162
|
"orders": len(points),
|
|
163
163
|
"judge_version": version,
|
|
164
|
+
"judge_model": resolved,
|
|
164
165
|
"unpriced": bool(unpriced),
|
|
165
166
|
},
|
|
166
167
|
)
|
|
@@ -179,7 +179,15 @@ def judge(
|
|
|
179
179
|
reason=f"{n}/{scale}: {reason}" if reason else f"{n}/{scale}",
|
|
180
180
|
cost_usd=cost,
|
|
181
181
|
latency_ms=ms,
|
|
182
|
-
|
|
182
|
+
# judge_model = `<provider>/<model>` as routed (what Promote pins); set here so
|
|
183
|
+
# a verdict served from an older cache entry carries it too
|
|
184
|
+
raw={
|
|
185
|
+
**raw,
|
|
186
|
+
"score": n,
|
|
187
|
+
"scale": scale,
|
|
188
|
+
"judge_version": judge_version,
|
|
189
|
+
"judge_model": resolved,
|
|
190
|
+
},
|
|
183
191
|
)
|
|
184
192
|
|
|
185
193
|
# The cache holds the model's raw verdict (level + reason), keyed by the
|
|
@@ -218,7 +226,7 @@ def judge(
|
|
|
218
226
|
n,
|
|
219
227
|
reason,
|
|
220
228
|
raw={
|
|
221
|
-
"model": resp.model,
|
|
229
|
+
"model": resp.model, # what the provider reported (a bare id, maybe dated)
|
|
222
230
|
"tokens": [resp.input_tokens, resp.output_tokens],
|
|
223
231
|
"unpriced": resp.cost_usd is None,
|
|
224
232
|
"verdict_reason": reason,
|
|
@@ -288,3 +288,20 @@ def test_judge_version_includes_the_resolved_default_model():
|
|
|
288
288
|
b = chk(S, Output(value="Within 30 days."), ctx_with(prov, default="openai/judge-b"))
|
|
289
289
|
assert a.raw["judge_version"] != b.raw["judge_version"]
|
|
290
290
|
assert len(prov.calls) == 2 # different resolved model → different cache key
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def test_verdict_records_the_routed_model_fresh_and_cached():
|
|
294
|
+
"""`judge_model` is what Promote pins: `<provider>/<model>` as routed, not `resp.model`."""
|
|
295
|
+
from evalstone.score import Score
|
|
296
|
+
|
|
297
|
+
chk = judge("h", "r")
|
|
298
|
+
ctx = ctx_with(FakeProvider())
|
|
299
|
+
fresh = chk(S, Output(value="30 days"), ctx)
|
|
300
|
+
assert fresh.raw["judge_model"] == "openai/judge-model"
|
|
301
|
+
assert chk(S, Output(value="30 days"), ctx).raw["judge_model"] == "openai/judge-model"
|
|
302
|
+
# a cache entry written by an older library (no judge_model) still gets it on the way out
|
|
303
|
+
key, old = next(iter(ctx.cache._data.items()))
|
|
304
|
+
ctx.cache._data[key] = Score.model_validate(
|
|
305
|
+
{**old.model_dump(), "raw": {k: v for k, v in old.raw.items() if k != "judge_model"}}
|
|
306
|
+
)
|
|
307
|
+
assert chk(S, Output(value="30 days"), ctx).raw["judge_model"] == "openai/judge-model"
|
|
@@ -276,3 +276,11 @@ def test_pairwise_unparseable_second_order_keeps_every_call_cost():
|
|
|
276
276
|
score = pairwise("b", "r", baseline={"s": "x"})(S, Output(value="y"), ctx(Half()))
|
|
277
277
|
assert score.passed is False and "no JSON object" in score.error
|
|
278
278
|
assert score.cost_usd == pytest.approx(0.002)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def test_pairwise_records_the_routed_model_fresh_and_cached():
|
|
282
|
+
chk = pairwise("b", "r", baseline={"s": "x"})
|
|
283
|
+
c = ctx(JudgeFake())
|
|
284
|
+
assert chk(S, Output(value="y"), c).raw["judge_model"] == "openai/judge"
|
|
285
|
+
cached = chk(S, Output(value="y"), c)
|
|
286
|
+
assert cached.raw["cached"] is True and cached.raw["judge_model"] == "openai/judge"
|
|
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
|