Repository navigation
Conversation
_extract_answer_string used match.lastindex to decide whether a pattern had a capture group. When the pattern has a single *optional* capture group that does not take part in a match (e.g. Answer: (\d+)? against Answer: abc), lastindex is None and the code wrongly treated it as a no-group pattern, returning the whole match as the answer. Switch to match.groups(), mirroring RegexTextExtractor, so an optional group that does not participate yields an empty answer instead of leaking the full match into the answer.
|
@zuizhuai is attempting to deploy a commit to the deepset Team on Vercel. A member of the Team first needs to authorize it. |
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||
lxcxjxhx
left a comment
There was a problem hiding this comment.
The distinction this fixes is worth stating plainly, because it is the kind of thing that reads as a typo:
lastindex |
groups() |
|
|---|---|---|
| means | index of the last group that participated | tuple of all groups, None for the ones that did not |
(\d+)? vs abc |
None |
(None,) |
| no groups at all | None |
() |
So if not match.lastindex was answering "did any group participate", not "does the pattern have a group". The two agree for every pattern with a mandatory group, which is why it survived.
One thing I checked, since or "" on a capture group looks like it could swallow a real answer: it does not. match.group(1) is a str when the group participates, and "0" is truthy — "0" or "" is "0". The or "" only fires on None, which is exactly the case the PR is about. A group that matches the empty string ((\d*) against abc) also returns "", which is the same value either way.
The test covers the reported case (Answer: (\d+)? against Answer: not a number) and the release note names the behaviour it now matches (RegexTextExtractor), which is the right reference — "what does the sibling component do" is more useful than "what did we decide".
No blocking notes.
Description
AnswerBuilder._extract_answer_stringusedmatch.lastindexto decide whether a pattern contained a capture group.lastindexis the index of the last group that actually participated in the match, so it isNonewhen a pattern has a single optional capture group that does not take part (for exampleAnswer: (\d+)?againstAnswer: abc).In that case the code wrongly treated the pattern as having no group and returned the whole match as the answer, instead of an empty answer.
Fix
Switch to
match.groups(), which reflects whether the pattern declares any capture group, and returnmatch.group(1) or ""for the single-group case. This mirrors the existing behaviour ofRegexTextExtractor(haystack/components/extractors/regex_text_extractor.py), which already handles optional groups correctly._check_num_groups_in_regexalready guarantees at most one capture group, so thematch.groups()truthiness check is sufficient to distinguishno groupfromone group.Changes
haystack/components/builders/answer_builder.py: usematch.groups()instead ofmatch.lastindex, andmatch.group(1) or "".test/components/builders/test_answer_builder.py: add a regression test for an optional group that does not participate.releasenotes/notes/answer-builder-optional-group-*.yaml: add a fix note.Verification
python -m pytest test/components/builders/test_answer_builder.py→ 40 passed.