Fix/query expander duplicate queries (#12035)

This commit is contained in:
Aquib Ahmad
2026-07-20 14:55:55 +02:00
committed by GitHub
parent 3780936e45
commit ad3391dc73
3 changed files with 41 additions and 4 deletions
+8 -4
View File
@@ -228,7 +228,7 @@ class QueryExpander:
)
expanded_queries = expanded_queries[:expansion_count]
# Add original query if requested and remove duplicates
# Add original query if not already present
if self.include_original_query:
expanded_queries_lower = [q.lower() for q in expanded_queries]
if query.lower() not in expanded_queries_lower:
@@ -297,7 +297,7 @@ class QueryExpander:
)
expanded_queries = expanded_queries[:expansion_count]
# Add original query if requested and remove duplicates
# Add original query if not already present
if self.include_original_query:
expanded_queries_lower = [q.lower() for q in expanded_queries]
if query.lower() not in expanded_queries_lower:
@@ -349,7 +349,7 @@ class QueryExpander:
Parse the generator response to extract individual expanded queries.
:param generator_response: The raw text response from the generator.
:return: List of parsed expanded queries.
:return: List of parsed expanded queries, deduplicated in first-seen order.
"""
parsed = _parse_dict_from_json(generator_response, expected_keys=["queries"], raise_on_failure=False)
@@ -364,9 +364,13 @@ class QueryExpander:
return []
queries = []
seen: set[str] = set()
for item in parsed["queries"]:
if isinstance(item, str) and item.strip():
queries.append(item.strip())
stripped = item.strip()
if stripped not in seen:
seen.add(stripped)
queries.append(stripped)
else:
logger.warning("Skipping non-string or empty query in response: {item}", item=item)
@@ -0,0 +1,7 @@
---
fixes:
- |
Fixed ``QueryExpander`` returning duplicate queries when the chat generator repeats an
expansion. Generated queries are now deduplicated while preserving first-seen order,
so repeated expansions no longer trigger redundant retrievals or consume the requested
expansion budget. Both ``run`` and ``run_async`` are affected.
@@ -200,6 +200,18 @@ class TestQueryExpander:
assert result["queries"] == ["original query", "alt1", "alt2"]
assert len(result["queries"]) == 3
def test_run_deduplicates_generated_queries(self):
chat_generator = MockChatGenerator('{"queries": ["same query", "same query", "other query"]}')
expander = QueryExpander(chat_generator=chat_generator, n_expansions=3, include_original_query=False)
result = expander.run("topic")
assert result["queries"] == ["same query", "other query"]
def test_run_deduplicates_before_truncating(self):
chat_generator = MockChatGenerator('{"queries": ["a", "a", "b", "c"]}')
expander = QueryExpander(chat_generator=chat_generator, n_expansions=3, include_original_query=False)
result = expander.run("topic")
assert result["queries"] == ["a", "b", "c"]
def test_run_truncates_excess_queries(self, caplog):
chat_generator = MockChatGenerator('{"queries": ["q1", "q2", "q3", "q4", "q5"]}')
expander = QueryExpander(chat_generator=chat_generator, n_expansions=3, include_original_query=False)
@@ -397,6 +409,20 @@ class TestQueryExpanderAsync:
]
fake_chat_generator.run.assert_called_once()
@pytest.mark.asyncio
async def test_run_async_deduplicates_generated_queries(self):
chat_generator = MockChatGenerator('{"queries": ["same query", "same query", "other query"]}')
expander = QueryExpander(chat_generator=chat_generator, n_expansions=3, include_original_query=False)
result = await expander.run_async("topic")
assert result["queries"] == ["same query", "other query"]
@pytest.mark.asyncio
async def test_run_async_deduplicates_before_truncating(self):
chat_generator = MockChatGenerator('{"queries": ["a", "a", "b", "c"]}')
expander = QueryExpander(chat_generator=chat_generator, n_expansions=3, include_original_query=False)
result = await expander.run_async("topic")
assert result["queries"] == ["a", "b", "c"]
@pytest.mark.integration
class TestQueryExpanderIntegration: