vectorwave 0.1.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. tests/__init__.py +0 -0
  2. tests/batch/__init__.py +0 -0
  3. tests/batch/test_batch.py +98 -0
  4. tests/core/__init__.py +0 -0
  5. tests/core/test_decorator.py +345 -0
  6. tests/database/__init__.py +0 -0
  7. tests/database/test_db.py +468 -0
  8. tests/database/test_db_search.py +163 -0
  9. tests/exception/__init__.py +0 -0
  10. tests/models/__init__.py +0 -0
  11. tests/models/test_db_config.py +152 -0
  12. tests/monitoring/__init__.py +0 -0
  13. tests/monitoring/test_tracer.py +202 -0
  14. tests/prediction/__init__.py +0 -0
  15. tests/vectorizer/__init__.py +0 -0
  16. vectorwave/__init__.py +13 -0
  17. vectorwave/batch/__init__.py +0 -0
  18. vectorwave/batch/batch.py +68 -0
  19. vectorwave/core/__init__.py +0 -0
  20. vectorwave/core/core.py +0 -0
  21. vectorwave/core/decorator.py +131 -0
  22. vectorwave/database/__init__.py +0 -0
  23. vectorwave/database/db.py +328 -0
  24. vectorwave/database/db_search.py +122 -0
  25. vectorwave/exception/__init__.py +0 -0
  26. vectorwave/exception/exceptions.py +22 -0
  27. vectorwave/models/__init__.py +0 -0
  28. vectorwave/models/db_config.py +92 -0
  29. vectorwave/monitoring/__init__.py +0 -0
  30. vectorwave/monitoring/monitoring.py +0 -0
  31. vectorwave/monitoring/tracer.py +131 -0
  32. vectorwave/prediction/__init__.py +0 -0
  33. vectorwave/prediction/predictor.py +0 -0
  34. vectorwave/vectorizer/__init__.py +0 -0
  35. vectorwave/vectorizer/base.py +12 -0
  36. vectorwave/vectorizer/factory.py +49 -0
  37. vectorwave/vectorizer/huggingface_vectorizer.py +33 -0
  38. vectorwave/vectorizer/openai_vectorizer.py +35 -0
  39. vectorwave-0.1.3.dist-info/METADATA +352 -0
  40. vectorwave-0.1.3.dist-info/RECORD +44 -0
  41. vectorwave-0.1.3.dist-info/WHEEL +5 -0
  42. vectorwave-0.1.3.dist-info/licenses/LICENSE +21 -0
  43. vectorwave-0.1.3.dist-info/licenses/NOTICE +31 -0
  44. vectorwave-0.1.3.dist-info/top_level.txt +2 -0
