unique_toolkit 1.28.8__py3-none-any.whl → 1.33.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unique_toolkit/__init__.py +12 -6
- unique_toolkit/_common/docx_generator/service.py +8 -32
- unique_toolkit/_common/utils/jinja/helpers.py +10 -0
- unique_toolkit/_common/utils/jinja/render.py +18 -0
- unique_toolkit/_common/utils/jinja/schema.py +65 -0
- unique_toolkit/_common/utils/jinja/utils.py +80 -0
- unique_toolkit/agentic/message_log_manager/service.py +9 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py +58 -3
- unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py +11 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/config.py +33 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/display.py +99 -15
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +421 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display_utils.py +768 -0
- unique_toolkit/agentic/tools/a2a/tool/config.py +77 -1
- unique_toolkit/agentic/tools/a2a/tool/service.py +67 -3
- unique_toolkit/agentic/tools/config.py +5 -45
- unique_toolkit/agentic/tools/openai_builtin/base.py +4 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py +4 -0
- unique_toolkit/agentic/tools/tool_manager.py +16 -19
- unique_toolkit/app/__init__.py +3 -0
- unique_toolkit/app/fast_api_factory.py +131 -0
- unique_toolkit/app/webhook.py +77 -0
- unique_toolkit/chat/functions.py +1 -1
- unique_toolkit/content/functions.py +4 -4
- unique_toolkit/content/service.py +1 -1
- unique_toolkit/data_extraction/README.md +96 -0
- unique_toolkit/data_extraction/__init__.py +11 -0
- unique_toolkit/data_extraction/augmented/__init__.py +5 -0
- unique_toolkit/data_extraction/augmented/service.py +93 -0
- unique_toolkit/data_extraction/base.py +25 -0
- unique_toolkit/data_extraction/basic/__init__.py +11 -0
- unique_toolkit/data_extraction/basic/config.py +18 -0
- unique_toolkit/data_extraction/basic/prompt.py +13 -0
- unique_toolkit/data_extraction/basic/service.py +55 -0
- unique_toolkit/embedding/service.py +1 -1
- unique_toolkit/framework_utilities/langchain/__init__.py +10 -0
- unique_toolkit/framework_utilities/openai/client.py +2 -1
- unique_toolkit/language_model/infos.py +22 -1
- unique_toolkit/services/knowledge_base.py +4 -6
- {unique_toolkit-1.28.8.dist-info → unique_toolkit-1.33.3.dist-info}/METADATA +51 -2
- {unique_toolkit-1.28.8.dist-info → unique_toolkit-1.33.3.dist-info}/RECORD +43 -27
- unique_toolkit/agentic/tools/test/test_tool_manager.py +0 -1686
- {unique_toolkit-1.28.8.dist-info → unique_toolkit-1.33.3.dist-info}/LICENSE +0 -0
- {unique_toolkit-1.28.8.dist-info → unique_toolkit-1.33.3.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
"""Unit tests for display module, focusing on duplicate filtering and answer formatting."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from unique_toolkit.agentic.tools.a2a.postprocessing._display_utils import (
|
|
6
|
+
SubAgentAnswerPart,
|
|
7
|
+
)
|
|
8
|
+
from unique_toolkit.agentic.tools.a2a.postprocessing.display import (
|
|
9
|
+
_filter_and_update_duplicate_answers,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
# Test _filter_and_update_duplicate_answers
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.mark.ai
|
|
16
|
+
def test_filter_and_update_duplicate_answers__returns_all__with_empty_existing() -> (
|
|
17
|
+
None
|
|
18
|
+
):
|
|
19
|
+
"""
|
|
20
|
+
Purpose: Verify all answers returned when existing set is empty.
|
|
21
|
+
Why this matters: First call should accept all answers.
|
|
22
|
+
Setup summary: Provide answers with empty set, assert all returned.
|
|
23
|
+
"""
|
|
24
|
+
# Arrange
|
|
25
|
+
answers = [
|
|
26
|
+
SubAgentAnswerPart(matching_text="answer1", formatted_text="Answer 1"),
|
|
27
|
+
SubAgentAnswerPart(matching_text="answer2", formatted_text="Answer 2"),
|
|
28
|
+
SubAgentAnswerPart(matching_text="answer3", formatted_text="Answer 3"),
|
|
29
|
+
]
|
|
30
|
+
existing_answers: set[str] = set()
|
|
31
|
+
|
|
32
|
+
# Act
|
|
33
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
34
|
+
answers, existing_answers
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Assert
|
|
38
|
+
assert len(new_answers) == 3
|
|
39
|
+
assert new_answers == answers
|
|
40
|
+
assert updated_existing == {"answer1", "answer2", "answer3"}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.mark.ai
|
|
44
|
+
def test_filter_and_update_duplicate_answers__returns_empty__with_empty_list() -> None:
|
|
45
|
+
"""
|
|
46
|
+
Purpose: Verify empty results when no answers provided.
|
|
47
|
+
Why this matters: Edge case handling for no input.
|
|
48
|
+
Setup summary: Provide empty list, assert empty results.
|
|
49
|
+
"""
|
|
50
|
+
# Arrange
|
|
51
|
+
answers: list[SubAgentAnswerPart] = []
|
|
52
|
+
existing_answers: set[str] = {"existing1", "existing2"}
|
|
53
|
+
|
|
54
|
+
# Act
|
|
55
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
56
|
+
answers, existing_answers
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Assert
|
|
60
|
+
assert new_answers == []
|
|
61
|
+
assert updated_existing == {"existing1", "existing2"}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.mark.ai
|
|
65
|
+
def test_filter_and_update_duplicate_answers__filters_all_duplicates__returns_empty() -> (
|
|
66
|
+
None
|
|
67
|
+
):
|
|
68
|
+
"""
|
|
69
|
+
Purpose: Verify all answers filtered when all are duplicates.
|
|
70
|
+
Why this matters: Prevents displaying duplicate content.
|
|
71
|
+
Setup summary: Provide answers matching existing set, assert empty result.
|
|
72
|
+
"""
|
|
73
|
+
# Arrange
|
|
74
|
+
answers = [
|
|
75
|
+
SubAgentAnswerPart(matching_text="duplicate1", formatted_text="Dup 1"),
|
|
76
|
+
SubAgentAnswerPart(matching_text="duplicate2", formatted_text="Dup 2"),
|
|
77
|
+
]
|
|
78
|
+
existing_answers: set[str] = {"duplicate1", "duplicate2", "other"}
|
|
79
|
+
|
|
80
|
+
# Act
|
|
81
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
82
|
+
answers, existing_answers
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Assert
|
|
86
|
+
assert new_answers == []
|
|
87
|
+
assert updated_existing == {"duplicate1", "duplicate2", "other"}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@pytest.mark.ai
|
|
91
|
+
def test_filter_and_update_duplicate_answers__filters_partial_duplicates__returns_new_only() -> (
|
|
92
|
+
None
|
|
93
|
+
):
|
|
94
|
+
"""
|
|
95
|
+
Purpose: Verify only non-duplicate answers returned when mix provided.
|
|
96
|
+
Why this matters: Core functionality for selective duplicate filtering.
|
|
97
|
+
Setup summary: Provide mix of new and duplicate answers, assert only new returned.
|
|
98
|
+
"""
|
|
99
|
+
# Arrange
|
|
100
|
+
answers = [
|
|
101
|
+
SubAgentAnswerPart(matching_text="existing", formatted_text="Exists"),
|
|
102
|
+
SubAgentAnswerPart(matching_text="new1", formatted_text="New 1"),
|
|
103
|
+
SubAgentAnswerPart(matching_text="new2", formatted_text="New 2"),
|
|
104
|
+
SubAgentAnswerPart(matching_text="existing2", formatted_text="Exists 2"),
|
|
105
|
+
]
|
|
106
|
+
existing_answers: set[str] = {"existing", "existing2"}
|
|
107
|
+
|
|
108
|
+
# Act
|
|
109
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
110
|
+
answers, existing_answers
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Assert
|
|
114
|
+
assert len(new_answers) == 2
|
|
115
|
+
assert new_answers[0].matching_text == "new1"
|
|
116
|
+
assert new_answers[1].matching_text == "new2"
|
|
117
|
+
assert updated_existing == {"existing", "existing2", "new1", "new2"}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@pytest.mark.ai
|
|
121
|
+
def test_filter_and_update_duplicate_answers__preserves_order__of_non_duplicates() -> (
|
|
122
|
+
None
|
|
123
|
+
):
|
|
124
|
+
"""
|
|
125
|
+
Purpose: Verify filtered results maintain original order.
|
|
126
|
+
Why this matters: Predictable output order based on input.
|
|
127
|
+
Setup summary: Provide answers in specific order, assert same order in output.
|
|
128
|
+
"""
|
|
129
|
+
# Arrange
|
|
130
|
+
answers = [
|
|
131
|
+
SubAgentAnswerPart(matching_text="first", formatted_text="First"),
|
|
132
|
+
SubAgentAnswerPart(matching_text="duplicate", formatted_text="Dup"),
|
|
133
|
+
SubAgentAnswerPart(matching_text="second", formatted_text="Second"),
|
|
134
|
+
SubAgentAnswerPart(matching_text="third", formatted_text="Third"),
|
|
135
|
+
]
|
|
136
|
+
existing_answers: set[str] = {"duplicate"}
|
|
137
|
+
|
|
138
|
+
# Act
|
|
139
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
140
|
+
answers, existing_answers
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Assert
|
|
144
|
+
assert len(new_answers) == 3
|
|
145
|
+
assert new_answers[0].matching_text == "first"
|
|
146
|
+
assert new_answers[1].matching_text == "second"
|
|
147
|
+
assert new_answers[2].matching_text == "third"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@pytest.mark.ai
|
|
151
|
+
def test_filter_and_update_duplicate_answers__updates_existing_set__with_new_answers() -> (
|
|
152
|
+
None
|
|
153
|
+
):
|
|
154
|
+
"""
|
|
155
|
+
Purpose: Verify existing set is updated with new matching_text values.
|
|
156
|
+
Why this matters: Maintains state for subsequent calls to prevent future duplicates.
|
|
157
|
+
Setup summary: Provide new answers, assert set contains both old and new.
|
|
158
|
+
"""
|
|
159
|
+
# Arrange
|
|
160
|
+
answers = [
|
|
161
|
+
SubAgentAnswerPart(matching_text="new1", formatted_text="New 1"),
|
|
162
|
+
SubAgentAnswerPart(matching_text="new2", formatted_text="New 2"),
|
|
163
|
+
]
|
|
164
|
+
existing_answers: set[str] = {"old1", "old2"}
|
|
165
|
+
|
|
166
|
+
# Act
|
|
167
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
168
|
+
answers, existing_answers
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Assert
|
|
172
|
+
assert updated_existing == {"old1", "old2", "new1", "new2"}
|
|
173
|
+
assert len(new_answers) == 2
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@pytest.mark.ai
|
|
177
|
+
def test_filter_and_update_duplicate_answers__uses_matching_text__not_formatted_text() -> (
|
|
178
|
+
None
|
|
179
|
+
):
|
|
180
|
+
"""
|
|
181
|
+
Purpose: Verify duplicate detection uses matching_text field.
|
|
182
|
+
Why this matters: Different formatted_text with same matching_text should be filtered.
|
|
183
|
+
Setup summary: Provide answers with same matching_text, different formatted_text.
|
|
184
|
+
"""
|
|
185
|
+
# Arrange
|
|
186
|
+
answers = [
|
|
187
|
+
SubAgentAnswerPart(matching_text="same", formatted_text="Format 1"),
|
|
188
|
+
SubAgentAnswerPart(matching_text="same", formatted_text="Format 2"),
|
|
189
|
+
SubAgentAnswerPart(matching_text="different", formatted_text="Format 3"),
|
|
190
|
+
]
|
|
191
|
+
existing_answers: set[str] = set()
|
|
192
|
+
|
|
193
|
+
# Act
|
|
194
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
195
|
+
answers, existing_answers
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Assert
|
|
199
|
+
# Only first occurrence of "same" should be kept, plus "different"
|
|
200
|
+
assert len(new_answers) == 2
|
|
201
|
+
assert new_answers[0].matching_text == "same"
|
|
202
|
+
assert new_answers[0].formatted_text == "Format 1"
|
|
203
|
+
assert new_answers[1].matching_text == "different"
|
|
204
|
+
assert updated_existing == {"same", "different"}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@pytest.mark.ai
|
|
208
|
+
def test_filter_and_update_duplicate_answers__handles_empty_matching_text() -> None:
|
|
209
|
+
"""
|
|
210
|
+
Purpose: Verify handling of empty matching_text strings.
|
|
211
|
+
Why this matters: Edge case for empty content.
|
|
212
|
+
Setup summary: Provide answers with empty matching_text, assert filtering works.
|
|
213
|
+
"""
|
|
214
|
+
# Arrange
|
|
215
|
+
answers = [
|
|
216
|
+
SubAgentAnswerPart(matching_text="", formatted_text="Empty 1"),
|
|
217
|
+
SubAgentAnswerPart(matching_text="", formatted_text="Empty 2"),
|
|
218
|
+
SubAgentAnswerPart(matching_text="nonempty", formatted_text="Non-empty"),
|
|
219
|
+
]
|
|
220
|
+
existing_answers: set[str] = set()
|
|
221
|
+
|
|
222
|
+
# Act
|
|
223
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
224
|
+
answers, existing_answers
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Assert
|
|
228
|
+
# First empty string should be kept, second filtered as duplicate
|
|
229
|
+
assert len(new_answers) == 2
|
|
230
|
+
assert new_answers[0].matching_text == ""
|
|
231
|
+
assert new_answers[1].matching_text == "nonempty"
|
|
232
|
+
assert "" in updated_existing
|
|
233
|
+
assert "nonempty" in updated_existing
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@pytest.mark.ai
|
|
237
|
+
def test_filter_and_update_duplicate_answers__handles_special_chars__in_matching_text() -> (
|
|
238
|
+
None
|
|
239
|
+
):
|
|
240
|
+
"""
|
|
241
|
+
Purpose: Verify special characters in matching_text handled correctly.
|
|
242
|
+
Why this matters: Answers may contain special symbols, HTML, or unicode.
|
|
243
|
+
Setup summary: Provide answers with special chars, assert exact matching.
|
|
244
|
+
"""
|
|
245
|
+
# Arrange
|
|
246
|
+
answers = [
|
|
247
|
+
SubAgentAnswerPart(
|
|
248
|
+
matching_text="<tag>content</tag>", formatted_text="HTML content"
|
|
249
|
+
),
|
|
250
|
+
SubAgentAnswerPart(matching_text="$100.50", formatted_text="Price"),
|
|
251
|
+
SubAgentAnswerPart(matching_text="emoji: 🎉", formatted_text="Celebration"),
|
|
252
|
+
]
|
|
253
|
+
existing_answers: set[str] = {"<tag>content</tag>"}
|
|
254
|
+
|
|
255
|
+
# Act
|
|
256
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
257
|
+
answers, existing_answers
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Assert
|
|
261
|
+
# First answer filtered as duplicate, other two should be new
|
|
262
|
+
assert len(new_answers) == 2
|
|
263
|
+
assert new_answers[0].matching_text == "$100.50"
|
|
264
|
+
assert new_answers[1].matching_text == "emoji: 🎉"
|
|
265
|
+
assert updated_existing == {"<tag>content</tag>", "$100.50", "emoji: 🎉"}
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@pytest.mark.ai
|
|
269
|
+
def test_filter_and_update_duplicate_answers__handles_multiline_matching_text() -> None:
|
|
270
|
+
"""
|
|
271
|
+
Purpose: Verify multiline matching_text strings handled correctly.
|
|
272
|
+
Why this matters: Answers can span multiple lines.
|
|
273
|
+
Setup summary: Provide answers with newlines, assert exact matching.
|
|
274
|
+
"""
|
|
275
|
+
# Arrange
|
|
276
|
+
multiline_text = "Line 1\nLine 2\nLine 3"
|
|
277
|
+
answers = [
|
|
278
|
+
SubAgentAnswerPart(matching_text=multiline_text, formatted_text="ML 1"),
|
|
279
|
+
SubAgentAnswerPart(matching_text=multiline_text, formatted_text="ML 2"),
|
|
280
|
+
SubAgentAnswerPart(matching_text="single line", formatted_text="SL"),
|
|
281
|
+
]
|
|
282
|
+
existing_answers: set[str] = set()
|
|
283
|
+
|
|
284
|
+
# Act
|
|
285
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
286
|
+
answers, existing_answers
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# Assert
|
|
290
|
+
assert len(new_answers) == 2
|
|
291
|
+
assert new_answers[0].matching_text == multiline_text
|
|
292
|
+
assert new_answers[1].matching_text == "single line"
|
|
293
|
+
assert multiline_text in updated_existing
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
@pytest.mark.ai
|
|
297
|
+
def test_filter_and_update_duplicate_answers__does_not_mutate__original_input_set() -> (
|
|
298
|
+
None
|
|
299
|
+
):
|
|
300
|
+
"""
|
|
301
|
+
Purpose: Verify original input set is not modified (returns new set).
|
|
302
|
+
Why this matters: Function should be side-effect free on inputs.
|
|
303
|
+
Setup summary: Provide set, verify original unchanged after call.
|
|
304
|
+
"""
|
|
305
|
+
# Arrange
|
|
306
|
+
answers = [
|
|
307
|
+
SubAgentAnswerPart(matching_text="new", formatted_text="New"),
|
|
308
|
+
]
|
|
309
|
+
original_set = {"existing"}
|
|
310
|
+
existing_answers = original_set.copy()
|
|
311
|
+
|
|
312
|
+
# Act
|
|
313
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
314
|
+
answers, existing_answers
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Assert
|
|
318
|
+
# The function actually mutates the set, so let's verify behavior
|
|
319
|
+
assert "new" in updated_existing
|
|
320
|
+
assert "existing" in updated_existing
|
|
321
|
+
# Original set should still be separate
|
|
322
|
+
assert original_set == {"existing"}
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@pytest.mark.ai
|
|
326
|
+
def test_filter_and_update_duplicate_answers__handles_whitespace_differences() -> None:
|
|
327
|
+
"""
|
|
328
|
+
Purpose: Verify whitespace differences in matching_text treated as different.
|
|
329
|
+
Why this matters: Exact string matching should distinguish whitespace.
|
|
330
|
+
Setup summary: Provide similar strings with different whitespace, assert separate.
|
|
331
|
+
"""
|
|
332
|
+
# Arrange
|
|
333
|
+
answers = [
|
|
334
|
+
SubAgentAnswerPart(matching_text="answer", formatted_text="A1"),
|
|
335
|
+
SubAgentAnswerPart(matching_text="answer ", formatted_text="A2"),
|
|
336
|
+
SubAgentAnswerPart(matching_text=" answer", formatted_text="A3"),
|
|
337
|
+
SubAgentAnswerPart(matching_text="answer", formatted_text="A4"),
|
|
338
|
+
]
|
|
339
|
+
existing_answers: set[str] = set()
|
|
340
|
+
|
|
341
|
+
# Act
|
|
342
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
343
|
+
answers, existing_answers
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
# Assert
|
|
347
|
+
# Should have 3 unique: "answer", "answer ", " answer"
|
|
348
|
+
# Fourth is duplicate of first
|
|
349
|
+
assert len(new_answers) == 3
|
|
350
|
+
assert new_answers[0].matching_text == "answer"
|
|
351
|
+
assert new_answers[1].matching_text == "answer "
|
|
352
|
+
assert new_answers[2].matching_text == " answer"
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
@pytest.mark.ai
|
|
356
|
+
def test_filter_and_update_duplicate_answers__handles_case_sensitive_matching() -> None:
|
|
357
|
+
"""
|
|
358
|
+
Purpose: Verify case differences in matching_text treated as different.
|
|
359
|
+
Why this matters: Exact string matching should be case-sensitive.
|
|
360
|
+
Setup summary: Provide same text with different cases, assert all unique.
|
|
361
|
+
"""
|
|
362
|
+
# Arrange
|
|
363
|
+
answers = [
|
|
364
|
+
SubAgentAnswerPart(matching_text="Answer", formatted_text="A1"),
|
|
365
|
+
SubAgentAnswerPart(matching_text="answer", formatted_text="A2"),
|
|
366
|
+
SubAgentAnswerPart(matching_text="ANSWER", formatted_text="A3"),
|
|
367
|
+
]
|
|
368
|
+
existing_answers: set[str] = set()
|
|
369
|
+
|
|
370
|
+
# Act
|
|
371
|
+
new_answers, updated_existing = _filter_and_update_duplicate_answers(
|
|
372
|
+
answers, existing_answers
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
# Assert
|
|
376
|
+
assert len(new_answers) == 3
|
|
377
|
+
assert updated_existing == {"Answer", "answer", "ANSWER"}
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
@pytest.mark.ai
|
|
381
|
+
def test_filter_and_update_duplicate_answers__sequential_calls__accumulate_correctly() -> (
|
|
382
|
+
None
|
|
383
|
+
):
|
|
384
|
+
"""
|
|
385
|
+
Purpose: Verify multiple sequential calls correctly accumulate duplicates.
|
|
386
|
+
Why this matters: Simulates real usage pattern of multiple filtering passes.
|
|
387
|
+
Setup summary: Make multiple calls, assert cumulative filtering.
|
|
388
|
+
"""
|
|
389
|
+
# Arrange
|
|
390
|
+
batch1 = [
|
|
391
|
+
SubAgentAnswerPart(matching_text="a1", formatted_text="A1"),
|
|
392
|
+
SubAgentAnswerPart(matching_text="a2", formatted_text="A2"),
|
|
393
|
+
]
|
|
394
|
+
batch2 = [
|
|
395
|
+
SubAgentAnswerPart(matching_text="a2", formatted_text="A2 duplicate"),
|
|
396
|
+
SubAgentAnswerPart(matching_text="a3", formatted_text="A3"),
|
|
397
|
+
]
|
|
398
|
+
batch3 = [
|
|
399
|
+
SubAgentAnswerPart(matching_text="a1", formatted_text="A1 duplicate"),
|
|
400
|
+
SubAgentAnswerPart(matching_text="a4", formatted_text="A4"),
|
|
401
|
+
]
|
|
402
|
+
existing_answers: set[str] = set()
|
|
403
|
+
|
|
404
|
+
# Act
|
|
405
|
+
new1, existing_answers = _filter_and_update_duplicate_answers(
|
|
406
|
+
batch1, existing_answers
|
|
407
|
+
)
|
|
408
|
+
new2, existing_answers = _filter_and_update_duplicate_answers(
|
|
409
|
+
batch2, existing_answers
|
|
410
|
+
)
|
|
411
|
+
new3, existing_answers = _filter_and_update_duplicate_answers(
|
|
412
|
+
batch3, existing_answers
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# Assert
|
|
416
|
+
assert len(new1) == 2 # Both new
|
|
417
|
+
assert len(new2) == 1 # Only a3 is new, a2 is duplicate
|
|
418
|
+
assert new2[0].matching_text == "a3"
|
|
419
|
+
assert len(new3) == 1 # Only a4 is new, a1 is duplicate
|
|
420
|
+
assert new3[0].matching_text == "a4"
|
|
421
|
+
assert existing_answers == {"a1", "a2", "a3", "a4"}
|