4
4
from azure .search .documents .agent .models import (
5
5
KnowledgeAgentAzureSearchDocReference ,
6
6
KnowledgeAgentMessage ,
7
- KnowledgeAgentMessageTextContent ,
8
7
KnowledgeAgentRetrievalResponse ,
9
- KnowledgeAgentSearchActivityRecord ,
10
- KnowledgeAgentSearchActivityRecordQuery ,
11
8
)
12
9
from azure .search .documents .aio import SearchClient
13
10
11
+ from .conftest import create_mock_retrieve
14
12
from .mocks import (
15
13
MockAsyncSearchResultsIterator ,
16
- mock_retrieval_response ,
17
- mock_retrieval_response_with_duplicates ,
18
- mock_retrieval_response_with_sorting ,
19
14
)
20
15
21
16
22
-
23
-
24
- async def mock_search (* args , ** kwargs ):
25
- return MockAsyncSearchResultsIterator (kwargs .get ("search_text" ), kwargs .get ("vector_queries" ))
26
-
27
-
28
- async def mock_search_for_hydration (* args , ** kwargs ):
29
- filter_param = kwargs .get ("filter" , "" )
30
-
31
- search_text = ""
32
- if "doc1" in filter_param and "doc2" in filter_param :
33
- search_text = "hydrated_multi"
34
- elif "doc1" in filter_param :
35
- search_text = "hydrated_single"
36
- else :
37
- search_text = "hydrated_empty"
38
-
39
- kwargs ["search_text" ] = search_text
40
-
41
- return mock_search (* args , ** kwargs )
42
-
43
-
44
17
@pytest .mark .asyncio
45
18
async def test_agentic_retrieval_non_hydrated_default_sort (chat_approach , monkeypatch ):
46
19
"""Test non-hydrated path with default sorting (preserve original order)"""
47
20
48
- monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval_response_with_sorting )
21
+ monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , create_mock_retrieve ( "sorting" ) )
49
22
50
23
agent_client = KnowledgeAgentRetrievalClient (endpoint = "" , agent_name = "" , credential = AzureKeyCredential ("" ))
51
24
@@ -71,12 +44,15 @@ async def test_agentic_retrieval_non_hydrated_default_sort(chat_approach, monkey
71
44
async def test_agentic_retrieval_non_hydrated_interleaved_sort (chat_approach , monkeypatch ):
72
45
"""Test non-hydrated path with interleaved sorting"""
73
46
74
- monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval_response_with_sorting )
47
+ monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , create_mock_retrieve ( "sorting" ) )
75
48
76
49
agent_client = KnowledgeAgentRetrievalClient (endpoint = "" , agent_name = "" , credential = AzureKeyCredential ("" ))
77
50
78
51
_ , results = await chat_approach .run_agentic_retrieval (
79
- messages = [], agent_client = agent_client , search_index_name = "test-index" , results_merge_strategy = "interleaved"
52
+ messages = [],
53
+ agent_client = agent_client ,
54
+ search_index_name = "test-index" ,
55
+ results_merge_strategy = "interleaved" ,
80
56
)
81
57
82
58
assert len (results ) == 2
@@ -94,13 +70,21 @@ async def test_agentic_retrieval_non_hydrated_interleaved_sort(chat_approach, mo
94
70
async def test_agentic_retrieval_hydrated_with_sorting (chat_approach_with_hydration , monkeypatch ):
95
71
"""Test hydrated path with sorting"""
96
72
97
- monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval_response_with_sorting )
98
- monkeypatch .setattr (SearchClient , "search" , mock_search_for_hydration )
73
+ monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , create_mock_retrieve ("sorting" ))
74
+
75
+ async def mock_search (self , * args , ** kwargs ):
76
+ # For hydration, we expect a filter like "search.in(id, 'doc1,doc2', ',')"
77
+ return MockAsyncSearchResultsIterator ("hydrated_multi" , None )
78
+
79
+ monkeypatch .setattr (SearchClient , "search" , mock_search )
99
80
100
81
agent_client = KnowledgeAgentRetrievalClient (endpoint = "" , agent_name = "" , credential = AzureKeyCredential ("" ))
101
82
102
83
_ , results = await chat_approach_with_hydration .run_agentic_retrieval (
103
- messages = [], agent_client = agent_client , search_index_name = "test-index" , results_merge_strategy = "interleaved"
84
+ messages = [],
85
+ agent_client = agent_client ,
86
+ search_index_name = "test-index" ,
87
+ results_merge_strategy = "interleaved" ,
104
88
)
105
89
106
90
assert len (results ) == 2
@@ -116,8 +100,13 @@ async def test_agentic_retrieval_hydrated_with_sorting(chat_approach_with_hydrat
116
100
async def test_hydrate_agent_references_deduplication (chat_approach_with_hydration , monkeypatch ):
117
101
"""Test that hydrate_agent_references deduplicates doc_keys"""
118
102
119
- monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval_response_with_duplicates )
120
- monkeypatch .setattr (SearchClient , "search" , mock_search_for_hydration )
103
+ monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , create_mock_retrieve ("duplicates" ))
104
+
105
+ async def mock_search (self , * args , ** kwargs ):
106
+ # For deduplication test, we expect doc1 and doc2 to be in the filter
107
+ return MockAsyncSearchResultsIterator ("hydrated_multi" , None )
108
+
109
+ monkeypatch .setattr (SearchClient , "search" , mock_search )
121
110
122
111
agent_client = KnowledgeAgentRetrievalClient (endpoint = "" , agent_name = "" , credential = AzureKeyCredential ("" ))
123
112
@@ -138,7 +127,9 @@ async def test_agentic_retrieval_no_references(chat_approach, monkeypatch):
138
127
139
128
async def mock_retrieval (* args , ** kwargs ):
140
129
return KnowledgeAgentRetrievalResponse (
141
- response = [KnowledgeAgentMessage (role = "assistant" , content = [])], activity = [], references = []
130
+ response = [KnowledgeAgentMessage (role = "assistant" , content = [])],
131
+ activity = [],
132
+ references = [],
142
133
)
143
134
144
135
monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval )
@@ -156,10 +147,7 @@ async def mock_retrieval(*args, **kwargs):
156
147
async def test_activity_mapping_injection (chat_approach , monkeypatch ):
157
148
"""Test that search_agent_query is properly injected from activity mapping"""
158
149
159
- async def mock_retrieval (* args , ** kwargs ):
160
- return mock_retrieval_response_with_sorting ()
161
-
162
- monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval )
150
+ monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , create_mock_retrieve ("sorting" ))
163
151
164
152
agent_client = KnowledgeAgentRetrievalClient (endpoint = "" , agent_name = "" , credential = AzureKeyCredential ("" ))
165
153
@@ -178,93 +166,20 @@ async def mock_retrieval(*args, **kwargs):
178
166
assert doc2 .search_agent_query == "second query" # From activity_source=2
179
167
180
168
181
- def mock_retrieval_response_with_missing_doc_key ():
182
- """Mock response with missing doc_key to test continue condition"""
183
- return KnowledgeAgentRetrievalResponse (
184
- response = [
185
- KnowledgeAgentMessage (
186
- role = "assistant" ,
187
- content = [KnowledgeAgentMessageTextContent (text = "Test response" )],
188
- )
189
- ],
190
- activity = [
191
- KnowledgeAgentSearchActivityRecord (
192
- id = 1 ,
193
- target_index = "index" ,
194
- query = KnowledgeAgentSearchActivityRecordQuery (search = "query" ),
195
- count = 10 ,
196
- elapsed_ms = 50 ,
197
- ),
198
- ],
199
- references = [
200
- KnowledgeAgentAzureSearchDocReference (
201
- id = "1" ,
202
- activity_source = 1 ,
203
- doc_key = None , # Missing doc_key
204
- source_data = {"content" : "Content 1" , "sourcepage" : "page1.pdf" },
205
- ),
206
- KnowledgeAgentAzureSearchDocReference (
207
- id = "2" ,
208
- activity_source = 1 ,
209
- doc_key = "" , # Empty doc_key
210
- source_data = {"content" : "Content 2" , "sourcepage" : "page2.pdf" },
211
- ),
212
- KnowledgeAgentAzureSearchDocReference (
213
- id = "3" ,
214
- activity_source = 1 ,
215
- doc_key = "doc3" , # Valid doc_key
216
- source_data = {"content" : "Content 3" , "sourcepage" : "page3.pdf" },
217
- ),
218
- ],
219
- )
220
-
221
-
222
- def mock_retrieval_response_with_top_limit ():
223
- """Mock response with many references to test top limit during document building"""
224
- references = []
225
- for i in range (15 ): # More than any reasonable top limit
226
- references .append (
227
- KnowledgeAgentAzureSearchDocReference (
228
- id = str (i ),
229
- activity_source = 1 ,
230
- doc_key = f"doc{ i } " ,
231
- source_data = {"content" : f"Content { i } " , "sourcepage" : f"page{ i } .pdf" },
232
- )
233
- )
234
-
235
- return KnowledgeAgentRetrievalResponse (
236
- response = [
237
- KnowledgeAgentMessage (
238
- role = "assistant" ,
239
- content = [KnowledgeAgentMessageTextContent (text = "Test response" )],
240
- )
241
- ],
242
- activity = [
243
- KnowledgeAgentSearchActivityRecord (
244
- id = 1 ,
245
- target_index = "index" ,
246
- query = KnowledgeAgentSearchActivityRecordQuery (search = "query" ),
247
- count = 10 ,
248
- elapsed_ms = 50 ,
249
- ),
250
- ],
251
- references = references ,
252
- )
253
-
254
-
255
169
@pytest .mark .asyncio
256
170
async def test_hydrate_agent_references_missing_doc_keys (chat_approach_with_hydration , monkeypatch ):
257
171
"""Test that hydrate_agent_references handles missing/empty doc_keys correctly"""
258
172
259
- async def mock_retrieval (* args , ** kwargs ):
260
- return mock_retrieval_response_with_missing_doc_key ()
173
+ monkeypatch .setattr (
174
+ KnowledgeAgentRetrievalClient ,
175
+ "retrieve" ,
176
+ create_mock_retrieve ("missing_doc_key" ),
177
+ )
261
178
262
- # Mock search to return single document for doc3
263
- async def mock_search_single (* args , ** kwargs ):
179
+ async def mock_search (self , * args , ** kwargs ):
264
180
return MockAsyncSearchResultsIterator ("hydrated_single" , None )
265
181
266
- monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval )
267
- monkeypatch .setattr (SearchClient , "search" , mock_search_single )
182
+ monkeypatch .setattr (SearchClient , "search" , mock_search )
268
183
269
184
agent_client = KnowledgeAgentRetrievalClient (endpoint = "" , agent_name = "" , credential = AzureKeyCredential ("" ))
270
185
@@ -327,12 +242,12 @@ async def mock_retrieval_valid_keys(*args, **kwargs):
327
242
],
328
243
)
329
244
330
- # Mock search to return empty results (no documents found)
331
- async def mock_search_returns_empty (* args , ** kwargs ):
245
+ monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval_valid_keys )
246
+
247
+ async def mock_search (self , * args , ** kwargs ):
332
248
return MockAsyncSearchResultsIterator ("hydrated_empty" , None )
333
249
334
- monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval_valid_keys )
335
- monkeypatch .setattr (SearchClient , "search" , mock_search_returns_empty )
250
+ monkeypatch .setattr (SearchClient , "search" , mock_search )
336
251
337
252
agent_client = KnowledgeAgentRetrievalClient (endpoint = "" , agent_name = "" , credential = AzureKeyCredential ("" ))
338
253
@@ -346,18 +261,18 @@ async def mock_search_returns_empty(*args, **kwargs):
346
261
347
262
348
263
@pytest .mark .asyncio
349
- async def test_agentic_retrieval_with_top_limit_during_building (chat_approach_with_hydration , monkeypatch ):
350
- """Test that document building respects top limit and breaks early"""
264
+ async def test_agentic_retrieval_with_top_limit_during_building (chat_approach , monkeypatch ):
265
+ """Test that document building respects top limit and breaks early (non-hydrated path) """
351
266
352
- async def mock_retrieval (* args , ** kwargs ):
353
- return mock_retrieval_response_with_top_limit ()
354
-
355
- monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval )
267
+ monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , create_mock_retrieve ("top_limit" ))
356
268
357
269
agent_client = KnowledgeAgentRetrievalClient (endpoint = "" , agent_name = "" , credential = AzureKeyCredential ("" ))
358
270
359
- _ , results = await chat_approach_with_hydration .run_agentic_retrieval (
360
- messages = [], agent_client = agent_client , search_index_name = "test-index" , top = 5 # Limit to 5 documents
271
+ _ , results = await chat_approach .run_agentic_retrieval (
272
+ messages = [],
273
+ agent_client = agent_client ,
274
+ search_index_name = "test-index" ,
275
+ top = 5 , # Limit to 5 documents
361
276
)
362
277
363
278
# Should get exactly 5 documents due to top limit during building
@@ -371,20 +286,20 @@ async def mock_retrieval(*args, **kwargs):
371
286
async def test_hydrate_agent_references_with_top_limit_during_collection (chat_approach_with_hydration , monkeypatch ):
372
287
"""Test that hydration respects top limit when collecting doc_keys"""
373
288
374
- async def mock_retrieval (* args , ** kwargs ):
375
- return mock_retrieval_response_with_top_limit ()
289
+ monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , create_mock_retrieve ("top_limit" ))
376
290
377
- # Mock search to return multi results (more than our top limit)
378
- async def mock_search_multi (* args , ** kwargs ):
291
+ async def mock_search (self , * args , ** kwargs ):
379
292
return MockAsyncSearchResultsIterator ("hydrated_multi" , None )
380
293
381
- monkeypatch .setattr (KnowledgeAgentRetrievalClient , "retrieve" , mock_retrieval )
382
- monkeypatch .setattr (SearchClient , "search" , mock_search_multi )
294
+ monkeypatch .setattr (SearchClient , "search" , mock_search )
383
295
384
296
agent_client = KnowledgeAgentRetrievalClient (endpoint = "" , agent_name = "" , credential = AzureKeyCredential ("" ))
385
297
386
298
_ , results = await chat_approach_with_hydration .run_agentic_retrieval (
387
- messages = [], agent_client = agent_client , search_index_name = "test-index" , top = 2 # Limit to 2 documents
299
+ messages = [],
300
+ agent_client = agent_client ,
301
+ search_index_name = "test-index" ,
302
+ top = 2 , # Limit to 2 documents
388
303
)
389
304
390
305
# Should get exactly 2 documents due to top limit during doc_keys collection
0 commit comments