Sufficiency scoring
How an answer is judged, and how under-allocation is counted.
An answer is sufficient when it does the job the request asked for.
The evaluator chain
Evaluators compose. An answer counts as sufficient when no evaluator in the
chain returns sufficient: false.
interface Evaluator {
readonly name: string
evaluate(context: EvaluationContext): Promise<EvaluationResult>
}
interface EvaluationResult {
sufficient: boolean
score: number // 0..1
reason: string
evaluator: string
}
Deterministic checks
The default, and always present. An answer fails immediately if it is empty or
was cut off at the ceiling; otherwise it is scored against the suite's
assertions, and score is the share that passed.
Truncation is an automatic failure regardless of the other checks. An answer that contains the right keyword and then stops mid-sentence still counts as a failure.
LLM judge
Optional, opt-in with --judge, and only for prompts that carry a judge
rubric.
{
"id": "explain-index-writes",
"prompt": "Why can adding an index make writes slower?",
"judge": "Does it explain that indexes must be maintained on every write?",
}
A judge that cannot be reached returns sufficient: true with a reason naming
the failure, so an outage on the judge does not report a regression that did
not happen.
This means an unreachable judge does not veto an answer, and the prompt is
scored on its deterministic checks alone. Those prompts still count towards
Sufficiency, so a run where the judge failed repeatedly reports a higher rate
than a run where it answered. Check the reason field for
judge unavailable before comparing two runs.
Your own
// evaluators/checkout.mjs
export function createEvaluator() {
return {
name: 'checkout-policy',
async evaluate({ answer, prompt }) {
const mentionsPolicy = /refund|return|30 days/i.test(answer)
return {
sufficient: mentionsPolicy,
score: mentionsPolicy ? 1 : 0,
reason: mentionsPolicy ? 'policy referenced' : 'no policy reference',
evaluator: 'checkout-policy',
}
},
}
}
meted eval ./evals.json --evaluator ./evaluators/checkout.mjs
Export createEvaluator, a default export of either shape, or an object with an
evaluate method.
The two headline rates
Sufficiency is the share of metered answers judged sufficient.
Under-allocation is the share of prompts matching either of two conditions:
underAllocated =
metedFinishReason === "length"
|| (!metedSufficient && naturalSufficient)
The first condition counts every metered answer that stopped at the ceiling, whatever the unconstrained run did.
The second condition counts a metered answer that failed the checks only when the unconstrained answer passed them. Where both failed, the prompt or the model is at fault rather than the ceiling.
The two rates do not have to sum to 100%. An answer can be insufficient for a reason that has nothing to do with allocation.
In production
meted eval compares two runs of a fixed suite. In production there is no
second run to compare against, so truncation is the available signal:
ID TASK ACTUAL TARGET DIFF TOTAL ALLOCATION
7daa fact 64 18 -256% 410ms applied 64 truncated
A response that stopped at the ceiling carries finish_reason: "length", and
the meted dev log flags it truncated.
x-meted-applied-max-tokens reports the limit that was sent upstream. It does
not indicate whether the response reached that limit. To detect truncation from
a response, read finish_reason from the body, or choices[].finish_reason
from the final streamed chunk.
To track the rate over time, count finish_reason: "length" where your
application handles responses, or from the
onRecord summary.