vectorwave 0.1.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.
- tests/__init__.py +0 -0
- tests/batch/__init__.py +0 -0
- tests/batch/test_batch.py +97 -0
- tests/core/__init__.py +0 -0
- tests/core/test_decorator.py +345 -0
- tests/database/__init__.py +0 -0
- tests/database/test_db.py +464 -0
- tests/database/test_db_search.py +163 -0
- tests/exception/__init__.py +0 -0
- tests/models/__init__.py +0 -0
- tests/models/test_db_config.py +143 -0
- tests/monitoring/__init__.py +0 -0
- tests/prediction/__init__.py +0 -0
- vectorwave/__init__.py +13 -0
- vectorwave/batch/__init__.py +0 -0
- vectorwave/batch/batch.py +65 -0
- vectorwave/core/__init__.py +0 -0
- vectorwave/core/core.py +0 -0
- vectorwave/core/decorator.py +108 -0
- vectorwave/database/__init__.py +0 -0
- vectorwave/database/db.py +302 -0
- vectorwave/database/db_search.py +100 -0
- vectorwave/exception/__init__.py +0 -0
- vectorwave/exception/exceptions.py +22 -0
- vectorwave/models/__init__.py +0 -0
- vectorwave/models/db_config.py +82 -0
- vectorwave/monitoring/__init__.py +0 -0
- vectorwave/monitoring/monitoring.py +0 -0
- vectorwave/monitoring/tracer.py +128 -0
- vectorwave/prediction/__init__.py +0 -0
- vectorwave/prediction/predictor.py +0 -0
- vectorwave-0.1.0.dist-info/METADATA +280 -0
- vectorwave-0.1.0.dist-info/RECORD +35 -0
- vectorwave-0.1.0.dist-info/WHEEL +5 -0
- vectorwave-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import json
|
|
3
|
+
from unittest.mock import patch, mock_open
|
|
4
|
+
from json import JSONDecodeError
|
|
5
|
+
|
|
6
|
+
# Function to test
|
|
7
|
+
from vectorwave.models.db_config import get_weaviate_settings
|
|
8
|
+
|
|
9
|
+
# --- Mock Data ---
|
|
10
|
+
|
|
11
|
+
# Mock content for a successfully loaded .weaviate_properties file
|
|
12
|
+
MOCK_JSON_DATA = """
|
|
13
|
+
{
|
|
14
|
+
"run_id": {
|
|
15
|
+
"data_type": "TEXT",
|
|
16
|
+
"description": "Test run ID"
|
|
17
|
+
},
|
|
18
|
+
"experiment_id": {
|
|
19
|
+
"data_type": "INT",
|
|
20
|
+
"description": "Identifier for the experiment"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# Mock content for a malformed .weaviate_properties file (invalid JSON)
|
|
26
|
+
MOCK_INVALID_JSON = """
|
|
27
|
+
{
|
|
28
|
+
"run_id": {
|
|
29
|
+
"data_type": "TEXT"
|
|
30
|
+
}
|
|
31
|
+
""" # Missing closing '}'
|
|
32
|
+
|
|
33
|
+
# --- Test Cases ---
|
|
34
|
+
|
|
35
|
+
@patch('os.path.exists', return_value=True)
|
|
36
|
+
@patch('builtins.open', new_callable=mock_open, read_data=MOCK_JSON_DATA)
|
|
37
|
+
def test_get_settings_loads_custom_props_success(mock_open_file, mock_exists):
|
|
38
|
+
"""
|
|
39
|
+
Case 1: .weaviate_properties file exists and JSON is valid
|
|
40
|
+
- settings.custom_properties should be loaded correctly as a dictionary
|
|
41
|
+
"""
|
|
42
|
+
# Arrange
|
|
43
|
+
# Clear the @lru_cache to bypass caching for this test
|
|
44
|
+
get_weaviate_settings.cache_clear()
|
|
45
|
+
|
|
46
|
+
# Act
|
|
47
|
+
settings = get_weaviate_settings()
|
|
48
|
+
|
|
49
|
+
# Assert
|
|
50
|
+
# Verify that the default path (.weaviate_properties) was checked
|
|
51
|
+
mock_exists.assert_called_with(".weaviate_properties")
|
|
52
|
+
# Verify the file was opened in 'r' mode
|
|
53
|
+
mock_open_file.assert_called_with(".weaviate_properties", 'r', encoding='utf-8')
|
|
54
|
+
|
|
55
|
+
assert settings.custom_properties is not None
|
|
56
|
+
assert "run_id" in settings.custom_properties
|
|
57
|
+
assert settings.custom_properties["run_id"]["data_type"] == "TEXT"
|
|
58
|
+
assert settings.custom_properties["run_id"]["description"] == "Test run ID"
|
|
59
|
+
assert "experiment_id" in settings.custom_properties
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@patch('os.path.exists', return_value=False)
|
|
63
|
+
def test_get_settings_file_not_found(mock_exists, capsys):
|
|
64
|
+
"""
|
|
65
|
+
Case 2: .weaviate_properties file does not exist
|
|
66
|
+
- settings.custom_properties should be None
|
|
67
|
+
- A 'file not found' note should be printed
|
|
68
|
+
"""
|
|
69
|
+
# Arrange
|
|
70
|
+
get_weaviate_settings.cache_clear()
|
|
71
|
+
|
|
72
|
+
# Act
|
|
73
|
+
settings = get_weaviate_settings()
|
|
74
|
+
|
|
75
|
+
# Assert
|
|
76
|
+
mock_exists.assert_called_with(".weaviate_properties")
|
|
77
|
+
assert settings.custom_properties is None
|
|
78
|
+
|
|
79
|
+
# Check if 'file not found' note was printed
|
|
80
|
+
captured = capsys.readouterr()
|
|
81
|
+
assert "file not found" in captured.out
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@patch('os.path.exists', return_value=True)
|
|
85
|
+
@patch('builtins.open', new_callable=mock_open, read_data=MOCK_INVALID_JSON)
|
|
86
|
+
@patch('json.load', side_effect=JSONDecodeError("Mock JSON Decode Error", "", 0))
|
|
87
|
+
def test_get_settings_invalid_json(mock_json_load, mock_open_file, mock_exists, capsys):
|
|
88
|
+
"""
|
|
89
|
+
Case 3: File exists but JSON format is invalid
|
|
90
|
+
- settings.custom_properties should be None
|
|
91
|
+
- A 'Could not parse JSON' warning should be printed
|
|
92
|
+
"""
|
|
93
|
+
# Arrange
|
|
94
|
+
get_weaviate_settings.cache_clear()
|
|
95
|
+
|
|
96
|
+
# Act
|
|
97
|
+
settings = get_weaviate_settings()
|
|
98
|
+
|
|
99
|
+
# Assert
|
|
100
|
+
mock_exists.assert_called_once()
|
|
101
|
+
mock_open_file.assert_called_once()
|
|
102
|
+
mock_json_load.assert_called_once() # json.load was called but failed (due to side_effect)
|
|
103
|
+
assert settings.custom_properties is None # Should be None due to parsing failure
|
|
104
|
+
|
|
105
|
+
# Check if 'Could not parse JSON' warning was printed
|
|
106
|
+
captured = capsys.readouterr()
|
|
107
|
+
assert "Could not parse JSON" in captured.out
|
|
108
|
+
|
|
109
|
+
@patch('os.path.exists', return_value=True)
|
|
110
|
+
@patch('builtins.open', new_callable=mock_open, read_data=MOCK_JSON_DATA)
|
|
111
|
+
@patch('os.environ.get') # os.environ.get을 모킹합니다
|
|
112
|
+
def test_get_settings_loads_global_custom_values(mock_env_get, mock_open_file, mock_exists):
|
|
113
|
+
"""
|
|
114
|
+
Case 4: Test if the value of the "RUN_ID" environment variable is loaded
|
|
115
|
+
into global_custom_values for "run_id" defined in .weaviate_properties
|
|
116
|
+
"""
|
|
117
|
+
# 1. Arrange
|
|
118
|
+
# MOCK_JSON_DATA defines "run_id" and "experiment_id".
|
|
119
|
+
# Set os.environ.get("RUN_ID") to return "test-run-123".
|
|
120
|
+
# Set os.environ.get("EXPERIMENT_ID") to return None.
|
|
121
|
+
def mock_env_side_effect(key):
|
|
122
|
+
if key == "RUN_ID":
|
|
123
|
+
return "test-run-123"
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
mock_env_get.side_effect = mock_env_side_effect
|
|
127
|
+
get_weaviate_settings.cache_clear()
|
|
128
|
+
|
|
129
|
+
# 2. Act
|
|
130
|
+
settings = get_weaviate_settings()
|
|
131
|
+
|
|
132
|
+
# 3. Assert
|
|
133
|
+
# .weaviate_properties should be loaded correctly.
|
|
134
|
+
assert settings.custom_properties is not None
|
|
135
|
+
assert "run_id" in settings.custom_properties
|
|
136
|
+
|
|
137
|
+
# Check if global_custom_values was loaded correctly.
|
|
138
|
+
assert settings.global_custom_values is not None
|
|
139
|
+
assert "run_id" in settings.global_custom_values
|
|
140
|
+
assert settings.global_custom_values["run_id"] == "test-run-123"
|
|
141
|
+
|
|
142
|
+
# "EXPERIMENT_ID" should not be included as os.environ.get returned None.
|
|
143
|
+
assert "experiment_id" not in settings.global_custom_values
|
|
File without changes
|
|
File without changes
|
vectorwave/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .core.decorator import vectorize
|
|
2
|
+
|
|
3
|
+
from .database.db import initialize_database
|
|
4
|
+
from .database.db_search import search_functions, search_executions
|
|
5
|
+
from .monitoring.tracer import trace_span
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'vectorize',
|
|
9
|
+
'initialize_database',
|
|
10
|
+
'search_functions',
|
|
11
|
+
'search_executions',
|
|
12
|
+
'trace_span'
|
|
13
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import weaviate
|
|
2
|
+
import atexit
|
|
3
|
+
import logging
|
|
4
|
+
from functools import lru_cache
|
|
5
|
+
from ..models.db_config import get_weaviate_settings, WeaviateSettings
|
|
6
|
+
from ..database.db import get_weaviate_client
|
|
7
|
+
from ..exception.exceptions import WeaviateConnectionError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WeaviateBatchManager:
|
|
12
|
+
"""
|
|
13
|
+
A singleton class that manages Weaviate batch imports.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self._initialized = False
|
|
18
|
+
print("Initializing WeaviateBatchManager...")
|
|
19
|
+
self.client: weaviate.WeaviateClient = None
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
# (get_weaviate_settings is reused as it is handled by lru_cache)
|
|
23
|
+
self.settings: WeaviateSettings = get_weaviate_settings()
|
|
24
|
+
self.client: weaviate.WeaviateClient = get_weaviate_client(self.settings)
|
|
25
|
+
|
|
26
|
+
if not self.client:
|
|
27
|
+
raise WeaviateConnectionError("Client is None, cannot configure batch.")
|
|
28
|
+
|
|
29
|
+
# self.client.batch.configure(
|
|
30
|
+
# batch_size=20,
|
|
31
|
+
# dynamic=True,
|
|
32
|
+
# timeout_retries=3,
|
|
33
|
+
# )
|
|
34
|
+
|
|
35
|
+
# Register atexit: Automatically calls self.flush() on script exit
|
|
36
|
+
# atexit.register(self.flush)
|
|
37
|
+
self._initialized = True
|
|
38
|
+
print("WeaviateBatchManager initialized and 'atexit' flush registered.")
|
|
39
|
+
|
|
40
|
+
except Exception as e:
|
|
41
|
+
# Prevents VectorWave from stopping the main app upon DB connection failure
|
|
42
|
+
print(f"Failed to initialize WeaviateBatchManager: {e}. Batching will be disabled.")
|
|
43
|
+
|
|
44
|
+
def add_object(self, collection: str, properties: dict, uuid: str = None):
|
|
45
|
+
"""
|
|
46
|
+
Adds an object to the Weaviate batch queue.
|
|
47
|
+
"""
|
|
48
|
+
if not self._initialized or not self.client:
|
|
49
|
+
print("Warning: Batch manager not initialized. Skipping add_object.") # print 유지
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
self.client.collections.get(collection).data.insert(
|
|
54
|
+
properties=properties,
|
|
55
|
+
uuid=uuid
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
except Exception as e:
|
|
59
|
+
print(f"Error: Failed to add object to batch (Collection: {collection}): {e}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@lru_cache(None)
|
|
64
|
+
def get_batch_manager() -> WeaviateBatchManager:
|
|
65
|
+
return WeaviateBatchManager()
|
|
File without changes
|
vectorwave/core/core.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# vtm/src/vectorwave/core/decorator.py
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from functools import wraps
|
|
5
|
+
|
|
6
|
+
from weaviate.util import generate_uuid5
|
|
7
|
+
|
|
8
|
+
from ..batch.batch import get_batch_manager
|
|
9
|
+
from ..models.db_config import get_weaviate_settings
|
|
10
|
+
from ..monitoring.tracer import trace_root, trace_span
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def vectorize(search_description: str,
|
|
14
|
+
sequence_narrative: str,
|
|
15
|
+
**execution_tags):
|
|
16
|
+
"""
|
|
17
|
+
VectorWave Decorator
|
|
18
|
+
|
|
19
|
+
(1) Collects function definitions (static data) once on script load.
|
|
20
|
+
(2) Records function execution (dynamic data) every time the function is called.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def decorator(func):
|
|
24
|
+
|
|
25
|
+
func_uuid = None
|
|
26
|
+
valid_execution_tags = {}
|
|
27
|
+
try:
|
|
28
|
+
module_name = func.__module__
|
|
29
|
+
function_name = func.__name__
|
|
30
|
+
|
|
31
|
+
func_identifier = f"{module_name}.{function_name}"
|
|
32
|
+
func_uuid = generate_uuid5(func_identifier)
|
|
33
|
+
|
|
34
|
+
static_properties = {
|
|
35
|
+
"function_name": function_name,
|
|
36
|
+
"module_name": module_name,
|
|
37
|
+
"docstring": inspect.getdoc(func) or "",
|
|
38
|
+
"source_code": inspect.getsource(func),
|
|
39
|
+
"search_description": search_description,
|
|
40
|
+
"sequence_narrative": sequence_narrative
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
batch = get_batch_manager()
|
|
44
|
+
settings = get_weaviate_settings()
|
|
45
|
+
|
|
46
|
+
if execution_tags:
|
|
47
|
+
if not settings.custom_properties:
|
|
48
|
+
print(
|
|
49
|
+
f"Warning: Function '{function_name}' provided execution_tags {list(execution_tags.keys())} "
|
|
50
|
+
f"but no .weaviate_properties file was loaded. These tags will be IGNORED."
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
allowed_keys = set(settings.custom_properties.keys())
|
|
54
|
+
for key, value in execution_tags.items():
|
|
55
|
+
if key in allowed_keys:
|
|
56
|
+
valid_execution_tags[key] = value
|
|
57
|
+
else:
|
|
58
|
+
print(
|
|
59
|
+
f"Warning: Function '{function_name}' has undefined execution_tag: '{key}'. "
|
|
60
|
+
f"This tag will be IGNORED. Please add it to your .weaviate_properties file."
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
batch.add_object(
|
|
64
|
+
collection=settings.COLLECTION_NAME,
|
|
65
|
+
properties=static_properties,
|
|
66
|
+
uuid=func_uuid
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
except Exception as e:
|
|
70
|
+
print(f"Error in @vectorize setup for {func.__name__}: {e}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# 2a. The *inner* wrapper to be wrapped by @trace_span
|
|
74
|
+
# This function receives all tags including full_kwargs from @trace_span.
|
|
75
|
+
@trace_root()
|
|
76
|
+
@trace_span(attributes_to_capture=['function_uuid', 'team', 'priority', 'run_id'])
|
|
77
|
+
@wraps(func)
|
|
78
|
+
def inner_wrapper(*args, **kwargs):
|
|
79
|
+
|
|
80
|
+
original_kwargs = kwargs.copy()
|
|
81
|
+
|
|
82
|
+
keys_to_remove = list(valid_execution_tags.keys())
|
|
83
|
+
keys_to_remove.append('function_uuid')
|
|
84
|
+
|
|
85
|
+
for key in execution_tags.keys():
|
|
86
|
+
if key not in keys_to_remove:
|
|
87
|
+
keys_to_remove.append(key)
|
|
88
|
+
|
|
89
|
+
for key in keys_to_remove:
|
|
90
|
+
original_kwargs.pop(key, None)
|
|
91
|
+
|
|
92
|
+
return func(*args, **original_kwargs)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@wraps(func)
|
|
96
|
+
def outer_wrapper(*args, **kwargs):
|
|
97
|
+
|
|
98
|
+
full_kwargs = kwargs.copy()
|
|
99
|
+
full_kwargs.update(valid_execution_tags)
|
|
100
|
+
full_kwargs['function_uuid'] = func_uuid
|
|
101
|
+
|
|
102
|
+
# 2. Call the *inner* wrapper with the full_kwargs
|
|
103
|
+
# This call passes through the @trace_root -> @trace_span decorators.
|
|
104
|
+
return inner_wrapper(*args, **full_kwargs)
|
|
105
|
+
|
|
106
|
+
return outer_wrapper
|
|
107
|
+
|
|
108
|
+
return decorator
|
|
File without changes
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import weaviate
|
|
2
|
+
import weaviate.classes.config as wvc # (wvc = Weaviate Classes Config)
|
|
3
|
+
import weaviate.config as wvc_config
|
|
4
|
+
from weaviate.config import AdditionalConfig
|
|
5
|
+
from vectorwave.models.db_config import WeaviateSettings
|
|
6
|
+
from vectorwave.exception.exceptions import (
|
|
7
|
+
WeaviateConnectionError,
|
|
8
|
+
WeaviateNotReadyError,
|
|
9
|
+
SchemaCreationError
|
|
10
|
+
)
|
|
11
|
+
from functools import lru_cache
|
|
12
|
+
from weaviate.exceptions import WeaviateConnectionError as WeaviateClientConnectionError
|
|
13
|
+
from vectorwave.models.db_config import get_weaviate_settings
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Code based on Weaviate v4 (latest) client.
|
|
17
|
+
|
|
18
|
+
def get_weaviate_client(settings: WeaviateSettings) -> weaviate.WeaviateClient:
|
|
19
|
+
"""
|
|
20
|
+
Creates and returns a Weaviate client.
|
|
21
|
+
|
|
22
|
+
[Raises]
|
|
23
|
+
- WeaviateConnectionError: If connection fails.
|
|
24
|
+
- WeaviateNotReadyError: If connected, but the server is not ready.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
client: weaviate.WeaviateClient
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
client = weaviate.connect_to_local(
|
|
31
|
+
host=settings.WEAVIATE_HOST,
|
|
32
|
+
port=settings.WEAVIATE_PORT,
|
|
33
|
+
grpc_port=settings.WEAVIATE_GRPC_PORT,
|
|
34
|
+
additional_config=AdditionalConfig(
|
|
35
|
+
dynamic=True,
|
|
36
|
+
batch_size=20,
|
|
37
|
+
timeout_retries=3
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
except WeaviateClientConnectionError as e:
|
|
41
|
+
raise WeaviateConnectionError(f"Failed to connect to Weaviate: {e}")
|
|
42
|
+
except Exception as e:
|
|
43
|
+
raise WeaviateConnectionError(f"An unknown error occurred while connecting to Weaviate: {e}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if not client.is_ready():
|
|
47
|
+
raise WeaviateNotReadyError("Connected to Weaviate, but the server is not ready.")
|
|
48
|
+
|
|
49
|
+
print("Weaviate client connected successfully.")
|
|
50
|
+
return client
|
|
51
|
+
|
|
52
|
+
@lru_cache()
|
|
53
|
+
def get_cached_client() -> weaviate.WeaviateClient:
|
|
54
|
+
"""
|
|
55
|
+
Singleton factory: Gets settings and returns a single client instance.
|
|
56
|
+
This function IS cached.
|
|
57
|
+
"""
|
|
58
|
+
print("Creating and caching new Weaviate client instance...")
|
|
59
|
+
settings = get_weaviate_settings()
|
|
60
|
+
client = get_weaviate_client(settings)
|
|
61
|
+
return client
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def create_vectorwave_schema(client: weaviate.WeaviateClient, settings: WeaviateSettings):
|
|
65
|
+
"""
|
|
66
|
+
Defines and creates the VectorWaveFunctions collection schema.
|
|
67
|
+
Now includes custom properties loaded from the settings (via .weaviate_properties file).
|
|
68
|
+
|
|
69
|
+
[Raises]
|
|
70
|
+
- SchemaCreationError: If an error occurs during schema creation.
|
|
71
|
+
"""
|
|
72
|
+
collection_name = settings.COLLECTION_NAME
|
|
73
|
+
|
|
74
|
+
# 1. Check if the collection already exists
|
|
75
|
+
if client.collections.exists(collection_name):
|
|
76
|
+
print(f"Collection '{collection_name}' already exists. Skipping creation.")
|
|
77
|
+
return client.collections.get(collection_name)
|
|
78
|
+
|
|
79
|
+
# 2. If it doesn't exist, define and create the collection
|
|
80
|
+
print(f"Creating collection '{collection_name}'...")
|
|
81
|
+
|
|
82
|
+
# 3. Define Base Properties
|
|
83
|
+
base_properties = [
|
|
84
|
+
wvc.Property(
|
|
85
|
+
name="function_name",
|
|
86
|
+
data_type=wvc.DataType.TEXT,
|
|
87
|
+
description="The name of the vectorized function"
|
|
88
|
+
),
|
|
89
|
+
wvc.Property(
|
|
90
|
+
name="module_name",
|
|
91
|
+
data_type=wvc.DataType.TEXT,
|
|
92
|
+
description="The Python module path where the function is defined"
|
|
93
|
+
),
|
|
94
|
+
wvc.Property(
|
|
95
|
+
name="docstring",
|
|
96
|
+
data_type=wvc.DataType.TEXT,
|
|
97
|
+
description="The function's Docstring (description)"
|
|
98
|
+
),
|
|
99
|
+
wvc.Property(
|
|
100
|
+
name="source_code",
|
|
101
|
+
data_type=wvc.DataType.TEXT,
|
|
102
|
+
description="The actual source code of the function"
|
|
103
|
+
),
|
|
104
|
+
wvc.Property(
|
|
105
|
+
name="search_description",
|
|
106
|
+
data_type=wvc.DataType.TEXT,
|
|
107
|
+
description="User-provided description for similarity search (from @vectorize)"
|
|
108
|
+
),
|
|
109
|
+
wvc.Property(
|
|
110
|
+
name="sequence_narrative",
|
|
111
|
+
data_type=wvc.DataType.TEXT,
|
|
112
|
+
description="User-provided context about what happens next (from @vectorize)"
|
|
113
|
+
),
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
# 4. Parse Custom Properties (loaded from JSON file via settings object)
|
|
117
|
+
custom_properties = []
|
|
118
|
+
if settings.custom_properties:
|
|
119
|
+
print(f"Adding custom properties: {list(settings.custom_properties.keys())}")
|
|
120
|
+
|
|
121
|
+
for name, prop_details in settings.custom_properties.items():
|
|
122
|
+
if not isinstance(prop_details, dict):
|
|
123
|
+
raise SchemaCreationError(f"Custom property '{name}' in config file must be a dictionary.")
|
|
124
|
+
|
|
125
|
+
# Get data_type (Required)
|
|
126
|
+
dtype_str = prop_details.get("data_type")
|
|
127
|
+
if not dtype_str:
|
|
128
|
+
raise SchemaCreationError(f"Custom property '{name}' in config file is missing 'data_type'.")
|
|
129
|
+
|
|
130
|
+
# Get description (Optional)
|
|
131
|
+
description = prop_details.get("description")
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
# Convert string (e.g., "TEXT") to Weaviate Enum (wvc.DataType.TEXT)
|
|
135
|
+
data_type = getattr(wvc.DataType, dtype_str.upper())
|
|
136
|
+
|
|
137
|
+
custom_properties.append(
|
|
138
|
+
wvc.Property(
|
|
139
|
+
name=name,
|
|
140
|
+
data_type=data_type,
|
|
141
|
+
description=description
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
except AttributeError:
|
|
145
|
+
raise SchemaCreationError(
|
|
146
|
+
f"Invalid data_type '{dtype_str}' for custom property '{name}'. "
|
|
147
|
+
f"Use a valid wvc.DataType string (e.g., 'TEXT', 'INT', 'NUMBER')."
|
|
148
|
+
)
|
|
149
|
+
except Exception as e:
|
|
150
|
+
raise SchemaCreationError(f"Error processing custom property '{name}': {e}")
|
|
151
|
+
|
|
152
|
+
# 5. Combine properties
|
|
153
|
+
all_properties = base_properties + custom_properties
|
|
154
|
+
|
|
155
|
+
vectorizer_name = settings.VECTORIZER_CONFIG.lower()
|
|
156
|
+
vector_config = None
|
|
157
|
+
|
|
158
|
+
print(f"Configuring vectorizer: {vectorizer_name}")
|
|
159
|
+
|
|
160
|
+
if vectorizer_name == "text2vec-openai":
|
|
161
|
+
vector_config = {
|
|
162
|
+
"vectorizer": "text2vec-openai",
|
|
163
|
+
"text2vec-openai": {
|
|
164
|
+
"vectorizeClassName": settings.IS_VECTORIZE_COLLECTION_NAME,
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
elif vectorizer_name == "none":
|
|
168
|
+
vector_config = {"vectorizer": "none"}
|
|
169
|
+
else:
|
|
170
|
+
raise SchemaCreationError(
|
|
171
|
+
f"Unsupported VECTORIZER_CONFIG: '{settings.VECTORIZER_CONFIG}'. "
|
|
172
|
+
f"Supported values: 'text2vec-openai', 'none'."
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
generative_config = None
|
|
176
|
+
if settings.GENERATIVE_CONFIG.lower() == "generative-openai":
|
|
177
|
+
generative_config = {"generator": "generative-openai"}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
vectorwave_collection = client.collections.create(
|
|
182
|
+
name=collection_name,
|
|
183
|
+
properties=all_properties,
|
|
184
|
+
|
|
185
|
+
# 7. Vectorizer Configuration
|
|
186
|
+
vector_config=vector_config,
|
|
187
|
+
|
|
188
|
+
# 8. Generative Configuration (for RAG, etc.)
|
|
189
|
+
generative_config=generative_config
|
|
190
|
+
)
|
|
191
|
+
return vectorwave_collection
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
# Raise a specific exception instead of returning None
|
|
195
|
+
raise SchemaCreationError(f"Error during schema creation: {e}")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def create_execution_schema(client: weaviate.WeaviateClient, settings: WeaviateSettings):
|
|
199
|
+
"""
|
|
200
|
+
Defines and creates the VectorWaveExecutions (dynamic) collection schema.
|
|
201
|
+
"""
|
|
202
|
+
collection_name = settings.EXECUTION_COLLECTION_NAME
|
|
203
|
+
|
|
204
|
+
if client.collections.exists(collection_name):
|
|
205
|
+
print(f"Collection '{collection_name}' already exists. Skipping creation.")
|
|
206
|
+
return client.collections.get(collection_name)
|
|
207
|
+
|
|
208
|
+
print(f"Creating collection '{collection_name}'...")
|
|
209
|
+
|
|
210
|
+
properties = [
|
|
211
|
+
wvc.Property(
|
|
212
|
+
name="trace_id",
|
|
213
|
+
data_type=wvc.DataType.TEXT,
|
|
214
|
+
description="The unique ID for the entire trace/workflow"
|
|
215
|
+
),
|
|
216
|
+
wvc.Property(
|
|
217
|
+
name="span_id",
|
|
218
|
+
data_type=wvc.DataType.TEXT,
|
|
219
|
+
description="The unique ID for this specific span/function execution"
|
|
220
|
+
),
|
|
221
|
+
wvc.Property(
|
|
222
|
+
name="function_name",
|
|
223
|
+
data_type=wvc.DataType.TEXT,
|
|
224
|
+
description="Name of the executed function (span name)"
|
|
225
|
+
),
|
|
226
|
+
wvc.Property(
|
|
227
|
+
name="function_uuid",
|
|
228
|
+
data_type=wvc.DataType.UUID,
|
|
229
|
+
description="The UUID of the executed function definition"
|
|
230
|
+
),
|
|
231
|
+
wvc.Property(
|
|
232
|
+
name="timestamp_utc",
|
|
233
|
+
data_type=wvc.DataType.DATE,
|
|
234
|
+
description="The UTC timestamp when the execution started"
|
|
235
|
+
),
|
|
236
|
+
wvc.Property(
|
|
237
|
+
name="duration_ms",
|
|
238
|
+
data_type=wvc.DataType.NUMBER,
|
|
239
|
+
description="Total execution time in milliseconds"
|
|
240
|
+
),
|
|
241
|
+
wvc.Property(
|
|
242
|
+
name="status",
|
|
243
|
+
data_type=wvc.DataType.TEXT, # "SUCCESS" or "ERROR"
|
|
244
|
+
description="Execution status"
|
|
245
|
+
),
|
|
246
|
+
wvc.Property(
|
|
247
|
+
name="error_message",
|
|
248
|
+
data_type=wvc.DataType.TEXT,
|
|
249
|
+
description="Error message and traceback if status is 'ERROR'"
|
|
250
|
+
),
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
if settings.custom_properties:
|
|
254
|
+
print(f"Adding custom properties to '{collection_name}': {list(settings.custom_properties.keys())}")
|
|
255
|
+
for name, prop_details in settings.custom_properties.items():
|
|
256
|
+
try:
|
|
257
|
+
if not isinstance(prop_details, dict):
|
|
258
|
+
raise ValueError("Property details must be a dictionary.")
|
|
259
|
+
|
|
260
|
+
dtype_str = prop_details.get("data_type")
|
|
261
|
+
if not dtype_str:
|
|
262
|
+
raise ValueError("data_type is missing.")
|
|
263
|
+
|
|
264
|
+
data_type = getattr(wvc.DataType, dtype_str.upper())
|
|
265
|
+
description = prop_details.get("description")
|
|
266
|
+
|
|
267
|
+
properties.append(
|
|
268
|
+
wvc.Property(
|
|
269
|
+
name=name,
|
|
270
|
+
data_type=data_type,
|
|
271
|
+
description=description
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
except Exception as e:
|
|
275
|
+
print(f"Warning: Skipping custom property {name} for {collection_name}: {e}")
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
execution_collection = client.collections.create(
|
|
279
|
+
name=collection_name,
|
|
280
|
+
properties=properties,
|
|
281
|
+
vectorizer_config=wvc.Configure.Vectorizer.none(),
|
|
282
|
+
vector_index_config=wvc.Configure.VectorIndex.none()
|
|
283
|
+
)
|
|
284
|
+
print(f"Collection '{collection_name}' created successfully.")
|
|
285
|
+
return execution_collection
|
|
286
|
+
except Exception as e:
|
|
287
|
+
raise SchemaCreationError(f"Error during execution schema creation: {e}")
|
|
288
|
+
|
|
289
|
+
def initialize_database():
|
|
290
|
+
"""
|
|
291
|
+
Helper function to initialize both the client and the two schemas.
|
|
292
|
+
"""
|
|
293
|
+
try:
|
|
294
|
+
settings = get_weaviate_settings()
|
|
295
|
+
client = get_cached_client()
|
|
296
|
+
if client:
|
|
297
|
+
create_vectorwave_schema(client, settings)
|
|
298
|
+
create_execution_schema(client, settings)
|
|
299
|
+
return client
|
|
300
|
+
except Exception as e:
|
|
301
|
+
print(f"Failed to initialize VectorWave database: {e}")
|
|
302
|
+
return None
|