Skip to content

Commit bf1bcb7

Browse files
committed
fix: raise clear error for empty inputs in retrieval/answer evaluators
AnswerExactMatchEvaluator, DocumentMAPEvaluator, DocumentMRREvaluator and DocumentRecallEvaluator averaged their per-query scores by dividing by the input length without guarding against an empty batch. Passing two empty lists (which satisfies the equal-length check) therefore raised an opaque ZeroDivisionError. They now raise a descriptive ValueError, matching the existing contract of DocumentNDCGEvaluator.
1 parent b69e9aa commit bf1bcb7

6 files changed

Lines changed: 52 additions & 0 deletions

File tree

haystack/components/evaluators/answer_exact_match.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ def run(self, ground_truth_answers: list[str], predicted_answers: list[str]) ->
5353
- `score` - A number from 0.0 to 1.0 that represents the proportion of questions where any predicted
5454
answer matched one of the ground truth answers.
5555
"""
56+
if len(ground_truth_answers) == 0 or len(predicted_answers) == 0:
57+
raise ValueError("ground_truth_answers and predicted_answers must be provided.")
58+
5659
if not len(ground_truth_answers) == len(predicted_answers):
5760
raise ValueError("The length of ground_truth_answers and predicted_answers must be the same.")
5861

haystack/components/evaluators/document_map.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ def run(
108108
- `individual_scores` - A list of numbers from 0.0 to 1.0 that represents how high retrieved documents
109109
are ranked.
110110
"""
111+
if len(ground_truth_documents) == 0 or len(retrieved_documents) == 0:
112+
msg = "ground_truth_documents and retrieved_documents must be provided."
113+
raise ValueError(msg)
114+
111115
if len(ground_truth_documents) != len(retrieved_documents):
112116
msg = "The length of ground_truth_documents and retrieved_documents must be the same."
113117
raise ValueError(msg)

haystack/components/evaluators/document_mrr.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ def run(
106106
- `individual_scores` - A list of numbers from 0.0 to 1.0 that represents how high the first retrieved
107107
document is ranked.
108108
"""
109+
if len(ground_truth_documents) == 0 or len(retrieved_documents) == 0:
110+
msg = "ground_truth_documents and retrieved_documents must be provided."
111+
raise ValueError(msg)
112+
109113
if len(ground_truth_documents) != len(retrieved_documents):
110114
msg = "The length of ground_truth_documents and retrieved_documents must be the same."
111115
raise ValueError(msg)

haystack/components/evaluators/document_recall.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ def run(
158158
- `individual_scores` - A list of numbers from 0.0 to 1.0 that represents the proportion of matching
159159
documents retrieved. If the mode is `single_hit`, the individual scores are 0 or 1.
160160
"""
161+
if len(ground_truth_documents) == 0 or len(retrieved_documents) == 0:
162+
msg = "ground_truth_documents and retrieved_documents must be provided."
163+
raise ValueError(msg)
164+
161165
if len(ground_truth_documents) != len(retrieved_documents):
162166
msg = "The length of ground_truth_documents and retrieved_documents must be the same."
163167
raise ValueError(msg)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
``AnswerExactMatchEvaluator``, ``DocumentMAPEvaluator``, ``DocumentMRREvaluator`` and
5+
``DocumentRecallEvaluator`` now raise a descriptive ``ValueError`` when called with
6+
empty input lists, instead of failing with an opaque ``ZeroDivisionError``. This
7+
matches the existing behavior of ``DocumentNDCGEvaluator``.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import pytest
6+
7+
from haystack.components.evaluators import (
8+
AnswerExactMatchEvaluator,
9+
DocumentMAPEvaluator,
10+
DocumentMRREvaluator,
11+
DocumentRecallEvaluator,
12+
)
13+
14+
15+
@pytest.mark.parametrize(
16+
"evaluator, kwargs",
17+
[
18+
(AnswerExactMatchEvaluator(), {"ground_truth_answers": [], "predicted_answers": []}),
19+
(DocumentMAPEvaluator(), {"ground_truth_documents": [], "retrieved_documents": []}),
20+
(DocumentMRREvaluator(), {"ground_truth_documents": [], "retrieved_documents": []}),
21+
(DocumentRecallEvaluator(), {"ground_truth_documents": [], "retrieved_documents": []}),
22+
],
23+
)
24+
def test_run_with_empty_inputs_raises_value_error(evaluator, kwargs):
25+
# Empty (equal-length) inputs previously fell through the length check and
26+
# crashed with a bare ZeroDivisionError when averaging over zero items.
27+
# They must instead raise a descriptive ValueError, matching
28+
# DocumentNDCGEvaluator's contract.
29+
with pytest.raises(ValueError, match="must be provided"):
30+
evaluator.run(**kwargs)

0 commit comments

Comments
 (0)