Skip to content

Commit 0d0c48e

Browse files
authored
fix: AnswerJoiner no longer crashes when sorting answers with a None score (#11701)
1 parent 79a37a8 commit 0d0c48e

3 files changed

Lines changed: 38 additions & 1 deletion

File tree

haystack/components/joiners/answer_joiner.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ def run(self, answers: Variadic[list[AnswerType]], top_k: int | None = None) ->
130130

131131
if self.sort_by_score:
132132
output_answers = sorted(
133-
output_answers, key=lambda answer: answer.score if hasattr(answer, "score") else -inf, reverse=True
133+
output_answers,
134+
key=lambda answer: score if (score := getattr(answer, "score", None)) is not None else -inf,
135+
reverse=True,
134136
)
135137

136138
top_k = top_k or self.top_k
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
Fixed a ``TypeError`` in ``AnswerJoiner`` when running with ``sort_by_score=True`` and at least one
5+
answer had a ``score`` of ``None``. As documented, an answer without a score is now treated as if its
6+
score were negative infinity and sorted last, instead of raising
7+
``TypeError: '<' not supported between instances of 'float' and 'NoneType'``.

test/components/joiners/test_answer_joiner.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,31 @@ def test_unsupported_join_mode(self):
100100
unsupported_mode = "unsupported_mode"
101101
with pytest.raises(ValueError):
102102
AnswerJoiner(join_mode=unsupported_mode)
103+
104+
def test_sort_by_score(self):
105+
joiner = AnswerJoiner(sort_by_score=True)
106+
answers1 = [ExtractedAnswer(query="a", score=0.3, meta={}, document=Document(content="a"))]
107+
answers2 = [ExtractedAnswer(query="b", score=0.9, meta={}, document=Document(content="b"))]
108+
result = joiner.run([answers1, answers2])
109+
scores = [answer.score for answer in result["answers"]]
110+
assert scores == [0.9, 0.3]
111+
112+
def test_sort_by_score_with_none_score(self):
113+
# The docstring promises that an answer with no score is handled as if its score is -infinity.
114+
# ExtractedAnswer with score=None must not raise a TypeError during sorting and must be sorted last.
115+
joiner = AnswerJoiner(sort_by_score=True)
116+
answers1 = [ExtractedAnswer(query="a", score=0.5, meta={}, document=Document(content="a"))]
117+
answers2 = [ExtractedAnswer(query="b", score=None, meta={}, document=Document(content="b"))] # type: ignore[arg-type]
118+
result = joiner.run([answers1, answers2])
119+
assert [answer.data for answer in result["answers"]] == [None, None]
120+
assert [answer.score for answer in result["answers"]] == [0.5, None]
121+
122+
def test_sort_by_score_with_answers_missing_score_attribute(self):
123+
# GeneratedAnswer has no score attribute at all; it must be handled as -infinity and sorted last.
124+
joiner = AnswerJoiner(sort_by_score=True)
125+
answers1 = [GeneratedAnswer(query="a", data="a", meta={}, documents=[Document(content="a")])]
126+
answers2 = [ExtractedAnswer(query="b", score=0.9, meta={}, document=Document(content="b"))]
127+
result = joiner.run([answers1, answers2])
128+
# The ExtractedAnswer (score 0.9) comes first, the GeneratedAnswer (no score) comes last.
129+
assert isinstance(result["answers"][0], ExtractedAnswer)
130+
assert isinstance(result["answers"][1], GeneratedAnswer)

0 commit comments

Comments
 (0)