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.
Files changed (44) hide show
  1. unique_toolkit/__init__.py +12 -6
  2. unique_toolkit/_common/docx_generator/service.py +8 -32
  3. unique_toolkit/_common/utils/jinja/helpers.py +10 -0
  4. unique_toolkit/_common/utils/jinja/render.py +18 -0
  5. unique_toolkit/_common/utils/jinja/schema.py +65 -0
  6. unique_toolkit/_common/utils/jinja/utils.py +80 -0
  7. unique_toolkit/agentic/message_log_manager/service.py +9 -0
  8. unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py +58 -3
  9. unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py +11 -0
  10. unique_toolkit/agentic/tools/a2a/postprocessing/config.py +33 -0
  11. unique_toolkit/agentic/tools/a2a/postprocessing/display.py +99 -15
  12. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +421 -0
  13. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display_utils.py +768 -0
  14. unique_toolkit/agentic/tools/a2a/tool/config.py +77 -1
  15. unique_toolkit/agentic/tools/a2a/tool/service.py +67 -3
  16. unique_toolkit/agentic/tools/config.py +5 -45
  17. unique_toolkit/agentic/tools/openai_builtin/base.py +4 -0
  18. unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py +4 -0
  19. unique_toolkit/agentic/tools/tool_manager.py +16 -19
  20. unique_toolkit/app/__init__.py +3 -0
  21. unique_toolkit/app/fast_api_factory.py +131 -0
  22. unique_toolkit/app/webhook.py +77 -0
  23. unique_toolkit/chat/functions.py +1 -1
  24. unique_toolkit/content/functions.py +4 -4
  25. unique_toolkit/content/service.py +1 -1
  26. unique_toolkit/data_extraction/README.md +96 -0
  27. unique_toolkit/data_extraction/__init__.py +11 -0
  28. unique_toolkit/data_extraction/augmented/__init__.py +5 -0
  29. unique_toolkit/data_extraction/augmented/service.py +93 -0
  30. unique_toolkit/data_extraction/base.py +25 -0
  31. unique_toolkit/data_extraction/basic/__init__.py +11 -0
  32. unique_toolkit/data_extraction/basic/config.py +18 -0
  33. unique_toolkit/data_extraction/basic/prompt.py +13 -0
  34. unique_toolkit/data_extraction/basic/service.py +55 -0
  35. unique_toolkit/embedding/service.py +1 -1
  36. unique_toolkit/framework_utilities/langchain/__init__.py +10 -0
  37. unique_toolkit/framework_utilities/openai/client.py +2 -1
  38. unique_toolkit/language_model/infos.py +22 -1
  39. unique_toolkit/services/knowledge_base.py +4 -6
  40. {unique_toolkit-1.28.8.dist-info → unique_toolkit-1.33.3.dist-info}/METADATA +51 -2
  41. {unique_toolkit-1.28.8.dist-info → unique_toolkit-1.33.3.dist-info}/RECORD +43 -27
  42. unique_toolkit/agentic/tools/test/test_tool_manager.py +0 -1686
  43. {unique_toolkit-1.28.8.dist-info → unique_toolkit-1.33.3.dist-info}/LICENSE +0 -0
  44. {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"}