You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[FEATURE] Add status field to EvaluationOutput for non-gradable results
Problem Statement
When an evaluator cannot produce a meaningful score for a given case, it has no way to say so. The only output shape is EvaluationOutput(score=..., test_pass=..., reason=...), which forces every result into the pass/fail binary. An evaluator that legitimately cannot grade a case (missing input data, inapplicable rubric, harness error) must pick a score anyway, and that score pollutes aggregates.
Take an error-handling evaluator. If the agent's trajectory contains no tool errors, there is nothing to evaluate. Today the evaluator has three bad options:
Return score=1.0, test_pass=True and inflate the pass rate with a hollow pass.
Return score=0.0, test_pass=False and penalize a correct agent for something that never happened.
Encode "could not evaluate" into the reason string and hope downstream consumers parse it out.
Option 3 is what I currently do. It works, but it's fragile and invisible to the framework. The _default_aggregator on Evaluator computes a straight average:
avg_score=sum(o.scoreforoinoutputs) /len(outputs)
There is no way to exclude a result from this sum without the aggregator parsing reason strings. That coupling should not exist.
The same problem shows up at the Experiment level. When building the EvaluationReport, scores are averaged directly:
"graded" (default) means the score and test_pass are real verdicts. Backward compatible with every existing evaluator and consumer.
"could_not_evaluate" means the evaluator tried to grade but could not. Preconditions not met, harness failure, missing data. Aggregators should exclude this from pass-rate and score math.
"informational" means the evaluator does not pass/fail by design. It surfaces content for human review but should never count toward a numeric aggregate.
The field is optional and defaults to "graded", so existing code keeps working with zero changes.
The _default_aggregator in evaluator.py would filter before computing:
The Experiment report assembly in experiment.py would also need to exclude non-graded rows when computing overall_score. Today it does sum(scores) / len(scores). With this feature, it would filter on the status carried through from the aggregated outputs. Concretely, the _run_evaluator method already calls evaluator.aggregator(outputs), and since non-graded outputs would be excluded at the aggregator level, the per-evaluator score would already be correct. The remaining gap is when multiple evaluators are flattened via EvaluationReport.flatten(), which also averages all scores. Adding status to EvaluationOutput (and propagating it into the report's cases dict) would let flatten() skip non-graded rows too.
An evaluator producing a non-gradable result would look like:
An error-handling evaluator that only applies when tool errors exist. No errors means nothing to score. Today it must fake a pass or a fail. With status="could_not_evaluate" the result gets excluded from aggregates and surfaced separately in reports.
A faithfulness evaluator where the agent declined to answer (empty output). There is no claim to ground-check. Instead of scoring 1.0 or 0.0, it returns CNE and the aggregate reflects only cases where faithfulness was actually testable.
A "referenced tickets" evaluator that lists which tickets the agent mentioned but does not pass/fail. It produces status="informational" results that show up in a report for human review without counting toward the suite's pass rate.
Context-window overflow during judging. Issue [BUG] Judge-based evaluators silently score 0 when a trace exceeds the judge's context window #342 describes judge-based evaluators scoring 0 when a trace exceeds the context window. The error-isolation catch block could set status="could_not_evaluate" instead of recording score=0, test_pass=False, giving downstream consumers a clean signal that the 0 is a harness failure, not a quality failure.
Alternatives Considered
A convention on score value (e.g. score=-1.0 means skip). I do this today. It works until someone writes an evaluator that legitimately scores on a scale including negative numbers, or a consumer forgets to filter. A dedicated field is explicit and impossible to misinterpret.
A convention on reason prefix (e.g. "[Could Not Evaluate] ..."). Also what I do today. Brittle, requires string parsing, and invisible to typed consumers. The framework should not require regex on a reason string to compute a correct average.
A separate EvaluationSkip type or exception. More disruptive. Evaluators return list[EvaluationOutput], and that contract is baked into every evaluator subclass and into _run_evaluator's aggregation flow. Adding a union type or exception would require every consumer to handle a new branch. An optional field on the existing model is the smallest possible change.
The field is a plain string rather than an enum to allow future extension without a breaking change. A Literal["graded", "could_not_evaluate", "informational"] type annotation is fine for documentation and tooling while remaining forward-compatible.
[FEATURE] Add
statusfield toEvaluationOutputfor non-gradable resultsProblem Statement
When an evaluator cannot produce a meaningful score for a given case, it has no way to say so. The only output shape is
EvaluationOutput(score=..., test_pass=..., reason=...), which forces every result into the pass/fail binary. An evaluator that legitimately cannot grade a case (missing input data, inapplicable rubric, harness error) must pick a score anyway, and that score pollutes aggregates.Take an error-handling evaluator. If the agent's trajectory contains no tool errors, there is nothing to evaluate. Today the evaluator has three bad options:
score=1.0, test_pass=Trueand inflate the pass rate with a hollow pass.score=0.0, test_pass=Falseand penalize a correct agent for something that never happened.reasonstring and hope downstream consumers parse it out.Option 3 is what I currently do. It works, but it's fragile and invisible to the framework. The
_default_aggregatoronEvaluatorcomputes a straight average:There is no way to exclude a result from this sum without the aggregator parsing
reasonstrings. That coupling should not exist.The same problem shows up at the
Experimentlevel. When building theEvaluationReport, scores are averaged directly:A non-gradable result at
score=0.0drags the average down. A non-gradable result atscore=1.0inflates it. Either way the aggregate lies.Proposed Solution
Add an optional
statusfield toEvaluationOutputthat defaults to"graded":Three values:
"graded"(default) means the score and test_pass are real verdicts. Backward compatible with every existing evaluator and consumer."could_not_evaluate"means the evaluator tried to grade but could not. Preconditions not met, harness failure, missing data. Aggregators should exclude this from pass-rate and score math."informational"means the evaluator does not pass/fail by design. It surfaces content for human review but should never count toward a numeric aggregate.The field is optional and defaults to
"graded", so existing code keeps working with zero changes.The
_default_aggregatorinevaluator.pywould filter before computing:The
Experimentreport assembly inexperiment.pywould also need to exclude non-graded rows when computingoverall_score. Today it doessum(scores) / len(scores). With this feature, it would filter on the status carried through from the aggregated outputs. Concretely, the_run_evaluatormethod already callsevaluator.aggregator(outputs), and since non-graded outputs would be excluded at the aggregator level, the per-evaluator score would already be correct. The remaining gap is when multiple evaluators are flattened viaEvaluationReport.flatten(), which also averages all scores. AddingstatustoEvaluationOutput(and propagating it into the report'scasesdict) would letflatten()skip non-graded rows too.An evaluator producing a non-gradable result would look like:
Use Case
An error-handling evaluator that only applies when tool errors exist. No errors means nothing to score. Today it must fake a pass or a fail. With
status="could_not_evaluate"the result gets excluded from aggregates and surfaced separately in reports.A faithfulness evaluator where the agent declined to answer (empty output). There is no claim to ground-check. Instead of scoring 1.0 or 0.0, it returns CNE and the aggregate reflects only cases where faithfulness was actually testable.
A "referenced tickets" evaluator that lists which tickets the agent mentioned but does not pass/fail. It produces
status="informational"results that show up in a report for human review without counting toward the suite's pass rate.Context-window overflow during judging. Issue [BUG] Judge-based evaluators silently score 0 when a trace exceeds the judge's context window #342 describes judge-based evaluators scoring 0 when a trace exceeds the context window. The error-isolation catch block could set
status="could_not_evaluate"instead of recordingscore=0, test_pass=False, giving downstream consumers a clean signal that the 0 is a harness failure, not a quality failure.Alternatives Considered
A convention on
scorevalue (e.g. score=-1.0 means skip). I do this today. It works until someone writes an evaluator that legitimately scores on a scale including negative numbers, or a consumer forgets to filter. A dedicated field is explicit and impossible to misinterpret.A convention on
reasonprefix (e.g."[Could Not Evaluate] ..."). Also what I do today. Brittle, requires string parsing, and invisible to typed consumers. The framework should not require regex on a reason string to compute a correct average.A separate
EvaluationSkiptype or exception. More disruptive. Evaluators returnlist[EvaluationOutput], and that contract is baked into every evaluator subclass and into_run_evaluator's aggregation flow. Adding a union type or exception would require every consumer to handle a new branch. An optional field on the existing model is the smallest possible change.Additional Context
score=0). This feature gives [BUG] Judge-based evaluators silently score 0 when a trace exceeds the judge's context window #342 a clean landing spot on the model layer. The two are complementary.Literal["graded", "could_not_evaluate", "informational"]type annotation is fine for documentation and tooling while remaining forward-compatible.