Evalstone
Changelog
Every release of the evalstone library, newest first.
Every release of the evalstone library (PyPI), newest
first; semantic versioning, tags lib-vX.Y.Z in the
repository.
Install or upgrade with pip install -U evalstone.
0.2.4 — 2026-10-14
A gate can require its baseline (suggested in feedback on the launch post).
Gate(require_baseline=True)(YAML:gate: { max_regression: 0.02, require_baseline: true }) fails the gate when the regression comparison cannot run: no baseline, a baseline from another suite, one that scored nothing or failed its ownmax_error_rate(recorded during a judge outage), or one from a different dataset version or check set. Without it nothing changes: a missing baseline isSKIP regression: no baseline run, a different dataset version is aWARN, and the gate can still pass. Use it in CI once a baseline report exists, so an expired artifact or a wrong--baselinepath fails the job instead of turning the regression check off.require_baselinewithoutmax_regressionis a configuration error.- A change that edits the dataset or the check list has no comparable baseline, so under
require_baselineit fails until the baseline is refreshed (rerun the suite on the new definition and save that report as the baseline). Checks are compared by key: a check that keeps its key but changes its threshold or judge is not detected. - Gates reject unknown keys: a misspelt threshold (
min_pass_rte: 0.9) is now a configuration error (exit 2) instead of being ignored.
0.2.3 — 2026-10-14
Promote pins the model the verdicts were routed through, found on the demo project's first real calibration.
judgeandcomparison.pairwiserecordraw.judge_model: the<provider>/<model>the verdict was routed through (for exampleopenrouter/anthropic/claude-haiku-5.5), next toraw.model, the id the provider reported (anthropic/claude-haiku-5.5). Verdicts served from the cache carry it too.- The center stamps only
raw.judge_model, so the Judges page, calibrations and Promote use the routed model. Before, it stampedraw.model, and Promote sent that provider-reported id, which judge settings reject (no provider named 'anthropic') or, when a provider of that name existed, route elsewhere. Results pushed by an older library are no longer stamped (a migration clears the old stamps): a version seen only from them shows no model and cannot be promoted until a 0.2.3+ run pushes it.
0.2.2 — 2026-10-14
Judge replies cut off by the token cap, found by the first run against a real vendor (Claude Haiku 5.5 via OpenRouter on the rag-bot demo: 4 of 10 verdicts errored).
judgeandcomparison.pairwisedefaultmax_tokensis now 1024 (was 256). The cut replies needed 286–349 tokens: a verdict plus a long one-sentence reason.max_tokensis not part of the judge version, so cached verdicts stay valid.- A reply the provider stopped at the cap (
length,max_tokens,MAX_TOKENS) whose JSON is unclosed or invalid now errors with "reply truncated at max_tokens=N; raise the check's max_tokens" followed by the original parse error and excerpt instead of only "no JSON object in judge reply". A complete verdict at the cap is accepted, and a complete but wrong one (score out of range) keeps its own error.ChatResponse.truncatedexposes the check. comparison.pairwise: an unparseable judge reply now counts that call's cost (and latency) in the errored score, asjudgealready did.
0.2.1 — 2026-10-14
Attach fixes found by the docs audit.
comparison.pairwisebuilt its check version (and so its cache key) from--judge-modelor the check's ownmodel:, ignoring the attached project's per-check override — the call itself already honoured it. Both now resolve through the same precedence (--judge-model> center override >model:> default), so a verdict cached under the override is found again and a changed override invalidates the cache.- Docstrings and this changelog no longer claim prompt targets are proxied when
attached; they never were (
providers:+ env keys serve the system under test). - Known limitation, documented: when attached,
--judge-model some-typo/modelis not validated up front — the center only rejects the provider at the first judge call, which surfaces as an errored score. Standalone runs still fail fast.
0.2.0 — 2026-10-14
Attach a project to a center.
center:inevalstone.yaml(url,project,api_key_env— anes_…ingest key) routes judge calls through the center's model proxy, so no judge key lives on the machine running the suite; the project's judge settings pick the model (per-check overrides, then the default),--judge-modelstill wins.- Judge verdicts are cached in the center (
CenterCache); a center hiccup is a cache miss, never a failed run. humanchecks of an attached project look labels up in the center (labels: center, the default when attached; a file path still works).evalstone runpushes its report to the center (push_runs: falseto opt out); a failed push warns and leaves the exit code alone.- Prompt targets (the system under test) always use the local
providers:+ env keys, even when attached — the center proxy is for judges only. The MCP server honours the attach too.
0.1.1 — 2026-10-13
Judge switching and cost honesty.
- Named providers: a
providers:entry may name any adapter (adapter: openai | anthropic | gemini), soopenrouter:,ollama:andopenai:can coexist and a judge model readsopenrouter/anthropic/claude-haiku-5-5. Unknown provider names and adapters are reported with the known ones. A named entry never falls back to the adapter's default key variable (one vendor's key is never sent to another); a 401/403 without a key says so. --judge-model provider/modelonevalstone runandgateoverrides every judge's and pairwise comparison's model for that run — never the target under test (Context.judge_model;Config.context(judge_model=…)). Validated before the run starts; a typo exits 2.short_circuit: trueon a suite skips the judge checks on a sample that a contract check (deterministic, safety, trajectory — not a human label, which is a quality verdict the judge should still be compared against) already failed. The skipped result is a failure, not an error (Score.skipped,RunSummary.skipped,CheckSummary.skipped), so no paid call is spent on an output that already failed and the error-rate gate is not tripped. Off by default. Turning it on changes pass rates (a skipped judge counts as a failure), so re-take your baseline when you enable it.- Cheap-first ordering now includes safety rules and human labels, not only deterministic and trajectory checks.
- Gate: with
max_cost_usdset, aWARNline counts results from models outside the price table (they count as $0). Gemini models priced from the official table (2026-10-09); OpenAI models are set underprices:.
0.1.0 — 2026-10-13
The first usable release: the hero path from evalstone.yaml to a gated run.
- Core:
Sample/Dataset(JSONL, content-hashed versions, deterministic filter / sample / split),Output,Score(an error is never a pass), theCheckprotocol,@check, the registry. - Deterministic checks:
exact,contains,regex,json_schema(JSON Schema or pydantic),numeric_tolerance,length,python. - Targets:
callable,http,recorded,prompt. - Suite / Run / Gate: per-check and per-tag summaries, dataset hash + git SHA
provenance, baseline regression gate, error-rate gate (default 0), JSON
report,
compare. - CLI:
init,run,gate,compare,show,list. - Providers: OpenAI-compatible (OpenAI, OpenRouter, Ollama, vLLM),
Anthropic (official SDK,
evalstone[anthropic]), Gemini; pricing table with overrides; unknown models reported as unpriced. - LLM-as-judge: rubric → small scale → JSON verdict, reference excluded unless asked, judge version hash, content-keyed cache.
- RAG (faithfulness, context relevance, answer relevance), safety (refusal, no_pii, forbidden), pairwise comparison (both orders), trajectory (tool_called, tool_order, max_steps, no_repeated_calls, finished).
- Sample:
samples/rag-bot— a no-LLM retrieval bot with a gated suite. - MCP server (
evalstone mcp, extraevalstone[mcp]):list_suites,run_suite,judge_output,record_sample,compare_runs,get_runover stdio; calls run one at a time, stray stdout goes to stderr, one provider registry and judge cache per session, report paths relative to the config. - pytest plugin (entry point
pytest11):@suite("name"), fixturesevalstone_run/evalstone_gate/evalstone_config, options--evalstone-configand--evalstone-baseline(a report or a directory of them); one report per suite (evalstone-report.<suite>.json); a suite without a gate fails the gated forms; the package imports lazily so the plugin costs nothing in unrelated test sessions. - GitHub Action (
action/): run a suite on a pull request, job summary, in-place PR comment (evalstone show --markdown), fail on the gate; inputs bound through the environment;reportoutput is an absolute path. - Human labels:
evalstone label <report>grades outputs in the terminal (pass/fail or a 1..N scale, resumable, keyed by sample id + output hash); thehumancheck scores from the labels file and errors on unlabelled outputs. - Reports:
Run.markdown(),Run.failure_lines(), results carry the sample's input and expected value. - Loading: project modules are reused across runs in one process unless a file changed or the registry was cleared, so test patches reach suites.