ai-evals
A regression gate for prompt and model changes, defineEval() scores a dataset through a grader taxonomy, gateAgainstBaseline() fails the build on a real score drop, all offline and deterministic.
@caisson/ai-evals is an eval harness for AI features: define a dataset of cases, score them with
a grader, and gate a build on a committed baseline instead of a gut feeling. It runs fully offline
and deterministic (no live model call, no secret, no flaky network dependency) so the same suite
produces the same verdict in CI every time.
Install
bun add @caisson/ai-evalsQuickstart
import {
defineEval,
exactGrader,
gateAgainstBaseline,
} from "@caisson/ai-evals";
const run = await defineEval({
name: "greeting-quality",
promptVersionId: version.id,
threshold: 0.95,
cases: [{ id: "case-1", input: { name: "Ada" }, output: "Hello, Ada!" }],
scorers: { "exact-match": exactGrader() },
});
const gate = gateAgainstBaseline("__evals__/baseline.json", [run]);
if (!gate.passed) throw new Error("eval regressed past its committed baseline");Grader taxonomy
Six graders in two classes. exactGrader, regexGrader, jsonShapeGrader, and schemaGrader run
pure and offline, no model call. judgeGrader routes through a Judge port for model-graded
scoring. injectionGrader is its own fail-closed class: its refusal rubric is a hard-coded
deny-list, so a persuasive input can talk a model judge out of a refusal but can never talk a
deny-list out of one.
import { schemaGrader, judgeGrader, cassetteJudge } from "@caisson/ai-evals";
import { z } from "zod";
const shapeCheck = schemaGrader(z.object({ ok: z.boolean() }));
const judged = judgeGrader(cassetteJudge(committedCassette), "matches the house tone");Offline judge via cassette replay
cassetteJudge() replays recorded verdicts from a committed cassette file, zero network call,
zero provider secret, safe to run in CI. An unrecorded case id is a hard cassette-miss error, not a
silent pass. recordingJudge() wraps a real local judge to mint a fresh cassette for review before
you commit it.
The regression gate
gateAgainstBaseline() compares a fresh run against a committed JSON baseline and fails closed, a
missing baseline, a score below threshold, or any individual scorer regression blocks the gate:
export function compareToBaseline(
run: EvalRun,
baseline: BaselineFile,
): BaselineComparison {
const findings: RegressionFinding[] = [];
if (run.score + EPS < run.threshold) {
findings.push({
kind: "below-threshold",
actual: run.score,
baseline: run.threshold,
detail: `score ${run.score} < threshold ${run.threshold}`,
});
}
const prior = baseline.evals[run.name];
if (prior === undefined) {
findings.push({
kind: "missing-baseline",
actual: run.score,
detail: `no committed baseline for eval "${run.name}" — bless to record it`,
});
return { eval: run.name, passed: false, findings, blessed: false };
}
// ...
}BLESS=1 bun run eval is the one sanctioned path to rewrite the baseline, it merges into existing
entries so a partial run never drops other evals, mirroring the golden-fixture discipline in
@caisson/testing.
Confidence and agreement statistics
wilsonLowerBound() threads an opt-in Wilson confidence floor into the baseline gate, so a small
lucky-draw sample can't pass as reliable. fleissKappa, ensembleAgreement, and
counterfactualStability score an eval suite's own reliability, not just its pass rate.
classifyExit() tags why a run exited (error, timeout, budget-exhausted, refusal, empty-output)
as a signal orthogonal to pass/fail.
Reflexivity queue
captureDisagreement() enqueues a case only when a model verdict and a human verdict disagree;
consolidateReflexivityQueue() dedupes and caps the list for operator review. Nothing here
auto-writes a committed dataset, merging a candidate back in stays a human act.
Spend, and how it composes
recordEvalSpend() tracks eval-run cost on its own budget-isolated ledger, it never touches the
production credit wallet or @caisson/ai-meter. ai-evals is a base primitive: the AI Production
bundle's prompt registry and metering compose on top of it to gate quality in the same CI run that
gates spend.
Sold standalone
ai-evals is sold standalone at $199 and is included in the AI-Production
bundle. It pairs with the bundle's metering and guardrails to gate CI on
regression.
ai-meter
PG-atomic reserve/reconcile token metering for LLM calls, per-tenant spend caps, a circuit breaker, and a MinHash dedup gate. Integer credits only, no floats.
guardrails
A fail-closed guard around every model call, PII redaction (mask, hash, or tokenize), a swappable moderator, and an unconditional secret-shape gate.