The three judge shapes
t.judge.autoevals exposes exactly three fixed forms, whatever you’re evaluating maps onto one of them:
closedQA(question, opts?)— a closed-ended judgment: does the output satisfy this criterion? Best for rubric-style checks (“is the tone polite and specific”, “does this avoid inventing policy details”).factuality(reference, opts?)— factual consistency between the output and a reference text you provide.summarizes(source, opts?)— whether the output is a faithful summary of a source document.
turn.judge defaults to that turn’s material, while root-level t.judge must receive explicit { input, output } strings (see below).
Where judge lives: t and turn
Like scoped assertions, judge calls follow the rule that the receiver decides the default material, not the call site:
turn.judge is turn-level: it only sees the message from that one turn, which is what you want when different turns in a multi-turn eval need different rubrics. Root-level t.judge carries no implicit material, so pass { input, output } explicitly for cross-turn, file, or other custom material:
fileChanged, not by what you pass to the Judge.
Judge model resolution
The judge model is resolved from most- to least-specific:Judge endpoint and key: OpenAI-compatible protocol
Judge calls use an OpenAI-compatible/chat/completions protocol. OpenAI, DeepSeek, and any gateway compatible with that protocol can serve as the judge endpoint. Point at it in the judge config:
Point
judge.baseUrl explicitly at your gateway or OpenAI-compatible proxy — NiceEval does not go looking for an endpoint in variables like OPENAI_BASE_URL. The key works the same way: exactly one name, either the one judge.apiKeyEnv names or the default NICEEVAL_JUDGE_KEY. The judge has its own credential and never borrows the one belonging to the app under test or to an agent.
Severity: judge defaults to soft
Judge calls are assertions, so they follow the same mechanics described in Assert · Gate vs soft severity — but their default is different from most value matchers:.atLeast() and no .gate(), a bare judge call is recorded purely as a quality score — it shows up on the eval’s score chip but can never fail the run on its own. This is the right default for judge: it’s a probabilistic model assessing another probabilistic model’s output, and treating every rubric miss as a hard failure would make suites brittle and constantly red. Reach for .gate() only when the judged property is truly load-bearing (for example, “the response must not fabricate account details”).