unique_toolkit 1.8.1__py3-none-any.whl → 1.23.0__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.
Potentially problematic release.
This version of unique_toolkit might be problematic. Click here for more details.
- unique_toolkit/__init__.py +20 -0
- unique_toolkit/_common/api_calling/human_verification_manager.py +121 -28
- unique_toolkit/_common/chunk_relevancy_sorter/config.py +3 -3
- unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +2 -5
- unique_toolkit/_common/default_language_model.py +9 -3
- unique_toolkit/_common/docx_generator/__init__.py +7 -0
- unique_toolkit/_common/docx_generator/config.py +12 -0
- unique_toolkit/_common/docx_generator/schemas.py +80 -0
- unique_toolkit/_common/docx_generator/service.py +252 -0
- unique_toolkit/_common/docx_generator/template/Doc Template.docx +0 -0
- unique_toolkit/_common/endpoint_builder.py +138 -117
- unique_toolkit/_common/endpoint_requestor.py +240 -14
- unique_toolkit/_common/exception.py +20 -0
- unique_toolkit/_common/feature_flags/schema.py +1 -5
- unique_toolkit/_common/referencing.py +53 -0
- unique_toolkit/_common/string_utilities.py +52 -1
- unique_toolkit/_common/tests/test_referencing.py +521 -0
- unique_toolkit/_common/tests/test_string_utilities.py +506 -0
- unique_toolkit/_common/utils/files.py +43 -0
- unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +16 -6
- unique_toolkit/agentic/debug_info_manager/test/test_debug_info_manager.py +278 -0
- unique_toolkit/agentic/evaluation/config.py +3 -2
- unique_toolkit/agentic/evaluation/context_relevancy/service.py +2 -2
- unique_toolkit/agentic/evaluation/evaluation_manager.py +9 -5
- unique_toolkit/agentic/evaluation/hallucination/constants.py +1 -1
- unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +26 -3
- unique_toolkit/agentic/history_manager/history_manager.py +14 -11
- unique_toolkit/agentic/history_manager/loop_token_reducer.py +3 -4
- unique_toolkit/agentic/history_manager/utils.py +10 -87
- unique_toolkit/agentic/postprocessor/postprocessor_manager.py +107 -16
- unique_toolkit/agentic/reference_manager/reference_manager.py +1 -1
- unique_toolkit/agentic/responses_api/__init__.py +19 -0
- unique_toolkit/agentic/responses_api/postprocessors/code_display.py +63 -0
- unique_toolkit/agentic/responses_api/postprocessors/generated_files.py +145 -0
- unique_toolkit/agentic/responses_api/stream_handler.py +15 -0
- unique_toolkit/agentic/tools/a2a/__init__.py +18 -2
- unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +2 -0
- unique_toolkit/agentic/tools/a2a/evaluation/_utils.py +3 -3
- unique_toolkit/agentic/tools/a2a/evaluation/config.py +1 -1
- unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +143 -91
- unique_toolkit/agentic/tools/a2a/manager.py +7 -1
- unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +11 -3
- unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py +185 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py +73 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/config.py +21 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/display.py +180 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/references.py +101 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display_utils.py +1335 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_ref_utils.py +603 -0
- unique_toolkit/agentic/tools/a2a/prompts.py +46 -0
- unique_toolkit/agentic/tools/a2a/response_watcher/__init__.py +6 -0
- unique_toolkit/agentic/tools/a2a/response_watcher/service.py +91 -0
- unique_toolkit/agentic/tools/a2a/tool/config.py +15 -5
- unique_toolkit/agentic/tools/a2a/tool/service.py +69 -36
- unique_toolkit/agentic/tools/config.py +16 -2
- unique_toolkit/agentic/tools/factory.py +4 -0
- unique_toolkit/agentic/tools/mcp/tool_wrapper.py +7 -35
- unique_toolkit/agentic/tools/openai_builtin/__init__.py +11 -0
- unique_toolkit/agentic/tools/openai_builtin/base.py +30 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/__init__.py +8 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/config.py +57 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py +230 -0
- unique_toolkit/agentic/tools/openai_builtin/manager.py +62 -0
- unique_toolkit/agentic/tools/test/test_mcp_manager.py +95 -7
- unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +240 -0
- unique_toolkit/agentic/tools/tool.py +0 -11
- unique_toolkit/agentic/tools/tool_manager.py +337 -122
- unique_toolkit/agentic/tools/tool_progress_reporter.py +81 -15
- unique_toolkit/agentic/tools/utils/__init__.py +18 -0
- unique_toolkit/agentic/tools/utils/execution/execution.py +8 -4
- unique_toolkit/agentic/tools/utils/source_handling/schema.py +1 -1
- unique_toolkit/chat/__init__.py +8 -1
- unique_toolkit/chat/deprecated/service.py +232 -0
- unique_toolkit/chat/functions.py +54 -40
- unique_toolkit/chat/rendering.py +34 -0
- unique_toolkit/chat/responses_api.py +461 -0
- unique_toolkit/chat/schemas.py +1 -1
- unique_toolkit/chat/service.py +96 -1569
- unique_toolkit/content/functions.py +116 -1
- unique_toolkit/content/schemas.py +59 -0
- unique_toolkit/content/service.py +5 -37
- unique_toolkit/content/smart_rules.py +301 -0
- unique_toolkit/framework_utilities/langchain/client.py +27 -3
- unique_toolkit/framework_utilities/openai/client.py +12 -1
- unique_toolkit/framework_utilities/openai/message_builder.py +85 -1
- unique_toolkit/language_model/default_language_model.py +3 -0
- unique_toolkit/language_model/functions.py +25 -9
- unique_toolkit/language_model/infos.py +72 -4
- unique_toolkit/language_model/schemas.py +246 -40
- unique_toolkit/protocols/support.py +91 -9
- unique_toolkit/services/__init__.py +7 -0
- unique_toolkit/services/chat_service.py +1630 -0
- unique_toolkit/services/knowledge_base.py +861 -0
- unique_toolkit/smart_rules/compile.py +56 -301
- unique_toolkit/test_utilities/events.py +197 -0
- {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/METADATA +173 -3
- {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/RECORD +99 -67
- unique_toolkit/agentic/tools/a2a/postprocessing/_display.py +0 -122
- unique_toolkit/agentic/tools/a2a/postprocessing/_utils.py +0 -19
- unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +0 -230
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_consolidate_references.py +0 -665
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +0 -391
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_postprocessor_reference_functions.py +0 -256
- {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/LICENSE +0 -0
- {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for referencing utilities.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.ai
|
|
9
|
+
def test_get_reference_pattern__returns_formatted_sup_tag__with_single_digit() -> None:
|
|
10
|
+
"""
|
|
11
|
+
Purpose: Verify get_reference_pattern creates correct HTML sup tag format.
|
|
12
|
+
Why this matters: Ensures consistent reference formatting across the system.
|
|
13
|
+
Setup summary: Call with single digit, assert proper HTML sup tag structure.
|
|
14
|
+
"""
|
|
15
|
+
# Arrange
|
|
16
|
+
from unique_toolkit._common.referencing import get_reference_pattern
|
|
17
|
+
|
|
18
|
+
ref_number = 1
|
|
19
|
+
|
|
20
|
+
# Act
|
|
21
|
+
result = get_reference_pattern(ref_number)
|
|
22
|
+
|
|
23
|
+
# Assert
|
|
24
|
+
assert result == "<sup>1</sup>"
|
|
25
|
+
assert isinstance(result, str)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.mark.ai
|
|
29
|
+
def test_get_reference_pattern__returns_formatted_sup_tag__with_multi_digit() -> None:
|
|
30
|
+
"""
|
|
31
|
+
Purpose: Verify get_reference_pattern handles multi-digit reference numbers.
|
|
32
|
+
Why this matters: Support for documents with many references (10+).
|
|
33
|
+
Setup summary: Call with multi-digit number, assert proper formatting.
|
|
34
|
+
"""
|
|
35
|
+
# Arrange
|
|
36
|
+
from unique_toolkit._common.referencing import get_reference_pattern
|
|
37
|
+
|
|
38
|
+
ref_number = 42
|
|
39
|
+
|
|
40
|
+
# Act
|
|
41
|
+
result = get_reference_pattern(ref_number)
|
|
42
|
+
|
|
43
|
+
# Assert
|
|
44
|
+
assert result == "<sup>42</sup>"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.mark.ai
|
|
48
|
+
def test_get_all_ref_numbers__returns_sorted_unique_list__with_multiple_refs() -> None:
|
|
49
|
+
"""
|
|
50
|
+
Purpose: Verify get_all_ref_numbers extracts and sorts all reference numbers.
|
|
51
|
+
Why this matters: Enables reference validation and manipulation across text.
|
|
52
|
+
Setup summary: Provide text with multiple references, assert sorted unique list.
|
|
53
|
+
"""
|
|
54
|
+
# Arrange
|
|
55
|
+
from unique_toolkit._common.referencing import get_all_ref_numbers
|
|
56
|
+
|
|
57
|
+
text = (
|
|
58
|
+
"Some text<sup>3</sup> with <sup>1</sup> multiple<sup>3</sup> refs<sup>2</sup>."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Act
|
|
62
|
+
result = get_all_ref_numbers(text)
|
|
63
|
+
|
|
64
|
+
# Assert
|
|
65
|
+
assert result == [1, 2, 3]
|
|
66
|
+
assert isinstance(result, list)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pytest.mark.ai
|
|
70
|
+
def test_get_all_ref_numbers__returns_empty_list__with_no_refs() -> None:
|
|
71
|
+
"""
|
|
72
|
+
Purpose: Verify get_all_ref_numbers handles text without references.
|
|
73
|
+
Why this matters: Prevents errors when processing unreferenced text.
|
|
74
|
+
Setup summary: Provide text without references, assert empty list.
|
|
75
|
+
"""
|
|
76
|
+
# Arrange
|
|
77
|
+
from unique_toolkit._common.referencing import get_all_ref_numbers
|
|
78
|
+
|
|
79
|
+
text = "Some text without any references."
|
|
80
|
+
|
|
81
|
+
# Act
|
|
82
|
+
result = get_all_ref_numbers(text)
|
|
83
|
+
|
|
84
|
+
# Assert
|
|
85
|
+
assert result == []
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@pytest.mark.ai
|
|
89
|
+
def test_get_all_ref_numbers__handles_whitespace__in_sup_tags() -> None:
|
|
90
|
+
"""
|
|
91
|
+
Purpose: Verify get_all_ref_numbers tolerates whitespace in sup tags.
|
|
92
|
+
Why this matters: Handles malformed or pretty-printed HTML gracefully.
|
|
93
|
+
Setup summary: Provide references with internal whitespace, assert correct extraction.
|
|
94
|
+
"""
|
|
95
|
+
# Arrange
|
|
96
|
+
from unique_toolkit._common.referencing import get_all_ref_numbers
|
|
97
|
+
|
|
98
|
+
text = "Text<sup> 1 </sup> with <sup> 2 </sup> spaced refs."
|
|
99
|
+
|
|
100
|
+
# Act
|
|
101
|
+
result = get_all_ref_numbers(text)
|
|
102
|
+
|
|
103
|
+
# Assert
|
|
104
|
+
assert result == [1, 2]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@pytest.mark.ai
|
|
108
|
+
def test_get_max_ref_number__returns_highest_number__with_multiple_refs() -> None:
|
|
109
|
+
"""
|
|
110
|
+
Purpose: Verify get_max_ref_number identifies the maximum reference number.
|
|
111
|
+
Why this matters: Essential for generating new reference numbers sequentially.
|
|
112
|
+
Setup summary: Provide text with multiple references, assert max value returned.
|
|
113
|
+
"""
|
|
114
|
+
# Arrange
|
|
115
|
+
from unique_toolkit._common.referencing import get_max_ref_number
|
|
116
|
+
|
|
117
|
+
text = "Text<sup>5</sup> with <sup>10</sup> multiple<sup>2</sup> refs."
|
|
118
|
+
|
|
119
|
+
# Act
|
|
120
|
+
result = get_max_ref_number(text)
|
|
121
|
+
|
|
122
|
+
# Assert
|
|
123
|
+
assert result == 10
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@pytest.mark.ai
|
|
127
|
+
def test_get_max_ref_number__returns_none__with_no_refs() -> None:
|
|
128
|
+
"""
|
|
129
|
+
Purpose: Verify get_max_ref_number returns None for text without references.
|
|
130
|
+
Why this matters: Enables proper handling of unreferenced text.
|
|
131
|
+
Setup summary: Provide text without references, assert None returned.
|
|
132
|
+
"""
|
|
133
|
+
# Arrange
|
|
134
|
+
from unique_toolkit._common.referencing import get_max_ref_number
|
|
135
|
+
|
|
136
|
+
text = "Text without any references."
|
|
137
|
+
|
|
138
|
+
# Act
|
|
139
|
+
result = get_max_ref_number(text)
|
|
140
|
+
|
|
141
|
+
# Assert
|
|
142
|
+
assert result is None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@pytest.mark.ai
|
|
146
|
+
def test_replace_ref_number__replaces_with_int__single_occurrence() -> None:
|
|
147
|
+
"""
|
|
148
|
+
Purpose: Verify replace_ref_number replaces reference with new integer.
|
|
149
|
+
Why this matters: Enables reference renumbering during text processing.
|
|
150
|
+
Setup summary: Replace one reference with integer, assert proper substitution.
|
|
151
|
+
"""
|
|
152
|
+
# Arrange
|
|
153
|
+
from unique_toolkit._common.referencing import replace_ref_number
|
|
154
|
+
|
|
155
|
+
text = "Some text<sup>1</sup> here."
|
|
156
|
+
|
|
157
|
+
# Act
|
|
158
|
+
result = replace_ref_number(text, ref_number=1, replacement=5)
|
|
159
|
+
|
|
160
|
+
# Assert
|
|
161
|
+
assert result == "Some text<sup>5</sup> here."
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@pytest.mark.ai
|
|
165
|
+
def test_replace_ref_number__replaces_with_string__single_occurrence() -> None:
|
|
166
|
+
"""
|
|
167
|
+
Purpose: Verify replace_ref_number can replace with arbitrary string.
|
|
168
|
+
Why this matters: Allows flexible reference transformation beyond renumbering.
|
|
169
|
+
Setup summary: Replace reference with string, assert exact substitution.
|
|
170
|
+
"""
|
|
171
|
+
# Arrange
|
|
172
|
+
from unique_toolkit._common.referencing import replace_ref_number
|
|
173
|
+
|
|
174
|
+
text = "Some text<sup>1</sup> here."
|
|
175
|
+
|
|
176
|
+
# Act
|
|
177
|
+
result = replace_ref_number(text, ref_number=1, replacement="[REF]")
|
|
178
|
+
|
|
179
|
+
# Assert
|
|
180
|
+
assert result == "Some text[REF] here."
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@pytest.mark.ai
|
|
184
|
+
def test_replace_ref_number__replaces_all_occurrences__of_same_number() -> None:
|
|
185
|
+
"""
|
|
186
|
+
Purpose: Verify replace_ref_number replaces all instances of target reference.
|
|
187
|
+
Why this matters: Ensures consistent reference updates throughout text.
|
|
188
|
+
Setup summary: Provide text with duplicate reference, assert all replaced.
|
|
189
|
+
"""
|
|
190
|
+
# Arrange
|
|
191
|
+
from unique_toolkit._common.referencing import replace_ref_number
|
|
192
|
+
|
|
193
|
+
text = "Text<sup>2</sup> with <sup>2</sup> duplicate refs<sup>2</sup>."
|
|
194
|
+
|
|
195
|
+
# Act
|
|
196
|
+
result = replace_ref_number(text, ref_number=2, replacement=9)
|
|
197
|
+
|
|
198
|
+
# Assert
|
|
199
|
+
assert result == "Text<sup>9</sup> with <sup>9</sup> duplicate refs<sup>9</sup>."
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@pytest.mark.ai
|
|
203
|
+
def test_replace_ref_number__preserves_other_refs__when_replacing() -> None:
|
|
204
|
+
"""
|
|
205
|
+
Purpose: Verify replace_ref_number only modifies target reference number.
|
|
206
|
+
Why this matters: Prevents unintended side effects on other references.
|
|
207
|
+
Setup summary: Replace one reference among many, assert others unchanged.
|
|
208
|
+
"""
|
|
209
|
+
# Arrange
|
|
210
|
+
from unique_toolkit._common.referencing import replace_ref_number
|
|
211
|
+
|
|
212
|
+
text = "Text<sup>1</sup> with <sup>2</sup> and <sup>3</sup>."
|
|
213
|
+
|
|
214
|
+
# Act
|
|
215
|
+
result = replace_ref_number(text, ref_number=2, replacement=7)
|
|
216
|
+
|
|
217
|
+
# Assert
|
|
218
|
+
assert result == "Text<sup>1</sup> with <sup>7</sup> and <sup>3</sup>."
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@pytest.mark.ai
|
|
222
|
+
def test_replace_ref_number__handles_whitespace__in_target_ref() -> None:
|
|
223
|
+
"""
|
|
224
|
+
Purpose: Verify replace_ref_number matches references with internal whitespace.
|
|
225
|
+
Why this matters: Ensures robust matching of malformed HTML.
|
|
226
|
+
Setup summary: Replace reference with whitespace, assert successful replacement.
|
|
227
|
+
"""
|
|
228
|
+
# Arrange
|
|
229
|
+
from unique_toolkit._common.referencing import replace_ref_number
|
|
230
|
+
|
|
231
|
+
text = "Text<sup> 3 </sup> with spacing."
|
|
232
|
+
|
|
233
|
+
# Act
|
|
234
|
+
result = replace_ref_number(text, ref_number=3, replacement=5)
|
|
235
|
+
|
|
236
|
+
# Assert
|
|
237
|
+
assert result == "Text<sup>5</sup> with spacing."
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@pytest.mark.ai
|
|
241
|
+
def test_remove_ref_number__removes_single_ref__completely() -> None:
|
|
242
|
+
"""
|
|
243
|
+
Purpose: Verify remove_ref_number deletes target reference from text.
|
|
244
|
+
Why this matters: Enables reference cleanup during text processing.
|
|
245
|
+
Setup summary: Remove one reference, assert it's deleted without trace.
|
|
246
|
+
"""
|
|
247
|
+
# Arrange
|
|
248
|
+
from unique_toolkit._common.referencing import remove_ref_number
|
|
249
|
+
|
|
250
|
+
text = "Some text<sup>1</sup> here."
|
|
251
|
+
|
|
252
|
+
# Act
|
|
253
|
+
result = remove_ref_number(text, ref_number=1)
|
|
254
|
+
|
|
255
|
+
# Assert
|
|
256
|
+
assert result == "Some text here."
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@pytest.mark.ai
|
|
260
|
+
def test_remove_ref_number__removes_all_occurrences__of_same_number() -> None:
|
|
261
|
+
"""
|
|
262
|
+
Purpose: Verify remove_ref_number removes all instances of target reference.
|
|
263
|
+
Why this matters: Ensures complete cleanup of duplicate references.
|
|
264
|
+
Setup summary: Remove reference appearing multiple times, assert all removed.
|
|
265
|
+
"""
|
|
266
|
+
# Arrange
|
|
267
|
+
from unique_toolkit._common.referencing import remove_ref_number
|
|
268
|
+
|
|
269
|
+
text = "Text<sup>2</sup> with <sup>2</sup> duplicate<sup>2</sup>."
|
|
270
|
+
|
|
271
|
+
# Act
|
|
272
|
+
result = remove_ref_number(text, ref_number=2)
|
|
273
|
+
|
|
274
|
+
# Assert
|
|
275
|
+
assert result == "Text with duplicate."
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@pytest.mark.ai
|
|
279
|
+
def test_remove_ref_number__preserves_other_refs__when_removing() -> None:
|
|
280
|
+
"""
|
|
281
|
+
Purpose: Verify remove_ref_number only deletes target reference number.
|
|
282
|
+
Why this matters: Prevents accidental deletion of other references.
|
|
283
|
+
Setup summary: Remove one reference among many, assert others remain.
|
|
284
|
+
"""
|
|
285
|
+
# Arrange
|
|
286
|
+
from unique_toolkit._common.referencing import remove_ref_number
|
|
287
|
+
|
|
288
|
+
text = "Text<sup>1</sup> with <sup>2</sup> and <sup>3</sup>."
|
|
289
|
+
|
|
290
|
+
# Act
|
|
291
|
+
result = remove_ref_number(text, ref_number=2)
|
|
292
|
+
|
|
293
|
+
# Assert
|
|
294
|
+
assert result == "Text<sup>1</sup> with and <sup>3</sup>."
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@pytest.mark.ai
|
|
298
|
+
def test_remove_all_refs__removes_all_references__from_text() -> None:
|
|
299
|
+
"""
|
|
300
|
+
Purpose: Verify remove_all_refs strips all sup tags from text.
|
|
301
|
+
Why this matters: Enables generation of clean, unreferenced text copies.
|
|
302
|
+
Setup summary: Provide text with multiple references, assert all removed.
|
|
303
|
+
"""
|
|
304
|
+
# Arrange
|
|
305
|
+
from unique_toolkit._common.referencing import remove_all_refs
|
|
306
|
+
|
|
307
|
+
text = "Text<sup>1</sup> with <sup>2</sup> multiple<sup>3</sup> refs."
|
|
308
|
+
|
|
309
|
+
# Act
|
|
310
|
+
result = remove_all_refs(text)
|
|
311
|
+
|
|
312
|
+
# Assert
|
|
313
|
+
assert result == "Text with multiple refs."
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@pytest.mark.ai
|
|
317
|
+
def test_remove_all_refs__returns_unchanged__with_no_refs() -> None:
|
|
318
|
+
"""
|
|
319
|
+
Purpose: Verify remove_all_refs handles unreferenced text correctly.
|
|
320
|
+
Why this matters: Ensures function is safe to call on any text.
|
|
321
|
+
Setup summary: Provide text without references, assert unchanged.
|
|
322
|
+
"""
|
|
323
|
+
# Arrange
|
|
324
|
+
from unique_toolkit._common.referencing import remove_all_refs
|
|
325
|
+
|
|
326
|
+
text = "Plain text without references."
|
|
327
|
+
|
|
328
|
+
# Act
|
|
329
|
+
result = remove_all_refs(text)
|
|
330
|
+
|
|
331
|
+
# Assert
|
|
332
|
+
assert result == "Plain text without references."
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@pytest.mark.ai
|
|
336
|
+
def test_remove_all_refs__handles_whitespace__in_all_refs() -> None:
|
|
337
|
+
"""
|
|
338
|
+
Purpose: Verify remove_all_refs handles references with internal whitespace.
|
|
339
|
+
Why this matters: Ensures robust cleanup of malformed HTML.
|
|
340
|
+
Setup summary: Provide references with spacing, assert all removed.
|
|
341
|
+
"""
|
|
342
|
+
# Arrange
|
|
343
|
+
from unique_toolkit._common.referencing import remove_all_refs
|
|
344
|
+
|
|
345
|
+
text = "Text<sup> 1 </sup> with <sup> 2 </sup> spaced."
|
|
346
|
+
|
|
347
|
+
# Act
|
|
348
|
+
result = remove_all_refs(text)
|
|
349
|
+
|
|
350
|
+
# Assert
|
|
351
|
+
assert result == "Text with spaced."
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
@pytest.mark.ai
|
|
355
|
+
def test_remove_consecutive_ref_space__removes_spaces__between_refs() -> None:
|
|
356
|
+
"""
|
|
357
|
+
Purpose: Verify remove_consecutive_ref_space collapses spaces between adjacent refs.
|
|
358
|
+
Why this matters: Improves visual formatting of multiple citations.
|
|
359
|
+
Setup summary: Provide consecutive references with spaces, assert spaces removed.
|
|
360
|
+
"""
|
|
361
|
+
# Arrange
|
|
362
|
+
from unique_toolkit._common.referencing import remove_consecutive_ref_space
|
|
363
|
+
|
|
364
|
+
text = "Text<sup>1</sup> <sup>2</sup> with spaces."
|
|
365
|
+
|
|
366
|
+
# Act
|
|
367
|
+
result = remove_consecutive_ref_space(text)
|
|
368
|
+
|
|
369
|
+
# Assert
|
|
370
|
+
assert result == "Text<sup>1</sup><sup>2</sup> with spaces."
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
@pytest.mark.ai
|
|
374
|
+
def test_remove_consecutive_ref_space__handles_multiple_spaces__between_refs() -> None:
|
|
375
|
+
"""
|
|
376
|
+
Purpose: Verify remove_consecutive_ref_space handles multiple spaces.
|
|
377
|
+
Why this matters: Handles various whitespace formatting scenarios.
|
|
378
|
+
Setup summary: Provide references with multiple spaces, assert collapsed.
|
|
379
|
+
"""
|
|
380
|
+
# Arrange
|
|
381
|
+
from unique_toolkit._common.referencing import remove_consecutive_ref_space
|
|
382
|
+
|
|
383
|
+
text = "Text<sup>1</sup> <sup>2</sup> and <sup>3</sup> <sup>4</sup>."
|
|
384
|
+
|
|
385
|
+
# Act
|
|
386
|
+
result = remove_consecutive_ref_space(text)
|
|
387
|
+
|
|
388
|
+
# Assert
|
|
389
|
+
assert result == "Text<sup>1</sup><sup>2</sup> and <sup>3</sup><sup>4</sup>."
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
@pytest.mark.ai
|
|
393
|
+
def test_remove_consecutive_ref_space__preserves_other_spaces__in_text() -> None:
|
|
394
|
+
"""
|
|
395
|
+
Purpose: Verify remove_consecutive_ref_space only affects inter-reference spaces.
|
|
396
|
+
Why this matters: Prevents unwanted text formatting changes.
|
|
397
|
+
Setup summary: Provide text with various spaces, assert only ref spaces removed.
|
|
398
|
+
"""
|
|
399
|
+
# Arrange
|
|
400
|
+
from unique_toolkit._common.referencing import remove_consecutive_ref_space
|
|
401
|
+
|
|
402
|
+
text = "Some text<sup>1</sup> <sup>2</sup> has many spaces."
|
|
403
|
+
|
|
404
|
+
# Act
|
|
405
|
+
result = remove_consecutive_ref_space(text)
|
|
406
|
+
|
|
407
|
+
# Assert
|
|
408
|
+
assert result == "Some text<sup>1</sup><sup>2</sup> has many spaces."
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
@pytest.mark.ai
|
|
412
|
+
def test_remove_consecutive_ref_space__returns_unchanged__without_consecutive_refs() -> (
|
|
413
|
+
None
|
|
414
|
+
):
|
|
415
|
+
"""
|
|
416
|
+
Purpose: Verify remove_consecutive_ref_space leaves non-consecutive refs unchanged.
|
|
417
|
+
Why this matters: Ensures function only modifies adjacent references.
|
|
418
|
+
Setup summary: Provide non-adjacent references, assert unchanged.
|
|
419
|
+
"""
|
|
420
|
+
# Arrange
|
|
421
|
+
from unique_toolkit._common.referencing import remove_consecutive_ref_space
|
|
422
|
+
|
|
423
|
+
text = "Text<sup>1</sup> words <sup>2</sup> between."
|
|
424
|
+
|
|
425
|
+
# Act
|
|
426
|
+
result = remove_consecutive_ref_space(text)
|
|
427
|
+
|
|
428
|
+
# Assert
|
|
429
|
+
assert result == "Text<sup>1</sup> words <sup>2</sup> between."
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
@pytest.mark.ai
|
|
433
|
+
@pytest.mark.parametrize(
|
|
434
|
+
"ref_number, expected",
|
|
435
|
+
[
|
|
436
|
+
(0, "<sup>0</sup>"),
|
|
437
|
+
(1, "<sup>1</sup>"),
|
|
438
|
+
(99, "<sup>99</sup>"),
|
|
439
|
+
(999, "<sup>999</sup>"),
|
|
440
|
+
],
|
|
441
|
+
ids=["zero", "single-digit", "double-digit", "triple-digit"],
|
|
442
|
+
)
|
|
443
|
+
def test_get_reference_pattern__handles_various_numbers(
|
|
444
|
+
ref_number: int, expected: str
|
|
445
|
+
) -> None:
|
|
446
|
+
"""
|
|
447
|
+
Purpose: Verify get_reference_pattern works for various number ranges.
|
|
448
|
+
Why this matters: Ensures robustness across different reference counts.
|
|
449
|
+
Setup summary: Parametrized test with different ref numbers.
|
|
450
|
+
"""
|
|
451
|
+
# Arrange
|
|
452
|
+
from unique_toolkit._common.referencing import get_reference_pattern
|
|
453
|
+
|
|
454
|
+
# Act
|
|
455
|
+
result = get_reference_pattern(ref_number)
|
|
456
|
+
|
|
457
|
+
# Assert
|
|
458
|
+
assert result == expected
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
@pytest.mark.ai
|
|
462
|
+
@pytest.mark.parametrize(
|
|
463
|
+
"text, expected",
|
|
464
|
+
[
|
|
465
|
+
("No refs here", []),
|
|
466
|
+
("<sup>1</sup>", [1]),
|
|
467
|
+
("<sup>5</sup><sup>3</sup><sup>1</sup>", [1, 3, 5]),
|
|
468
|
+
("<sup>1</sup>text<sup>1</sup>", [1]),
|
|
469
|
+
("<sup>10</sup><sup>20</sup><sup>5</sup>", [5, 10, 20]),
|
|
470
|
+
],
|
|
471
|
+
ids=[
|
|
472
|
+
"no-refs",
|
|
473
|
+
"single-ref",
|
|
474
|
+
"multiple-unordered",
|
|
475
|
+
"duplicate-refs",
|
|
476
|
+
"multi-digit",
|
|
477
|
+
],
|
|
478
|
+
)
|
|
479
|
+
def test_get_all_ref_numbers__various_text_formats(
|
|
480
|
+
text: str, expected: list[int]
|
|
481
|
+
) -> None:
|
|
482
|
+
"""
|
|
483
|
+
Purpose: Table-driven test for reference extraction across formats.
|
|
484
|
+
Why this matters: Ensures consistent behavior across different text patterns.
|
|
485
|
+
Setup summary: Parametrized inputs with expected sorted unique results.
|
|
486
|
+
"""
|
|
487
|
+
# Arrange
|
|
488
|
+
from unique_toolkit._common.referencing import get_all_ref_numbers
|
|
489
|
+
|
|
490
|
+
# Act
|
|
491
|
+
result = get_all_ref_numbers(text)
|
|
492
|
+
|
|
493
|
+
# Assert
|
|
494
|
+
assert result == expected
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
@pytest.mark.ai
|
|
498
|
+
@pytest.mark.parametrize(
|
|
499
|
+
"text, expected",
|
|
500
|
+
[
|
|
501
|
+
("No refs", None),
|
|
502
|
+
("<sup>1</sup>", 1),
|
|
503
|
+
("<sup>100</sup><sup>50</sup><sup>25</sup>", 100),
|
|
504
|
+
("<sup>5</sup> and <sup>10</sup>", 10),
|
|
505
|
+
],
|
|
506
|
+
ids=["no-refs-returns-none", "single-ref", "unordered-multi-digit", "simple-multi"],
|
|
507
|
+
)
|
|
508
|
+
def test_get_max_ref_number__various_scenarios(text: str, expected: int | None) -> None:
|
|
509
|
+
"""
|
|
510
|
+
Purpose: Table-driven test for max reference number extraction.
|
|
511
|
+
Why this matters: Validates max finding logic across edge cases.
|
|
512
|
+
Setup summary: Parametrized test with various reference patterns.
|
|
513
|
+
"""
|
|
514
|
+
# Arrange
|
|
515
|
+
from unique_toolkit._common.referencing import get_max_ref_number
|
|
516
|
+
|
|
517
|
+
# Act
|
|
518
|
+
result = get_max_ref_number(text)
|
|
519
|
+
|
|
520
|
+
# Assert
|
|
521
|
+
assert result == expected
|