tests/__init__.py ADDED
File without changes
File without changes
@@ -0,0 +1,98 @@
1
+ from unittest.mock import MagicMock
2
+
3
+ import pytest
4
+ from vectorwave.batch.batch import get_batch_manager
5
+ from vectorwave.exception.exceptions import WeaviateConnectionError
6
+ from vectorwave.models.db_config import WeaviateSettings
7
+
8
+
9
+ @pytest.fixture
10
+ def mock_deps(monkeypatch):
11
+ """
12
+ Fixture to mock dependencies for batch.py (db, config, atexit)
13
+ """
14
+ # Mock WeaviateClient
15
+ mock_client = MagicMock()
16
+ mock_client.batch = MagicMock()
17
+ mock_client.batch.configure = MagicMock()
18
+ mock_client.batch.add_object = MagicMock()
19
+ mock_client.batch.flush = MagicMock()
20
+
21
+ mock_collection_data = MagicMock()
22
+ mock_collection = MagicMock()
23
+ mock_collection.data = mock_collection_data
24
+ mock_client.collections.get = MagicMock(return_value=mock_collection)
25
+
26
+ # Mock get_weaviate_client
27
+ mock_get_client = MagicMock(return_value=mock_client)
28
+ monkeypatch.setattr("vectorwave.batch.batch.get_weaviate_client", mock_get_client)
29
+
30
+ # Mock get_weaviate_settings
31
+ mock_settings = WeaviateSettings()
32
+ mock_get_settings = MagicMock(return_value=mock_settings)
33
+ monkeypatch.setattr("vectorwave.batch.batch.get_weaviate_settings", mock_get_settings)
34
+
35
+ # Mock atexit.register
36
+ mock_atexit_register = MagicMock()
37
+ monkeypatch.setattr("atexit.register", mock_atexit_register)
38
+
39
+ # Clear lru_cache
40
+ get_batch_manager.cache_clear()
41
+
42
+ return {
43
+ "get_client": mock_get_client,
44
+ "get_settings": mock_get_settings,
45
+ "client": mock_client,
46
+ "settings": mock_settings,
47
+ "atexit": mock_atexit_register
48
+ }
49
+
50
+ def test_get_batch_manager_is_singleton(mock_deps):
51
+ """
52
+ Case 1: Test if get_batch_manager() always returns the same instance (singleton)
53
+ """
54
+ manager1 = get_batch_manager()
55
+ manager2 = get_batch_manager()
56
+ assert manager1 is manager2
57
+
58
+ def test_batch_manager_initialization(mock_deps):
59
+ """
60
+ Case 2: Test if BatchManager correctly calls dependencies (configure, atexit) upon initialization
61
+ """
62
+ manager = get_batch_manager()
63
+
64
+ mock_deps["get_settings"].assert_called_once()
65
+ mock_deps["get_client"].assert_called_once_with(mock_deps["settings"])
66
+
67
+ assert manager._initialized is True
68
+
69
+ def test_batch_manager_init_failure(monkeypatch):
70
+ """
71
+ Case 3: Test if _initialized remains False when DB connection (get_weaviate_client) fails
72
+ """
73
+ # Mock get_weaviate_client to raise an exception
74
+ mock_get_client_fail = MagicMock(side_effect=WeaviateConnectionError("Test connection error"))
75
+ monkeypatch.setattr("vectorwave.batch.batch.get_weaviate_client", mock_get_client_fail)
76
+
77
+ get_batch_manager.cache_clear()
78
+ manager = get_batch_manager()
79
+
80
+ # The _initialized flag should be False if initialization fails
81
+ assert manager._initialized is False
82
+
83
+ def test_add_object_calls_client_batch(mock_deps):
84
+ """
85
+ Case 4: Test if add_object() correctly calls client.batch.add_object
86
+ """
87
+ manager = get_batch_manager()
88
+ props = {"key": "value"}
89
+
90
+ manager.add_object(collection="TestCollection", properties=props, uuid="test-uuid")
91
+
92
+ mock_deps["client"].collections.get.assert_called_once_with("TestCollection")
93
+
94
+ mock_deps["client"].collections.get.return_value.data.insert.assert_called_once_with(
95
+ properties=props,
96
+ uuid="test-uuid",
97
+ vector=None
98
+ )
tests/core/__init__.py ADDED
File without changes
@@ -0,0 +1,345 @@
1
+ import pytest
2
+ from unittest.mock import patch, MagicMock
3
+ import inspect
4
+ from weaviate.util import generate_uuid5
5
+
6
+ from vectorwave.core.decorator import vectorize
7
+ from vectorwave.models.db_config import WeaviateSettings
8
+
9
+ from vectorwave.batch.batch import get_batch_manager as real_get_batch_manager
10
+ from vectorwave.database.db import get_cached_client as real_get_cached_client
11
+ from vectorwave.models.db_config import get_weaviate_settings as real_get_settings
12
+
13
+
14
+ @pytest.fixture
15
+ def mock_decorator_deps(monkeypatch):
16
+ """
17
+ Mocks dependencies for decorator.py (get_batch_manager, get_weaviate_settings)
18
+ """
19
+ # 1. Mock BatchManager
20
+ mock_batch_manager = MagicMock()
21
+ mock_batch_manager.add_object = MagicMock()
22
+ mock_get_batch_manager = MagicMock(return_value=mock_batch_manager)
23
+
24
+ # 2. Mock Settings
25
+ mock_custom_props = {
26
+ "run_id": {"data_type": "TEXT"},
27
+ "team": {"data_type": "TEXT"},
28
+ "priority": {"data_type": "INT"}
29
+ }
30
+ mock_settings = WeaviateSettings(
31
+ COLLECTION_NAME="TestFunctions",
32
+ EXECUTION_COLLECTION_NAME="TestExecutions",
33
+ custom_properties=mock_custom_props,
34
+ global_custom_values={"run_id": "test-run-abc"}
35
+ )
36
+ mock_get_settings = MagicMock(return_value=mock_settings)
37
+
38
+ mock_client = MagicMock()
39
+ mock_get_client = MagicMock(return_value=mock_client)
40
+
41
+
42
+ # --- decorator.py ---
43
+ monkeypatch.setattr("vectorwave.core.decorator.get_batch_manager", mock_get_batch_manager)
44
+ monkeypatch.setattr("vectorwave.core.decorator.get_weaviate_settings", mock_get_settings)
45
+
46
+ # --- tracer.py ---
47
+ monkeypatch.setattr("vectorwave.monitoring.tracer.get_batch_manager", mock_get_batch_manager)
48
+ monkeypatch.setattr("vectorwave.monitoring.tracer.get_weaviate_settings", mock_get_settings)
49
+
50
+ # --- batch.py (tracer가 get_batch_manager()를 호출할 때 사용) ---
51
+ # WeaviateBatchManager.__init__이 실패하지 않도록 패치
52
+ monkeypatch.setattr("vectorwave.batch.batch.get_weaviate_client", mock_get_client)
53
+ monkeypatch.setattr("vectorwave.batch.batch.get_weaviate_settings", mock_get_settings)
54
+
55
+ # 5. Clear caches to ensure mocks are used
56
+ real_get_batch_manager.cache_clear()
57
+ real_get_cached_client.cache_clear()
58
+ real_get_settings.cache_clear()
59
+
60
+ return {
61
+ "get_batch": mock_get_batch_manager,
62
+ "get_settings": mock_get_settings,
63
+ "batch": mock_batch_manager,
64
+ "settings": mock_settings
65
+ }
66
+
67
+
68
+ @pytest.fixture
69
+ def mock_decorator_deps_no_props(monkeypatch):
70
+ """
71
+ Fixture variant where settings.custom_properties is None
72
+ """
73
+ # 1. Mock BatchManager
74
+ mock_batch_manager = MagicMock()
75
+ mock_batch_manager.add_object = MagicMock()
76
+ mock_get_batch_manager = MagicMock(return_value=mock_batch_manager)
77
+
78
+ # 2. Mock Settings (custom_properties=None)
79
+ mock_settings = WeaviateSettings(
80
+ COLLECTION_NAME="TestFunctions",
81
+ EXECUTION_COLLECTION_NAME="TestExecutions",
82
+ custom_properties=None, # <-- No properties loaded
83
+ global_custom_values=None # <-- No globals
84
+ )
85
+ mock_get_settings = MagicMock(return_value=mock_settings)
86
+
87
+ mock_client = MagicMock()
88
+ mock_get_client = MagicMock(return_value=mock_client)
89
+
90
+ monkeypatch.setattr("vectorwave.core.decorator.get_batch_manager", mock_get_batch_manager)
91
+ monkeypatch.setattr("vectorwave.core.decorator.get_weaviate_settings", mock_get_settings)
92
+ monkeypatch.setattr("vectorwave.monitoring.tracer.get_batch_manager", mock_get_batch_manager)
93
+ monkeypatch.setattr("vectorwave.monitoring.tracer.get_weaviate_settings", mock_get_settings)
94
+ monkeypatch.setattr("vectorwave.batch.batch.get_weaviate_client", mock_get_client)
95
+ monkeypatch.setattr("vectorwave.batch.batch.get_weaviate_settings", mock_get_settings)
96
+
97
+ # 5. Clear caches
98
+ real_get_batch_manager.cache_clear()
99
+ real_get_cached_client.cache_clear()
100
+ real_get_settings.cache_clear()
101
+
102
+ return {
103
+ "get_batch": mock_get_batch_manager,
104
+ "get_settings": mock_get_settings,
105
+ "batch": mock_batch_manager,
106
+ "settings": mock_settings
107
+ }
108
+
109
+
110
+ def test_vectorize_static_data_collection(mock_decorator_deps):
111
+ """
112
+ Case 1: Test if data is added once to 'VectorWaveFunctions' (static) when the decorator is loaded
113
+ """
114
+ mock_batch = mock_decorator_deps["batch"]
115
+ mock_settings = mock_decorator_deps["settings"]
116
+
117
+ @vectorize(
118
+ search_description="Test search desc",
119
+ sequence_narrative="Test sequence narr"
120
+ )
121
+ def my_test_function_static():
122
+ """My test docstring"""
123
+ pass
124
+
125
+ # --- ----------------- ---
126
+
127
+ # 1. Assert: get_batch_manager and get_weaviate_settings are called at load time
128
+ mock_decorator_deps["get_batch"].assert_called_once()
129
+ # (get_weaviate_settings might have already been called once during batch initialization,
130
+ # so check 'called' instead of 'call_count')
131
+ assert mock_decorator_deps["get_settings"].called
132
+
133
+ # 2. Assert: batch.add_object is called once
134
+ mock_batch.add_object.assert_called_once()
135
+
136
+ # 3. Assert: Check if the call arguments are for the 'VectorWaveFunctions' collection
137
+ args, kwargs = mock_batch.add_object.call_args
138
+
139
+ assert kwargs["collection"] == mock_settings.COLLECTION_NAME
140
+ assert kwargs["properties"]["function_name"] == "my_test_function_static"
141
+ assert kwargs["properties"]["docstring"] == "My test docstring"
142
+ assert "def my_test_function_static" in kwargs["properties"]["source_code"]
143
+ assert kwargs["properties"]["search_description"] == "Test search desc"
144
+ assert kwargs["properties"]["sequence_narrative"] == "Test sequence narr"
145
+
146
+
147
+ def test_vectorize_dynamic_data_logging_success(mock_decorator_deps):
148
+ """
149
+ Case 2: Test if the decorated function adds a log to 'VectorWaveExecutions' (dynamic) on 'successful' execution
150
+ """
151
+ mock_batch = mock_decorator_deps["batch"]
152
+ mock_settings = mock_decorator_deps["settings"]
153
+
154
+ @vectorize(search_description="Test", sequence_narrative="Test")
155
+ def my_test_function_dynamic():
156
+ return "Success"
157
+
158
+ result = my_test_function_dynamic()
159
+
160
+ # 1. Assert: Function returns the result normally
161
+ assert result == "Success"
162
+
163
+ # 2. Assert: add_object is called 2 times in total (1 static + 1 dynamic)
164
+ assert mock_batch.add_object.call_count == 2
165
+
166
+ # 3. Assert: Check arguments of the last call (dynamic log)
167
+ args, kwargs = mock_batch.add_object.call_args
168
+
169
+ assert kwargs["collection"] == mock_settings.EXECUTION_COLLECTION_NAME
170
+ assert kwargs["properties"]["status"] == "SUCCESS"
171
+ assert kwargs["properties"]["error_message"] is None
172
+ assert kwargs["properties"]["duration_ms"] > 0
173
+ # Check if global_custom_values (run_id) were merged
174
+ assert kwargs["properties"]["run_id"] == "test-run-abc"
175
+
176
+
177
+ def test_vectorize_dynamic_data_logging_failure(mock_decorator_deps):
178
+ """
179
+ Case 3: Test if the decorated function adds a 'status=ERROR' log on 'failed' execution
180
+ """
181
+ mock_batch = mock_decorator_deps["batch"]
182
+ mock_settings = mock_decorator_deps["settings"]
183
+
184
+ @vectorize(search_description="FailTest", sequence_narrative="FailTest")
185
+ def my_failing_function():
186
+ raise ValueError("This is a test error")
187
+
188
+ with pytest.raises(ValueError, match="This is a test error"):
189
+ my_failing_function()
190
+
191
+ # 1. Assert: add_object is called 2 times in total (1 static + 1 dynamic)
192
+ assert mock_batch.add_object.call_count == 2
193
+
194
+ # 2. Assert: Check arguments of the last call (dynamic log)
195
+ args, kwargs = mock_batch.add_object.call_args
196
+
197
+ assert kwargs["collection"] == mock_settings.EXECUTION_COLLECTION_NAME
198
+ assert kwargs["properties"]["status"] == "ERROR"
199
+ assert "ValueError: This is a test error" in kwargs["properties"]["error_message"]
200
+ assert "Traceback (most recent call last):" in kwargs["properties"]["error_message"]
201
+ assert kwargs["properties"]["run_id"] == "test-run-abc"
202
+
203
+
204
+ def test_vectorize_dynamic_data_with_execution_tags(mock_decorator_deps):
205
+ """
206
+ Case 4: Test if execution logs correctly merge global tags (run_id) and
207
+ function-specific tags (team, priority) provided via **kwargs.
208
+ """
209
+ mock_batch = mock_decorator_deps["batch"]
210
+ mock_settings = mock_decorator_deps["settings"]
211
+
212
+ # 1. Arrange: Define a function with function-specific execution tags
213
+ @vectorize(
214
+ search_description="Test with specific tags",
215
+ sequence_narrative="Tags should be merged",
216
+ team="backend", # <-- Function-specific tag
217
+ priority=1 # <-- Function-specific tag
218
+ )
219
+ def my_tagged_function():
220
+ return "Tagged success"
221
+
222
+ # 2. Act: Execute the decorated function
223
+ result = my_tagged_function()
224
+
225
+ # 3. Assert: Basic execution
226
+ assert result == "Tagged success"
227
+ # (1 static call + 1 dynamic call)
228
+ assert mock_batch.add_object.call_count == 2
229
+
230
+ # 4. Assert: Check the properties of the dynamic log (the last call)
231
+ args, kwargs = mock_batch.add_object.call_args
232
+
233
+ assert kwargs["collection"] == mock_settings.EXECUTION_COLLECTION_NAME
234
+ assert kwargs["properties"]["status"] == "SUCCESS"
235
+
236
+ # 4a. Verify Global Tag (from fixture)
237
+ assert kwargs["properties"]["run_id"] == "test-run-abc"
238
+
239
+ # 4b. Verify Function-Specific Tags (from @vectorize)
240
+ assert kwargs["properties"]["team"] == "backend"
241
+ assert kwargs["properties"]["priority"] == 1
242
+
243
+
244
+ def test_vectorize_execution_tags_override_global_tags(mock_decorator_deps):
245
+ """
246
+ Case 5: Test if a function-specific tag (e.g., 'run_id')
247
+ correctly overrides a global tag with the same name.
248
+ """
249
+ mock_batch = mock_decorator_deps["batch"]
250
+
251
+ # 1. Arrange: Define function where 'run_id' will override the global value
252
+ # The fixture provides a global "run_id": "test-run-abc"
253
+ @vectorize(
254
+ search_description="Test override",
255
+ sequence_narrative="Next",
256
+ run_id="override-run-xyz" # <-- This should WIN against "test-run-abc"
257
+ )
258
+ def my_override_function():
259
+ pass
260
+
261
+ # 2. Act
262
+ my_override_function()
263
+
264
+ # 3. Assert
265
+ args, kwargs = mock_batch.add_object.call_args
266
+
267
+ # Verify that the function-specific 'run_id' overrode the global one
268
+ assert kwargs["properties"]["run_id"] == "override-run-xyz"
269
+
270
+
271
+ def test_vectorize_filters_invalid_execution_tags(mock_decorator_deps):
272
+ """
273
+ Case 6: Test that the decorator filters out execution tags that are
274
+ NOT in settings.custom_properties and only logs the valid tags.
275
+ (This test intentionally does not check the warning output mechanism)
276
+ """
277
+ mock_batch = mock_decorator_deps["batch"]
278
+ mock_settings = mock_decorator_deps["settings"]
279
+
280
+ # 1. Arrange: Define function with valid tags ('team', 'priority')
281
+ # and one invalid tag ('unknown_tag')
282
+ # (Fixture defines 'run_id', 'team', 'priority' as custom_properties)
283
+ @vectorize(
284
+ search_description="Test tag filtering",
285
+ sequence_narrative="Next",
286
+ team="data-science", # <-- Valid
287
+ priority=2, # <-- Valid
288
+ unknown_tag="should-be-ignored" # <-- INVALID
289
+ )
290
+ def my_mixed_tags_function():
291
+ pass
292
+
293
+ # 2. Act
294
+ my_mixed_tags_function() # Run the wrapper
295
+
296
+ # 3. Assert: Check final execution log properties
297
+ # (We check the *last* call, which is the dynamic log)
298
+ args, kwargs = mock_batch.add_object.call_args
299
+ props = kwargs["properties"]
300
+
301
+ assert kwargs["collection"] == mock_settings.EXECUTION_COLLECTION_NAME
302
+
303
+ # 3a. Valid global tag should exist
304
+ assert props["run_id"] == "test-run-abc"
305
+
306
+ # 3b. Valid function-specific tags should exist
307
+ assert props["team"] == "data-science"
308
+ assert props["priority"] == 2
309
+
310
+ # 3c. Invalid tag should NOT exist
311
+ assert "unknown_tag" not in props
312
+
313
+
314
+ def test_vectorize_handles_tags_when_no_props_file(mock_decorator_deps_no_props):
315
+ """
316
+ Case 7: Test that if settings.custom_properties is None,
317
+ ALL execution_tags are ignored.
318
+ (This test intentionally does not check the warning output mechanism)
319
+ """
320
+ mock_batch = mock_decorator_deps_no_props["batch"]
321
+ mock_settings = mock_decorator_deps_no_props["settings"]
322
+
323
+ # 1. Arrange
324
+ @vectorize(
325
+ search_description="Test no props",
326
+ sequence_narrative="Next",
327
+ team="should-be-ignored" # <-- Invalid (because no file)
328
+ )
329
+ def my_no_props_function():
330
+ pass
331
+
332
+ # 2. Act
333
+ my_no_props_function() # Run wrapper
334
+
335
+ # 3. Assert: Check final execution log properties
336
+ args, kwargs = mock_batch.add_object.call_args
337
+ props = kwargs["properties"]
338
+
339
+ assert kwargs["collection"] == mock_settings.EXECUTION_COLLECTION_NAME
340
+
341
+ # 3a. Basic properties should still exist
342
+ assert props["status"] == "SUCCESS"
343
+
344
+ # 3b. The tag should NOT exist
345
+ assert "team" not in props
File without changes