vectorwave 0.2.6__tar.gz → 0.2.8__tar.gz
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.
- vectorwave-0.2.8/PKG-INFO +60 -0
- vectorwave-0.2.8/Readme.md +33 -0
- vectorwave-0.2.8/crates/src/lib.rs +171 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/pyproject.toml +1 -1
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/core/decorator.py +35 -4
- vectorwave-0.2.8/src/vectorwave/database/db.py +308 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/database/db_search.py +15 -26
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/monitoring/tracer.py +64 -131
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/utils/return_caching_utils.py +18 -7
- vectorwave-0.2.6/PKG-INFO +0 -680
- vectorwave-0.2.6/Readme.md +0 -653
- vectorwave-0.2.6/crates/src/lib.rs +0 -193
- vectorwave-0.2.6/src/vectorwave/database/db.py +0 -413
- {vectorwave-0.2.6 → vectorwave-0.2.8}/crates/Cargo.lock +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/crates/Cargo.toml +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/batch/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/batch/batch.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/core/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/core/auto_injector.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/core/core.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/core/generator.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/core/llm/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/core/llm/base.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/core/llm/factory.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/core/llm/openai_client.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/database/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/database/archiver.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/database/dataset.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/exception/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/exception/exceptions.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/models/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/models/db_config.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/monitoring/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/monitoring/alert/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/monitoring/alert/base.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/monitoring/alert/factory.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/monitoring/alert/null_alerter.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/monitoring/alert/webhook_alerter.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/monitoring/monitoring.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/prediction/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/prediction/predictor.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/search/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/search/execution_search.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/search/extended_search.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/search/rag_search.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/utils/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/utils/context.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/utils/function_cache.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/utils/healer.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/utils/replayer.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/utils/replayer_semantic.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/utils/status.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/vectorizer/__init__.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/vectorizer/base.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/vectorizer/factory.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/vectorizer/huggingface_vectorizer.py +0 -0
- {vectorwave-0.2.6 → vectorwave-0.2.8}/src/vectorwave/vectorizer/openai_vectorizer.py +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vectorwave
|
|
3
|
+
Version: 0.2.8
|
|
4
|
+
Classifier: Programming Language :: Python :: 3
|
|
5
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
+
Classifier: Programming Language :: Rust
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Requires-Dist: weaviate-client>=4.0.0
|
|
14
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
15
|
+
Requires-Dist: sentence-transformers
|
|
16
|
+
Requires-Dist: requests
|
|
17
|
+
Requires-Dist: openai
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
License-File: NOTICE
|
|
20
|
+
Summary: VectorWave: Seamless Auto-Vectorization Framework
|
|
21
|
+
Author-email: junyeonggim <junyeonggim5@gmail.com>
|
|
22
|
+
License-Expression: MIT
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
25
|
+
Project-URL: Repository, https://github.com/cozymori/vectorwave
|
|
26
|
+
|
|
27
|
+
Need more information? Visit [here](https://cozymori.github.io/vectorwave-docs/)
|
|
28
|
+
|
|
29
|
+
# VectorWave
|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+

|
|
34
|
+

|
|
35
|
+

|
|
36
|
+

|
|
37
|
+

|
|
38
|
+

|
|
39
|
+
<br>**Seamless Auto-Vectorization Framework**
|
|
40
|
+
|
|
41
|
+
We transform volatile data that disappears the moment code is executed into a Searchable and Reusable permanent knowledge asset.
|
|
42
|
+
|
|
43
|
+
### Requirements
|
|
44
|
+
|
|
45
|
+
* **Python**: 3.10 ~ 3.13
|
|
46
|
+
* **Docker**: Required to run the Weaviate database.
|
|
47
|
+
* (Optional) **OpenAI API Key**: Required for AI auto-documentation and high-performance embedding.
|
|
48
|
+
|
|
49
|
+
### How to reach us
|
|
50
|
+
|
|
51
|
+
Have questions or found a bug? Please join our community.
|
|
52
|
+
|
|
53
|
+
* **GitHub Issues**: [https://github.com/cozymori/vectorwave/issues](https://github.com/cozymori/vectorwave/issues)
|
|
54
|
+
|
|
55
|
+
### Contributors
|
|
56
|
+
[See the contributors of vectorwave](https://github.com/Cozymori/VectorWave/graphs/contributors)<br>
|
|
57
|
+
VectorWave is an open-source project and we welcome your contributions.
|
|
58
|
+
Please refer to [CONTRIBUTING.md](https://www.google.com/search?q=https://github.com/cozymori/vectorwave/blob/main/CONTRIBUTING.md) in the GitHub repository.
|
|
59
|
+
|
|
60
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Need more information? Visit [here](https://cozymori.github.io/vectorwave-docs/)
|
|
2
|
+
|
|
3
|
+
# VectorWave
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
<br>**Seamless Auto-Vectorization Framework**
|
|
14
|
+
|
|
15
|
+
We transform volatile data that disappears the moment code is executed into a Searchable and Reusable permanent knowledge asset.
|
|
16
|
+
|
|
17
|
+
### Requirements
|
|
18
|
+
|
|
19
|
+
* **Python**: 3.10 ~ 3.13
|
|
20
|
+
* **Docker**: Required to run the Weaviate database.
|
|
21
|
+
* (Optional) **OpenAI API Key**: Required for AI auto-documentation and high-performance embedding.
|
|
22
|
+
|
|
23
|
+
### How to reach us
|
|
24
|
+
|
|
25
|
+
Have questions or found a bug? Please join our community.
|
|
26
|
+
|
|
27
|
+
* **GitHub Issues**: [https://github.com/cozymori/vectorwave/issues](https://github.com/cozymori/vectorwave/issues)
|
|
28
|
+
|
|
29
|
+
### Contributors
|
|
30
|
+
[See the contributors of vectorwave](https://github.com/Cozymori/VectorWave/graphs/contributors)<br>
|
|
31
|
+
VectorWave is an open-source project and we welcome your contributions.
|
|
32
|
+
Please refer to [CONTRIBUTING.md](https://www.google.com/search?q=https://github.com/cozymori/vectorwave/blob/main/CONTRIBUTING.md) in the GitHub repository.
|
|
33
|
+
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
use pyo3::prelude::*;
|
|
2
|
+
use pyo3::types::{PyDict, PyList, PyString, PyBool, PyFloat, PyInt};
|
|
3
|
+
use std::collections::HashSet;
|
|
4
|
+
use std::thread;
|
|
5
|
+
use std::time::{Duration, Instant};
|
|
6
|
+
use std::sync::Mutex;
|
|
7
|
+
use crossbeam_channel::{bounded, Receiver, Sender, TrySendError};
|
|
8
|
+
|
|
9
|
+
struct LogItem {
|
|
10
|
+
collection: PyObject,
|
|
11
|
+
properties: Py<PyDict>,
|
|
12
|
+
uuid: Option<PyObject>,
|
|
13
|
+
vector: Option<Vec<f32>>,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#[pyclass]
|
|
17
|
+
struct RustBatchManager {
|
|
18
|
+
sender: Sender<LogItem>,
|
|
19
|
+
#[allow(dead_code)]
|
|
20
|
+
flush_callback: PyObject,
|
|
21
|
+
worker_handle: Mutex<Option<thread::JoinHandle<()>>>,
|
|
22
|
+
stop_signal: Sender<()>,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[pymethods]
|
|
26
|
+
impl RustBatchManager {
|
|
27
|
+
#[new]
|
|
28
|
+
fn new(
|
|
29
|
+
py: Python<'_>,
|
|
30
|
+
callback: PyObject,
|
|
31
|
+
batch_threshold: usize,
|
|
32
|
+
flush_interval_ms: u64,
|
|
33
|
+
) -> Self {
|
|
34
|
+
let (tx, rx) = bounded::<LogItem>(10000);
|
|
35
|
+
let (stop_tx, stop_rx) = bounded(1);
|
|
36
|
+
let worker_callback = callback.clone_ref(py);
|
|
37
|
+
|
|
38
|
+
let handle = thread::spawn(move || {
|
|
39
|
+
Self::worker_loop(rx, stop_rx, worker_callback, batch_threshold, flush_interval_ms);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
RustBatchManager {
|
|
43
|
+
sender: tx,
|
|
44
|
+
flush_callback: callback,
|
|
45
|
+
worker_handle: Mutex::new(Some(handle)),
|
|
46
|
+
stop_signal: stop_tx,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[pyo3(signature = (collection, properties, uuid=None, vector=None))]
|
|
51
|
+
fn add_object(&self, collection: PyObject, properties: Py<PyDict>, uuid: Option<PyObject>, vector: Option<Vec<f32>>) {
|
|
52
|
+
let item = LogItem { collection, properties, uuid, vector };
|
|
53
|
+
let _ = self.sender.try_send(item);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fn shutdown(&self, py: Python<'_>) {
|
|
57
|
+
let _ = self.stop_signal.send(());
|
|
58
|
+
py.allow_threads(|| {
|
|
59
|
+
if let Ok(mut handle_guard) = self.worker_handle.lock() {
|
|
60
|
+
if let Some(handle) = handle_guard.take() {
|
|
61
|
+
let _ = handle.join();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
impl RustBatchManager {
|
|
69
|
+
fn worker_loop(rx: Receiver<LogItem>, stop_rx: Receiver<()>, callback: PyObject, threshold: usize, interval_ms: u64) {
|
|
70
|
+
let mut buffer = Vec::with_capacity(threshold);
|
|
71
|
+
let mut last_flush = Instant::now();
|
|
72
|
+
let flush_interval = Duration::from_millis(interval_ms);
|
|
73
|
+
|
|
74
|
+
loop {
|
|
75
|
+
crossbeam_channel::select! {
|
|
76
|
+
recv(stop_rx) -> msg => {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
recv(rx) -> msg => {
|
|
80
|
+
match msg {
|
|
81
|
+
Ok(item) => buffer.push(item),
|
|
82
|
+
Err(_) => break,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
default(Duration::from_millis(100)) => {}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if stop_rx.is_empty() && (buffer.len() >= threshold || (last_flush.elapsed() >= flush_interval && !buffer.is_empty())) {
|
|
90
|
+
|
|
91
|
+
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
92
|
+
Self::flush_buffer(&buffer, &callback);
|
|
93
|
+
}));
|
|
94
|
+
buffer.clear();
|
|
95
|
+
last_flush = Instant::now();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fn flush_buffer(buffer: &Vec<LogItem>, callback: &PyObject) {
|
|
101
|
+
Python::with_gil(|py| {
|
|
102
|
+
let py_list = PyList::empty(py);
|
|
103
|
+
|
|
104
|
+
for item in buffer {
|
|
105
|
+
let dict = PyDict::new(py);
|
|
106
|
+
let mut set_field = |key: &str, val: PyObject| -> bool {
|
|
107
|
+
if let Err(_) = dict.set_item(key, val) { return false; }
|
|
108
|
+
true
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
if !set_field("collection", item.collection.clone_ref(py)) { continue; }
|
|
112
|
+
if !set_field("properties", item.properties.to_object(py)) { continue; }
|
|
113
|
+
let uuid_val = item.uuid.as_ref().map_or(py.None(), |u| u.clone_ref(py));
|
|
114
|
+
if !set_field("uuid", uuid_val) { continue; }
|
|
115
|
+
let vector_val = item.vector.as_ref().map_or(py.None(), |v| v.to_object(py));
|
|
116
|
+
if !set_field("vector", vector_val) { continue; }
|
|
117
|
+
|
|
118
|
+
if let Err(e) = py_list.append(dict) {
|
|
119
|
+
eprintln!("[RustCore] ⚠️ Failed to append item: {}", e);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if let Err(e) = callback.call1(py, (py_list,)) {
|
|
124
|
+
eprintln!("[RustCore] ⚠️ Flush failed: {}", e);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fn process_recursive(py: Python, value: &Bound<'_, PyAny>, sensitive_set: &HashSet<String>) -> PyResult<PyObject> {
|
|
131
|
+
if let Ok(dict_obj) = value.downcast::<PyDict>() {
|
|
132
|
+
let new_dict = PyDict::new(py);
|
|
133
|
+
for (k, v) in dict_obj {
|
|
134
|
+
let k_str = k.to_string().to_lowercase();
|
|
135
|
+
if sensitive_set.contains(&k_str) {
|
|
136
|
+
new_dict.set_item(k, "[MASKED]")?;
|
|
137
|
+
} else {
|
|
138
|
+
new_dict.set_item(k, process_recursive(py, &v, sensitive_set)?)?;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
Ok(new_dict.into())
|
|
142
|
+
} else if let Ok(list_obj) = value.downcast::<PyList>() {
|
|
143
|
+
let new_list = PyList::empty(py);
|
|
144
|
+
for item in list_obj {
|
|
145
|
+
new_list.append(process_recursive(py, &item, sensitive_set)?)?;
|
|
146
|
+
}
|
|
147
|
+
Ok(new_list.into())
|
|
148
|
+
} else {
|
|
149
|
+
if value.is_none() || value.is_instance_of::<PyBool>() || value.is_instance_of::<PyFloat>() || value.is_instance_of::<PyInt>() || value.is_instance_of::<PyString>() {
|
|
150
|
+
Ok(value.clone().unbind())
|
|
151
|
+
} else {
|
|
152
|
+
match value.str() {
|
|
153
|
+
Ok(s) => Ok(s.into()),
|
|
154
|
+
Err(_) => Ok(PyString::new(py, "[SERIALIZATION_ERROR]").into())
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
#[pyfunction]
|
|
161
|
+
fn mask_and_serialize(py: Python, data: &Bound<'_, PyAny>, sensitive_keys: Vec<String>) -> PyResult<PyObject> {
|
|
162
|
+
let sensitive_set: HashSet<String> = sensitive_keys.into_iter().map(|s| s.to_lowercase()).collect();
|
|
163
|
+
process_recursive(py, data, &sensitive_set)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#[pymodule]
|
|
167
|
+
fn vectorwave_core(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
168
|
+
m.add_class::<RustBatchManager>()?;
|
|
169
|
+
m.add_function(wrap_pyfunction!(mask_and_serialize, m)?)?;
|
|
170
|
+
Ok(())
|
|
171
|
+
}
|
|
@@ -26,6 +26,9 @@ def vectorize(search_description: Optional[str] = None,
|
|
|
26
26
|
cache_threshold: float = 0.9,
|
|
27
27
|
replay: bool = False,
|
|
28
28
|
attributes_to_capture: Optional[List[str]] = None,
|
|
29
|
+
capture_inputs: bool = False,
|
|
30
|
+
semantic_cache_filters: Optional[Dict[str, Any]] = None,
|
|
31
|
+
semantic_cache_scope: Optional[List[str]] = None,
|
|
29
32
|
**execution_tags):
|
|
30
33
|
"""
|
|
31
34
|
VectorWave Decorator with Auto-Generation support.
|
|
@@ -60,6 +63,15 @@ def vectorize(search_description: Optional[str] = None,
|
|
|
60
63
|
if attr not in final_attributes:
|
|
61
64
|
final_attributes.append(attr)
|
|
62
65
|
|
|
66
|
+
if capture_inputs:
|
|
67
|
+
try:
|
|
68
|
+
sig = inspect.signature(func)
|
|
69
|
+
for param_name in sig.parameters:
|
|
70
|
+
if param_name not in ('self', 'cls') and param_name not in final_attributes:
|
|
71
|
+
final_attributes.append(param_name)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logger.warning(f"Failed to inspect signature for auto-capture in '{function_name}': {e}")
|
|
74
|
+
|
|
63
75
|
if replay:
|
|
64
76
|
try:
|
|
65
77
|
sig = inspect.signature(func)
|
|
@@ -109,7 +121,6 @@ def vectorize(search_description: Optional[str] = None,
|
|
|
109
121
|
"static_properties": static_properties
|
|
110
122
|
})
|
|
111
123
|
else:
|
|
112
|
-
# [Existing] Immediate Registration Mode
|
|
113
124
|
current_content_hash = function_cache_manager.calculate_content_hash(func_identifier, static_properties)
|
|
114
125
|
|
|
115
126
|
if function_cache_manager.is_cached_and_unchanged(func_uuid, current_content_hash):
|
|
@@ -137,6 +148,25 @@ def vectorize(search_description: Optional[str] = None,
|
|
|
137
148
|
except Exception as e:
|
|
138
149
|
logger.error("Error in @vectorize setup for '%s': %s", func.__name__, e)
|
|
139
150
|
|
|
151
|
+
def resolve_semantic_filters(args, kwargs):
|
|
152
|
+
runtime_filters = semantic_cache_filters.copy() if semantic_cache_filters else {}
|
|
153
|
+
|
|
154
|
+
if semantic_cache_scope:
|
|
155
|
+
try:
|
|
156
|
+
sig = inspect.signature(func)
|
|
157
|
+
bound_args = sig.bind(*args, **kwargs)
|
|
158
|
+
bound_args.apply_defaults()
|
|
159
|
+
|
|
160
|
+
for arg_name in semantic_cache_scope:
|
|
161
|
+
if arg_name in bound_args.arguments:
|
|
162
|
+
runtime_filters[arg_name] = bound_args.arguments[arg_name]
|
|
163
|
+
else:
|
|
164
|
+
logger.warning(f"Semantic Scope: Argument '{arg_name}' not found in call to '{function_name}'.")
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.error(f"Failed to resolve semantic_cache_scope for '{function_name}': {e}")
|
|
167
|
+
|
|
168
|
+
return runtime_filters
|
|
169
|
+
|
|
140
170
|
# --- Wrapper Logic ---
|
|
141
171
|
|
|
142
172
|
if is_async_func:
|
|
@@ -144,7 +174,6 @@ def vectorize(search_description: Optional[str] = None,
|
|
|
144
174
|
@trace_span(attributes_to_capture=final_attributes, capture_return_value=capture_return_value)
|
|
145
175
|
@wraps(func)
|
|
146
176
|
async def inner_wrapper(*args, **kwargs):
|
|
147
|
-
# Remove injected tags from kwargs before calling original func
|
|
148
177
|
clean_kwargs = {k: v for k, v in kwargs.items() if
|
|
149
178
|
k not in valid_execution_tags and k != 'function_uuid' and k != 'exec_source'}
|
|
150
179
|
return await func(*args, **clean_kwargs)
|
|
@@ -152,7 +181,8 @@ def vectorize(search_description: Optional[str] = None,
|
|
|
152
181
|
@wraps(func)
|
|
153
182
|
async def outer_wrapper(*args, **kwargs):
|
|
154
183
|
if semantic_cache:
|
|
155
|
-
|
|
184
|
+
filters = resolve_semantic_filters(args, kwargs)
|
|
185
|
+
cached = _check_and_return_cached_result(func, args, kwargs, function_name, cache_threshold, True, filters=filters)
|
|
156
186
|
if cached is not None: return cached
|
|
157
187
|
|
|
158
188
|
full_kwargs = kwargs.copy()
|
|
@@ -176,7 +206,8 @@ def vectorize(search_description: Optional[str] = None,
|
|
|
176
206
|
@wraps(func)
|
|
177
207
|
def outer_wrapper(*args, **kwargs):
|
|
178
208
|
if semantic_cache:
|
|
179
|
-
|
|
209
|
+
filters = resolve_semantic_filters(args, kwargs)
|
|
210
|
+
cached = _check_and_return_cached_result(func, args, kwargs, function_name, cache_threshold, False, filters=filters)
|
|
180
211
|
if cached is not None: return cached
|
|
181
212
|
|
|
182
213
|
full_kwargs = kwargs.copy()
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
|
|
4
|
+
import weaviate
|
|
5
|
+
import weaviate.classes.config as wvc
|
|
6
|
+
from weaviate.classes.config import Tokenization
|
|
7
|
+
from vectorwave.exception.exceptions import (
|
|
8
|
+
WeaviateConnectionError,
|
|
9
|
+
WeaviateNotReadyError,
|
|
10
|
+
SchemaCreationError
|
|
11
|
+
)
|
|
12
|
+
from vectorwave.models.db_config import WeaviateSettings
|
|
13
|
+
from vectorwave.models.db_config import get_weaviate_settings
|
|
14
|
+
from weaviate.config import AdditionalConfig
|
|
15
|
+
from weaviate.exceptions import WeaviateConnectionError as WeaviateClientConnectionError
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_weaviate_client(settings: WeaviateSettings) -> weaviate.WeaviateClient:
|
|
21
|
+
try:
|
|
22
|
+
client = weaviate.connect_to_local(
|
|
23
|
+
host=settings.WEAVIATE_HOST,
|
|
24
|
+
port=settings.WEAVIATE_PORT,
|
|
25
|
+
grpc_port=settings.WEAVIATE_GRPC_PORT,
|
|
26
|
+
additional_config=AdditionalConfig(
|
|
27
|
+
dynamic=True,
|
|
28
|
+
batch_size=20,
|
|
29
|
+
timeout_retries=3
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
except WeaviateClientConnectionError as e:
|
|
33
|
+
raise WeaviateConnectionError(f"Failed to connect to Weaviate: {e}")
|
|
34
|
+
except Exception as e:
|
|
35
|
+
raise WeaviateConnectionError(f"An unknown error occurred while connecting to Weaviate: {e}")
|
|
36
|
+
|
|
37
|
+
if not client.is_ready():
|
|
38
|
+
raise WeaviateNotReadyError("Connected to Weaviate, but the server is not ready.")
|
|
39
|
+
|
|
40
|
+
logger.info("Weaviate client connected successfully")
|
|
41
|
+
return client
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@lru_cache()
|
|
45
|
+
def get_cached_client() -> weaviate.WeaviateClient:
|
|
46
|
+
logger.debug("Creating and caching new Weaviate client instance")
|
|
47
|
+
settings = get_weaviate_settings()
|
|
48
|
+
client = get_weaviate_client(settings)
|
|
49
|
+
return client
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _create_property_from_config(name: str, prop_details: dict) -> wvc.Property:
|
|
53
|
+
dtype_str = prop_details.get("data_type")
|
|
54
|
+
if not dtype_str:
|
|
55
|
+
raise ValueError(f"Property '{name}' missing 'data_type'")
|
|
56
|
+
|
|
57
|
+
if not hasattr(wvc.DataType, dtype_str.upper()):
|
|
58
|
+
raise ValueError(f"Invalid data_type '{dtype_str}' for property '{name}'")
|
|
59
|
+
|
|
60
|
+
data_type = getattr(wvc.DataType, dtype_str.upper())
|
|
61
|
+
description = prop_details.get("description")
|
|
62
|
+
|
|
63
|
+
tokenization = None
|
|
64
|
+
token_str = prop_details.get("tokenization")
|
|
65
|
+
|
|
66
|
+
if token_str:
|
|
67
|
+
token_map = {
|
|
68
|
+
"word": Tokenization.WORD,
|
|
69
|
+
"whitespace": Tokenization.WHITESPACE,
|
|
70
|
+
"field": Tokenization.FIELD,
|
|
71
|
+
"lowercase": Tokenization.LOWERCASE
|
|
72
|
+
}
|
|
73
|
+
token_key = token_str.lower()
|
|
74
|
+
if token_key in token_map:
|
|
75
|
+
tokenization = token_map[token_key]
|
|
76
|
+
else:
|
|
77
|
+
logger.warning(f"Invalid tokenization '{token_str}' for '{name}'. Using default.")
|
|
78
|
+
|
|
79
|
+
return wvc.Property(
|
|
80
|
+
name=name,
|
|
81
|
+
data_type=data_type,
|
|
82
|
+
description=description,
|
|
83
|
+
tokenization=tokenization
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def create_vectorwave_schema(client: weaviate.WeaviateClient, settings: WeaviateSettings):
|
|
88
|
+
collection_name = settings.COLLECTION_NAME
|
|
89
|
+
|
|
90
|
+
if client.collections.exists(collection_name):
|
|
91
|
+
logger.info("Collection '%s' already exists, skipping creation", collection_name)
|
|
92
|
+
return client.collections.get(collection_name)
|
|
93
|
+
|
|
94
|
+
logger.info("Creating collection '%s'", collection_name)
|
|
95
|
+
|
|
96
|
+
base_properties = [
|
|
97
|
+
wvc.Property(name="function_name", data_type=wvc.DataType.TEXT),
|
|
98
|
+
wvc.Property(name="module_name", data_type=wvc.DataType.TEXT),
|
|
99
|
+
wvc.Property(name="docstring", data_type=wvc.DataType.TEXT),
|
|
100
|
+
wvc.Property(name="source_code", data_type=wvc.DataType.TEXT),
|
|
101
|
+
wvc.Property(name="search_description", data_type=wvc.DataType.TEXT),
|
|
102
|
+
wvc.Property(name="sequence_narrative", data_type=wvc.DataType.TEXT),
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
custom_properties = []
|
|
106
|
+
if settings.custom_properties:
|
|
107
|
+
logger.info(f"Adding {len(settings.custom_properties)} custom properties to '{collection_name}'")
|
|
108
|
+
for name, prop_details in settings.custom_properties.items():
|
|
109
|
+
try:
|
|
110
|
+
prop = _create_property_from_config(name, prop_details)
|
|
111
|
+
custom_properties.append(prop)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logger.error(f"Invalid property definition for '{name}': {e}")
|
|
114
|
+
raise SchemaCreationError(f"Invalid property definition for '{name}': {e}")
|
|
115
|
+
|
|
116
|
+
all_properties = base_properties + custom_properties
|
|
117
|
+
|
|
118
|
+
vector_config = wvc.Configure.Vectorizer.none()
|
|
119
|
+
generative_config = None
|
|
120
|
+
|
|
121
|
+
if settings.VECTORIZER.lower() == "weaviate_module":
|
|
122
|
+
if settings.WEAVIATE_VECTORIZER_MODULE == "text2vec-openai":
|
|
123
|
+
vector_config = wvc.Configure.Vectorizer.text2vec_openai()
|
|
124
|
+
if settings.WEAVIATE_GENERATIVE_MODULE == "generative-openai":
|
|
125
|
+
generative_config = wvc.Configure.Generative.openai()
|
|
126
|
+
|
|
127
|
+
elif settings.VECTORIZER.lower() not in ["none", "huggingface", "openai_client"]:
|
|
128
|
+
raise SchemaCreationError(f"Invalid VECTORIZER setting: {settings.VECTORIZER}")
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
return client.collections.create(
|
|
132
|
+
name=collection_name,
|
|
133
|
+
properties=all_properties,
|
|
134
|
+
vectorizer_config=vector_config,
|
|
135
|
+
generative_config=generative_config
|
|
136
|
+
)
|
|
137
|
+
except Exception as e:
|
|
138
|
+
raise SchemaCreationError(f"Error during schema creation: {e}")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def create_execution_schema(client: weaviate.WeaviateClient, settings: WeaviateSettings):
|
|
142
|
+
collection_name = settings.EXECUTION_COLLECTION_NAME
|
|
143
|
+
|
|
144
|
+
if client.collections.exists(collection_name):
|
|
145
|
+
logger.info("Collection '%s' already exists, skipping creation", collection_name)
|
|
146
|
+
return client.collections.get(collection_name)
|
|
147
|
+
|
|
148
|
+
logger.info("Creating collection '%s'", collection_name)
|
|
149
|
+
|
|
150
|
+
properties = [
|
|
151
|
+
wvc.Property(name="trace_id", data_type=wvc.DataType.TEXT),
|
|
152
|
+
wvc.Property(name="span_id", data_type=wvc.DataType.TEXT),
|
|
153
|
+
wvc.Property(name="parent_span_id", data_type=wvc.DataType.TEXT),
|
|
154
|
+
wvc.Property(name="function_name", data_type=wvc.DataType.TEXT),
|
|
155
|
+
wvc.Property(name="function_uuid", data_type=wvc.DataType.UUID),
|
|
156
|
+
wvc.Property(name="timestamp_utc", data_type=wvc.DataType.DATE),
|
|
157
|
+
wvc.Property(name="duration_ms", data_type=wvc.DataType.NUMBER),
|
|
158
|
+
wvc.Property(name="status", data_type=wvc.DataType.TEXT),
|
|
159
|
+
wvc.Property(name="error_message", data_type=wvc.DataType.TEXT),
|
|
160
|
+
wvc.Property(name="error_code", data_type=wvc.DataType.TEXT),
|
|
161
|
+
wvc.Property(name="return_value", data_type=wvc.DataType.TEXT),
|
|
162
|
+
wvc.Property(name="exec_source", data_type=wvc.DataType.TEXT)
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
if settings.custom_properties:
|
|
166
|
+
logger.info(f"Adding {len(settings.custom_properties)} custom properties to '{collection_name}'")
|
|
167
|
+
for name, prop_details in settings.custom_properties.items():
|
|
168
|
+
try:
|
|
169
|
+
prop = _create_property_from_config(name, prop_details)
|
|
170
|
+
properties.append(prop)
|
|
171
|
+
except Exception as e:
|
|
172
|
+
logger.error(f"Invalid property definition for '{name}': {e}")
|
|
173
|
+
raise SchemaCreationError(f"Invalid property definition for '{name}': {e}")
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
return client.collections.create(
|
|
177
|
+
name=collection_name,
|
|
178
|
+
properties=properties,
|
|
179
|
+
vectorizer_config=wvc.Configure.Vectorizer.none(),
|
|
180
|
+
)
|
|
181
|
+
except Exception as e:
|
|
182
|
+
raise SchemaCreationError(f"Error during execution schema creation: {e}")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def create_golden_dataset_schema(client: weaviate.WeaviateClient, settings: WeaviateSettings):
|
|
186
|
+
collection_name = settings.GOLDEN_COLLECTION_NAME
|
|
187
|
+
|
|
188
|
+
if client.collections.exists(collection_name):
|
|
189
|
+
logger.info("Collection '%s' already exists.", collection_name)
|
|
190
|
+
return client.collections.get(collection_name)
|
|
191
|
+
|
|
192
|
+
logger.info("Creating collection '%s'", collection_name)
|
|
193
|
+
|
|
194
|
+
properties = [
|
|
195
|
+
wvc.Property(name="original_uuid", data_type=wvc.DataType.TEXT),
|
|
196
|
+
wvc.Property(name="function_name", data_type=wvc.DataType.TEXT),
|
|
197
|
+
wvc.Property(name="function_uuid", data_type=wvc.DataType.UUID),
|
|
198
|
+
wvc.Property(name="return_value", data_type=wvc.DataType.TEXT),
|
|
199
|
+
wvc.Property(name="note", data_type=wvc.DataType.TEXT),
|
|
200
|
+
wvc.Property(name="created_at", data_type=wvc.DataType.DATE),
|
|
201
|
+
wvc.Property(name="tags", data_type=wvc.DataType.TEXT_ARRAY)
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
if settings.custom_properties:
|
|
205
|
+
logger.info(f"Adding {len(settings.custom_properties)} custom properties to '{collection_name}'")
|
|
206
|
+
for name, prop_details in settings.custom_properties.items():
|
|
207
|
+
try:
|
|
208
|
+
prop = _create_property_from_config(name, prop_details)
|
|
209
|
+
properties.append(prop)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
logger.error(f"Invalid property definition for '{name}' in Golden Dataset: {e}")
|
|
212
|
+
raise SchemaCreationError(f"Invalid property definition for '{name}': {e}")
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
return client.collections.create(
|
|
216
|
+
name=collection_name,
|
|
217
|
+
properties=properties,
|
|
218
|
+
vectorizer_config=wvc.Configure.Vectorizer.none(),
|
|
219
|
+
)
|
|
220
|
+
except Exception as e:
|
|
221
|
+
raise SchemaCreationError(f"Error creating Golden Dataset schema: {e}")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def create_usage_schema(client: weaviate.WeaviateClient, settings: WeaviateSettings):
|
|
225
|
+
collection_name = "VectorWaveTokenUsage"
|
|
226
|
+
if client.collections.exists(collection_name):
|
|
227
|
+
return client.collections.get(collection_name)
|
|
228
|
+
|
|
229
|
+
logger.info("Creating collection '%s'", collection_name)
|
|
230
|
+
properties = [
|
|
231
|
+
wvc.Property(name="timestamp_utc", data_type=wvc.DataType.DATE),
|
|
232
|
+
wvc.Property(name="model", data_type=wvc.DataType.TEXT),
|
|
233
|
+
wvc.Property(name="usage_type", data_type=wvc.DataType.TEXT),
|
|
234
|
+
wvc.Property(name="category", data_type=wvc.DataType.TEXT),
|
|
235
|
+
wvc.Property(name="tokens", data_type=wvc.DataType.INT),
|
|
236
|
+
]
|
|
237
|
+
try:
|
|
238
|
+
return client.collections.create(
|
|
239
|
+
name=collection_name,
|
|
240
|
+
properties=properties,
|
|
241
|
+
vectorizer_config=wvc.Configure.Vectorizer.none(),
|
|
242
|
+
)
|
|
243
|
+
except Exception as e:
|
|
244
|
+
raise SchemaCreationError(f"Error usage schema: {e}")
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def update_database_schema():
|
|
248
|
+
"""
|
|
249
|
+
[Migration Utility]
|
|
250
|
+
Updates the schema of the running database.
|
|
251
|
+
Finds properties defined in .weaviate_properties that are missing from the DB and adds them.
|
|
252
|
+
Used to apply new filtering features without data loss.
|
|
253
|
+
"""
|
|
254
|
+
try:
|
|
255
|
+
settings = get_weaviate_settings()
|
|
256
|
+
client = get_cached_client()
|
|
257
|
+
|
|
258
|
+
target_collections = [
|
|
259
|
+
settings.EXECUTION_COLLECTION_NAME,
|
|
260
|
+
settings.GOLDEN_COLLECTION_NAME
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
logger.info("🚀 Starting Schema Migration...")
|
|
264
|
+
|
|
265
|
+
for col_name in target_collections:
|
|
266
|
+
if not client.collections.exists(col_name):
|
|
267
|
+
logger.warning(f"Collection '{col_name}' does not exist. Skipping.")
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
collection = client.collections.get(col_name)
|
|
271
|
+
# 현재 DB에 존재하는 속성 이름 가져오기
|
|
272
|
+
existing_props = {p.name for p in collection.config.get().properties}
|
|
273
|
+
|
|
274
|
+
# 추가할 속성 확인
|
|
275
|
+
if settings.custom_properties:
|
|
276
|
+
for name, prop_details in settings.custom_properties.items():
|
|
277
|
+
if name not in existing_props:
|
|
278
|
+
logger.info(f"➕ Adding new property '{name}' to '{col_name}'...")
|
|
279
|
+
try:
|
|
280
|
+
new_prop = _create_property_from_config(name, prop_details)
|
|
281
|
+
collection.config.add_property(new_prop)
|
|
282
|
+
logger.info(f" ✅ Property '{name}' added successfully.")
|
|
283
|
+
except Exception as e:
|
|
284
|
+
logger.error(f" ❌ Failed to add property '{name}': {e}")
|
|
285
|
+
else:
|
|
286
|
+
logger.debug(f" Pass: Property '{name}' already exists.")
|
|
287
|
+
|
|
288
|
+
logger.info("✨ Schema Migration Completed!")
|
|
289
|
+
return True
|
|
290
|
+
|
|
291
|
+
except Exception as e:
|
|
292
|
+
logger.error(f"Migration failed: {e}", exc_info=True)
|
|
293
|
+
return False
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def initialize_database():
|
|
297
|
+
try:
|
|
298
|
+
settings = get_weaviate_settings()
|
|
299
|
+
client = get_cached_client()
|
|
300
|
+
if client:
|
|
301
|
+
create_vectorwave_schema(client, settings)
|
|
302
|
+
create_execution_schema(client, settings)
|
|
303
|
+
create_usage_schema(client, settings)
|
|
304
|
+
create_golden_dataset_schema(client, settings)
|
|
305
|
+
return client
|
|
306
|
+
except Exception as e:
|
|
307
|
+
logger.error("Failed to initialize VectorWave database: %s", e)
|
|
308
|
+
return None
|