Evalstone
Reference

evalstone.yaml

Every key in the config file.

project: support-bot
imports: [evals.checks]              # modules whose @check registrations to load
report: evalstone-report.json        # default report path (relative to this file)

datasets:
  golden: evals/golden.jsonl         # JSONL: {"id"?, "input", "expected"?, "tags"?, "metadata"?}

checks:                              # keyed definitions: {type: <check type>, ...kwargs}
  must_include: { type: contains }                          # expected.must_include
  schema: { type: json_schema, schema: { type: object } }
  short: { type: length, max_words: 120 }
  helpful: { type: judge, rubric: "...", scale: 5 }
  faithful: { type: rag.faithfulness }
  reviewed: { type: human, labels: evals/labels.jsonl }   # or labels: center (the default when attached)

suites:
  nightly:
    dataset: golden
    target: { kind: callable, fn: "myapp.bot:answer" }
    checks: [must_include, short, helpful, no_apology]      # keys above or @check names
    gate: { min_pass_rate: 0.95, max_regression: 0.02, max_cost_usd: 2, max_error_rate: 0 }
    short_circuit: true              # skip the judges on a sample a contract check already failed

judges:
  default_model: anthropic/claude-haiku-5-5

providers:
  anthropic:  { api_key_env: ANTHROPIC_API_KEY }
  openai:     { api_key_env: OPENAI_API_KEY }
  openrouter: { adapter: openai, base_url: https://openrouter.ai/api/v1, api_key_env: OPENROUTER_API_KEY }
  ollama:     { adapter: openai, base_url: http://localhost:11434/v1 }
  gemini:     { api_key_env: GEMINI_API_KEY }

prices:                              # USD per 1M tokens (input, output); overrides the built-in table
  my-model: [1.0, 2.0]

center:                              # optional: attach the project to an Evalstone center
  url: https://api.evalstone.com
  project: 035d2060-80b1-4a90-9685-29a793df0f65
  api_key_env: EVALSTONE_API_KEY     # an es_… ingest key from the project's settings

providers.<name> takes adapter (openai, anthropic or gemini; defaults to the entry's name), base_url, api_key_env, api_key, headers, timeout (and max_tokens_param for OpenAI-compatible servers). A judge model is <name>/<model>, so openrouter/anthropic/claude-haiku-5-5 and ollama/llama3.1 can sit next to openai/… and anthropic/…; evalstone run --judge-model <name>/<model> overrides every judge (never the target under test) for one run. A named entry needs its own api_key_env or api_key: only an entry named after its adapter (openai:, anthropic:, gemini:) falls back to that adapter's default variable, so one vendor's key is never sent to another (Ollama needs none). The openai adapter speaks to any OpenAI-compatible server, so OpenRouter, Ollama, vLLM or Groq are a base_url away. Keys come from the environment variable named by api_key_env (or the provider's default one); api_key sets one inline, which is for programmatic use — a key in a committed YAML is a committed key. Only models in the built-in price table (Claude models today) or under prices: count toward max_cost_usd; others are reported unpriced, and a gate with max_cost_usd prints a WARN … unpriced model counted as $0 line next to its verdict rather than guessing a price. The full picture: How it works.

Center

center: attaches the project to an Evalstone center (ours at https://api.evalstone.com, or a self-hosted one). Attached, judges run through the center's model proxy with the organization's keys, verdicts are cached there, human checks look labels up there and evalstone run pushes its report. Standalone (no block) nothing leaves the machine.

KeyDefaultMeaning
urlrequiredthe center's API base URL
projectrequiredthe project id (uuid): the p/<id> segment of the project's URL in the web app
api_key_envEVALSTONE_API_KEYthe environment variable holding an es_… ingest key (minted on the project's Settings page by an admin; a user token works too); a missing value is exit 2
timeout120seconds a judge call may wait on the provider behind the proxy
cache_timeout10seconds for cache and label lookups; a slow center is a cache miss, never a failed run
push_runstrueevalstone run / gate push the report; a failed push warns, never fails
cachetrueread and write judge verdicts in the center; false keeps an in-process cache

What changes when attached. judges: is ignored and providers: no longer serves the judges: the judge model comes from the project's judge settings in the center, and no judge key is needed on the machine running the suite. The system under test does not: a prompt target still uses the local providers: entry and its environment key, and callable/http targets are untouched. human checks default to labels: center (write labels: center explicitly to say so; unattached the default is labels.jsonl next to the config, and a file path still works attached). The pytest plugin and the MCP server use the center's judges and cache but never push runs.

Which model a judge uses, first match wins:

  1. evalstone run --judge-model <provider>/<model> (the whole run)
  2. the center project's per-check override (Judges page, keyed by the check's key)
  3. the check's own model:
  4. the center project's default judge model when attached, else judges.default_model

Standalone, step 2 does not exist and an unknown provider in --judge-model is exit 2; attached, the proxy only rejects a bad name per call (see CLI).

Targets

kindKeysProduces
callablefn: "module:function" (imported from the config's directory), whole_sample: falsefn(sample.input) or fn(sample); may return a value or an Output
httpurl, method: POST, headers, body (template; "{input}" leaf = raw input, embedded = text), response_path (dotted, digits index lists), timeoutthe JSON response (or the path into it)
recordedpath: outputs.jsonl ({"id", "output"} per line) or outputs: {id: output}offline outputs; missing id = error
promptmodel (required — never the judge's by default), template ({input} and input keys), system, max_tokensthe model's text; cost and tokens recorded

Gate

KeyMeaning
min_pass_ratefraction of results that must pass
min_mean_scoremean 0..1 score across results
max_regressionallowed drop in pass rate vs --baseline (warns when the dataset version differs)
require_baselinedefault false. With max_regression: fail when there is no baseline, or it is from another suite, scored nothing, exceeded max_error_rate, or ran on a different dataset version or check set (default: skip or warn)
max_cost_usdtotal run cost
max_error_ratedefault 0: a judge outage fails the gate

A run with zero results never passes.

Unknown gate keys are a configuration error, so a misspelt threshold fails loudly.

Model strings

<provider>/<model>: openai/… (any OpenAI-compatible base URL — OpenAI, OpenRouter, Ollama, vLLM; max_tokens_param forces the token parameter name), anthropic/… (official SDK, pip install "evalstone[anthropic]"), gemini/…. Keys are read from the environment and never stored. Unknown models are reported as unpriced, never guessed.

On this page