numpy-vector-store 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.
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NumPy Vector Store - A simple vector store implementation using NumPy.
|
|
3
|
+
|
|
4
|
+
This package provides a lightweight vector store implementation for storing
|
|
5
|
+
and searching vector embeddings using NumPy arrays.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
__author__ = "Tim VanReenen"
|
|
10
|
+
|
|
11
|
+
from .vector_store import VectorStore
|
|
12
|
+
|
|
13
|
+
__all__ = ["VectorStore"]
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Optional
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class VectorStore:
|
|
8
|
+
"""
|
|
9
|
+
A simple vector store implementation using NumPy.
|
|
10
|
+
|
|
11
|
+
This class provides basic functionality for storing and searching
|
|
12
|
+
vector embeddings using cosine similarity.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
dimensions: int,
|
|
18
|
+
file_path: Optional[str] = None,
|
|
19
|
+
metadata_schema: Optional[dict] = None,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Initialize the vector store.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
dimensions: The number of dimensions for vectors to be stored.
|
|
26
|
+
file_path: Optional path to save/load vectors from.
|
|
27
|
+
metadata_schema: Optional schema for structured metadata operations.
|
|
28
|
+
"""
|
|
29
|
+
self.dimensions = dimensions
|
|
30
|
+
self.file_path = Path(file_path) if file_path else None
|
|
31
|
+
# Initialize as empty 2D array with correct dimensions
|
|
32
|
+
self.vectors: np.ndarray = np.array([]).reshape(0, dimensions)
|
|
33
|
+
self._loaded = False
|
|
34
|
+
|
|
35
|
+
if metadata_schema:
|
|
36
|
+
# Use structured NumPy array for performance
|
|
37
|
+
self.metadata = np.array([], dtype=self._create_dtype(metadata_schema))
|
|
38
|
+
self._use_structured = True
|
|
39
|
+
self._metadata_schema: Optional[dict] = metadata_schema
|
|
40
|
+
else:
|
|
41
|
+
# Use object array for flexibility (still NumPy)
|
|
42
|
+
self.metadata = np.array([], dtype=object)
|
|
43
|
+
self._use_structured = False
|
|
44
|
+
self._metadata_schema = None
|
|
45
|
+
|
|
46
|
+
def _create_dtype(self, schema: dict) -> list:
|
|
47
|
+
"""Convert schema dict to NumPy dtype."""
|
|
48
|
+
dtype = []
|
|
49
|
+
for field, field_type in schema.items():
|
|
50
|
+
if isinstance(field_type, str):
|
|
51
|
+
dtype.append((field, field_type))
|
|
52
|
+
else:
|
|
53
|
+
dtype.append((field, "O")) # Object type for complex data
|
|
54
|
+
return dtype
|
|
55
|
+
|
|
56
|
+
def add_vectors(self, vectors_2d: np.ndarray, metadata_array: np.ndarray) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Add vectors and metadata directly as NumPy arrays (most efficient).
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
vectors_2d: 2D NumPy array of shape (n_vectors, dimensions)
|
|
62
|
+
metadata_array: 1D NumPy array of metadata objects
|
|
63
|
+
"""
|
|
64
|
+
if vectors_2d.ndim != 2:
|
|
65
|
+
raise ValueError("vectors_2d must be a 2D NumPy array")
|
|
66
|
+
|
|
67
|
+
if metadata_array.ndim != 1:
|
|
68
|
+
raise ValueError("metadata_array must be a 1D NumPy array")
|
|
69
|
+
|
|
70
|
+
if vectors_2d.shape[1] != self.dimensions:
|
|
71
|
+
raise ValueError(
|
|
72
|
+
f"Vector dimensions {vectors_2d.shape[1]} doesn't match store dimensions {self.dimensions}"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if len(vectors_2d) != len(metadata_array):
|
|
76
|
+
raise ValueError("Number of vectors must match number of metadata items")
|
|
77
|
+
|
|
78
|
+
# Normalize vectors in batch
|
|
79
|
+
norms = np.linalg.norm(vectors_2d, axis=1, keepdims=True)
|
|
80
|
+
if np.any(norms == 0):
|
|
81
|
+
raise ValueError("Cannot add zero-norm vectors")
|
|
82
|
+
normalized_vectors = vectors_2d / norms
|
|
83
|
+
|
|
84
|
+
# Add vectors directly
|
|
85
|
+
if len(self.vectors) == 0:
|
|
86
|
+
self.vectors = normalized_vectors
|
|
87
|
+
else:
|
|
88
|
+
self.vectors = np.vstack([self.vectors, normalized_vectors])
|
|
89
|
+
|
|
90
|
+
# Add metadata directly
|
|
91
|
+
self.metadata = np.append(self.metadata, metadata_array)
|
|
92
|
+
|
|
93
|
+
def load(self) -> None:
|
|
94
|
+
"""
|
|
95
|
+
Load vectors from file if file_path is specified.
|
|
96
|
+
"""
|
|
97
|
+
if self._loaded or not self.file_path:
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
if self.file_path.exists():
|
|
101
|
+
data = np.load(self.file_path, allow_pickle=True)
|
|
102
|
+
loaded_vectors = data["vectors"]
|
|
103
|
+
loaded_metadata = data["metadata"]
|
|
104
|
+
data.close()
|
|
105
|
+
|
|
106
|
+
if loaded_vectors.ndim != 2:
|
|
107
|
+
raise ValueError("Loaded vectors must be a 2D array")
|
|
108
|
+
|
|
109
|
+
if loaded_vectors.shape[1] != self.dimensions:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"Loaded vector dimension {loaded_vectors.shape[1]} doesn't match store dimensions {self.dimensions}"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if len(loaded_vectors) != len(loaded_metadata):
|
|
115
|
+
raise ValueError("Loaded vectors and metadata length mismatch")
|
|
116
|
+
|
|
117
|
+
if self._use_structured and self._metadata_schema is not None:
|
|
118
|
+
expected_fields = tuple(self._metadata_schema.keys())
|
|
119
|
+
loaded_fields = loaded_metadata.dtype.names
|
|
120
|
+
if loaded_fields != expected_fields:
|
|
121
|
+
raise ValueError(
|
|
122
|
+
f"Loaded metadata schema {loaded_fields} doesn't match expected schema {expected_fields}"
|
|
123
|
+
)
|
|
124
|
+
loaded_metadata = loaded_metadata.astype(
|
|
125
|
+
np.dtype(self._create_dtype(self._metadata_schema)), copy=False
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
self.vectors = loaded_vectors
|
|
129
|
+
self.metadata = loaded_metadata
|
|
130
|
+
|
|
131
|
+
self._loaded = True
|
|
132
|
+
|
|
133
|
+
def save(self) -> None:
|
|
134
|
+
"""
|
|
135
|
+
Save vectors to file if file_path is specified.
|
|
136
|
+
"""
|
|
137
|
+
if not self.file_path:
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
vectors_array: np.ndarray = self.vectors.astype(np.float32)
|
|
141
|
+
# Preserve metadata dtype so structured arrays remain structured
|
|
142
|
+
# after save/load round-trips.
|
|
143
|
+
metadata_array = np.array(self.metadata, copy=True)
|
|
144
|
+
np.savez_compressed(self.file_path, vectors=vectors_array, metadata=metadata_array)
|
|
145
|
+
|
|
146
|
+
def search(
|
|
147
|
+
self, query_vector: np.ndarray, top_k: int = 10, score_cutoff: float = 0.0
|
|
148
|
+
) -> list[tuple[int, float, dict]]:
|
|
149
|
+
"""
|
|
150
|
+
Search for the most similar vectors.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
query_vector: The query vector to search with.
|
|
154
|
+
top_k: Number of top results to return.
|
|
155
|
+
score_cutoff: Minimum similarity score to include in results.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
List of tuples containing (index, similarity_score, metadata).
|
|
159
|
+
"""
|
|
160
|
+
if len(query_vector) != self.dimensions:
|
|
161
|
+
raise ValueError(
|
|
162
|
+
f"Query vector dimension {len(query_vector)} doesn't match store dimensions {self.dimensions}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
if top_k <= 0:
|
|
166
|
+
raise ValueError("top_k must be greater than 0")
|
|
167
|
+
|
|
168
|
+
if len(self.vectors) == 0:
|
|
169
|
+
return []
|
|
170
|
+
|
|
171
|
+
# Use efficient cosine similarity computation
|
|
172
|
+
similarities = self._cosine_similarity_numpy(query_vector, self.vectors)
|
|
173
|
+
|
|
174
|
+
# Filter by similarity cutoff first
|
|
175
|
+
valid_indices = np.where(similarities >= score_cutoff)[0]
|
|
176
|
+
if len(valid_indices) == 0:
|
|
177
|
+
return []
|
|
178
|
+
|
|
179
|
+
# Get top_k results from valid indices
|
|
180
|
+
valid_similarities = similarities[valid_indices]
|
|
181
|
+
top_valid_indices = np.argsort(valid_similarities)[::-1][:top_k]
|
|
182
|
+
top_indices = valid_indices[top_valid_indices]
|
|
183
|
+
|
|
184
|
+
results = []
|
|
185
|
+
for idx in top_indices:
|
|
186
|
+
results.append((int(idx), float(similarities[idx]), self.metadata[idx]))
|
|
187
|
+
|
|
188
|
+
return results
|
|
189
|
+
|
|
190
|
+
def _cosine_similarity_numpy(
|
|
191
|
+
self, query: np.ndarray, vectors: np.ndarray
|
|
192
|
+
) -> np.ndarray:
|
|
193
|
+
"""
|
|
194
|
+
Efficient cosine similarity computation using NumPy.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
query: Query vector.
|
|
198
|
+
vectors: Array of vectors to compare against.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Array of similarity scores.
|
|
202
|
+
"""
|
|
203
|
+
query_magnitude = np.linalg.norm(query)
|
|
204
|
+
if query_magnitude == 0:
|
|
205
|
+
raise ValueError("Cannot search with zero-norm query vector")
|
|
206
|
+
query_norm = query / query_magnitude
|
|
207
|
+
vector_norms = np.linalg.norm(vectors, axis=1, keepdims=True)
|
|
208
|
+
if np.any(vector_norms == 0):
|
|
209
|
+
raise ValueError("Store contains zero-norm vectors")
|
|
210
|
+
vectors_norm = vectors / vector_norms
|
|
211
|
+
return np.dot(vectors_norm, query_norm) # type: ignore[no-any-return]
|
|
212
|
+
|
|
213
|
+
def get(self, index: int) -> Optional[tuple[np.ndarray, dict]]:
|
|
214
|
+
"""
|
|
215
|
+
Get vector and metadata by index.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
index: The index of the vector to retrieve.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Tuple of (vector, metadata) or None if index is out of bounds.
|
|
222
|
+
"""
|
|
223
|
+
if 0 <= index < len(self.vectors):
|
|
224
|
+
if self._use_structured and self._metadata_schema is not None:
|
|
225
|
+
# Convert structured array row to dict
|
|
226
|
+
metadata_row = self.metadata[index]
|
|
227
|
+
metadata_dict = {
|
|
228
|
+
field: metadata_row[field] for field in self._metadata_schema.keys()
|
|
229
|
+
}
|
|
230
|
+
return (self.vectors[index], metadata_dict)
|
|
231
|
+
else:
|
|
232
|
+
# Object array - return the dict directly
|
|
233
|
+
return (self.vectors[index], self.metadata[index])
|
|
234
|
+
return None
|
|
235
|
+
|
|
236
|
+
def clear(self) -> None:
|
|
237
|
+
"""Clear all vectors and metadata from the store."""
|
|
238
|
+
self.vectors = np.array([]).reshape(0, self.dimensions)
|
|
239
|
+
if self._use_structured and self._metadata_schema is not None:
|
|
240
|
+
self.metadata = np.array(
|
|
241
|
+
[], dtype=self._create_dtype(self._metadata_schema)
|
|
242
|
+
)
|
|
243
|
+
else:
|
|
244
|
+
self.metadata = np.array([], dtype=object)
|
|
245
|
+
|
|
246
|
+
def __enter__(self) -> "VectorStore":
|
|
247
|
+
"""Enter the context manager."""
|
|
248
|
+
return self
|
|
249
|
+
|
|
250
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
251
|
+
"""Exit the context manager, auto-save if file_path is specified."""
|
|
252
|
+
if self.file_path:
|
|
253
|
+
self.save()
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: numpy-vector-store
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A fast, lightweight, and zero-setup in-memory vector store powered by NumPy
|
|
5
|
+
Project-URL: Homepage, https://github.com/tvanreenen/numpy-vector-store
|
|
6
|
+
Project-URL: Repository, https://github.com/tvanreenen/numpy-vector-store
|
|
7
|
+
Project-URL: Issues, https://github.com/tvanreenen/numpy-vector-store/issues
|
|
8
|
+
Author: Tim VanReenen
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: embeddings,numpy,search,store,vector
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Requires-Dist: numpy>=1.20.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# NumPy Vector Store
|
|
25
|
+
|
|
26
|
+
A fast, lightweight, and zero-setup in-memory vector store powered by NumPy.
|
|
27
|
+
|
|
28
|
+
- **High-performance** index-free O(n) cosine similarity searches
|
|
29
|
+
- **Vectorized** metadata queries with NumPy operations
|
|
30
|
+
- **Easy persistence** to and from compressed NumPy binary files (.npz)
|
|
31
|
+
- **Zero dependency** on external services or data stores
|
|
32
|
+
|
|
33
|
+
## Why?
|
|
34
|
+
|
|
35
|
+
This library is purpose-built for small to medium-scale vector search tasks and offers a simple, lightweight alternative to heavyweight solutions like Pinecone, Qdrant, Weaviate, Postgres + pgvector, or Azure AI Search—no complex setup or infrastructure required.
|
|
36
|
+
|
|
37
|
+
> Sometimes you don't need a sledgehammer to crack a nut.
|
|
38
|
+
|
|
39
|
+
## When/Where?
|
|
40
|
+
|
|
41
|
+
Below are benchmark results for the module's search method to help you assess its suitability for your use case.
|
|
42
|
+
|
|
43
|
+
| Embedding Type | Dimensions | ~5ms | ~25ms | ~100ms | ~500ms |
|
|
44
|
+
|----------------|------------|------|-------|--------|--------|
|
|
45
|
+
| **Sentence Transformers** | 384 | 1K vectors<br/>1.5MB | 10K vectors<br/>15MB | 100K vectors<br/>147MB | 500K vectors<br/>732MB |
|
|
46
|
+
| **OpenAI Small** | 1536 | 500 vectors<br/>3MB | 5K vectors<br/>29MB | 25K vectors<br/>147MB | 100K vectors<br/>586MB |
|
|
47
|
+
| **OpenAI Large** | 3072 | 200 vectors<br/>2MB | 2.5K vectors<br/>29MB | 5K vectors<br/>59MB | 25K vectors<br/>293MB |
|
|
48
|
+
|
|
49
|
+
*Benchmarks performed on Apple M2 hardware*
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
**⚠️ Pending submission to PyPI**
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv add numpy-vector-store
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import numpy as np
|
|
63
|
+
from numpy_vector_store import VectorStore
|
|
64
|
+
|
|
65
|
+
# Load your vector store
|
|
66
|
+
store = VectorStore(dimensions=1536, file_path='vectors.npz')
|
|
67
|
+
store.load()
|
|
68
|
+
|
|
69
|
+
# Embed your search query
|
|
70
|
+
query = np.array([0.2, 0.3, 0.4, ...])
|
|
71
|
+
|
|
72
|
+
# Search using cosine similarity
|
|
73
|
+
results = store.search(query, top_k=3)
|
|
74
|
+
|
|
75
|
+
# Compare the results
|
|
76
|
+
for index, similarity, meta in results:
|
|
77
|
+
print(f"{meta['title']}: {similarity:.3f}")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Usage Examples
|
|
81
|
+
|
|
82
|
+
### Adding Vectors
|
|
83
|
+
|
|
84
|
+
#### Adding Vectors in Batch
|
|
85
|
+
|
|
86
|
+
The `add_vectors` method takes a **2D NumPy array** where each row is a vector, and a **1D NumPy array** of metadata objects.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
# 2D np.array of vectors
|
|
90
|
+
embeddings = np.array([
|
|
91
|
+
[0.1, 0.2, 0.3, ...], # Text embedding 1
|
|
92
|
+
[0.4, 0.5, 0.6, ...], # Text embedding 2
|
|
93
|
+
[0.7, 0.8, 0.9, ...] # Text embedding 3
|
|
94
|
+
])
|
|
95
|
+
|
|
96
|
+
# 1D np.array of metadata objects
|
|
97
|
+
metadata = np.array([
|
|
98
|
+
{"title": "AI Overview", "word_count": 12},
|
|
99
|
+
{"title": "Python Guide", "word_count": 10},
|
|
100
|
+
{"title": "Vector DBs", "word_count": 8}
|
|
101
|
+
])
|
|
102
|
+
|
|
103
|
+
# Vectors and metadata added using efficient vectorized NumPy operations
|
|
104
|
+
store.add_vectors(embeddings, metadata)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### Adding Vectors Individually
|
|
108
|
+
|
|
109
|
+
Batch operations are generally preferable and more efficient. But individual vectors can be added with the same method.
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
store.add_vectors(
|
|
113
|
+
np.array([new_embedding]), # or np.atleast_2d(new_embedding)
|
|
114
|
+
np.array([{"title": "Neural Networks Paper", "word_count": 15}])
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Save the Vector Store
|
|
119
|
+
|
|
120
|
+
Save to a gzip compressed NumPy binary file directly
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
store.save()
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Or use context manager for automatic persistence
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
with VectorStore(dimensions=3, file_path="vectors.npz") as store:
|
|
130
|
+
store.add_vectors(vectors_2d, metadata_array)
|
|
131
|
+
|
|
132
|
+
# Automatically saves when exiting the context
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Working with Metadata
|
|
136
|
+
|
|
137
|
+
Work with flexible, unstructured metadata using standard Python operations. No schema required - perfect for getting started quickly:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
# Access individual metadata
|
|
141
|
+
first_metadata = store.metadata[0]
|
|
142
|
+
print(f"First entry: {first_metadata}")
|
|
143
|
+
|
|
144
|
+
# Iterate through all metadata
|
|
145
|
+
for i, metadata in enumerate(store.metadata):
|
|
146
|
+
print(f"Entry {i}: {metadata}")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Advanced: Structured Metadata for Performance
|
|
150
|
+
|
|
151
|
+
If you define a homogeneous NumPy schema upfront, you get **significant performance improvements** for metadata operations. Instead of Python loops and dictionary lookups, you get **vectorized NumPy operations** that are orders of magnitude faster.
|
|
152
|
+
|
|
153
|
+
> **Learn more**: [NumPy Structured Arrays Documentation](https://numpy.org/doc/stable/user/basics.rec.html)
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# Define schema for performance
|
|
157
|
+
store = VectorStore(
|
|
158
|
+
dimensions=512,
|
|
159
|
+
metadata_schema={'title': 'U200', 'year': 'i4', 'citations': 'i4'}
|
|
160
|
+
)
|
|
161
|
+
store.add_vectors(
|
|
162
|
+
np.array([vector1, vector2]),
|
|
163
|
+
np.array([
|
|
164
|
+
{"title": "Paper 1", "year": 2023, "citations": 100},
|
|
165
|
+
{"title": "Paper 2", "year": 2022, "citations": 50}
|
|
166
|
+
])
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Perform vectorized NumPy operations on metadata
|
|
170
|
+
recent_mask = store.metadata['year'] == 2023
|
|
171
|
+
recent_vectors = np.array(store.vectors)[recent_mask]
|
|
172
|
+
|
|
173
|
+
# Sort by citations
|
|
174
|
+
sorted_indices = np.argsort(store.metadata['citations'])[::-1]
|
|
175
|
+
top_vectors = np.array(store.vectors)[sorted_indices]
|
|
176
|
+
|
|
177
|
+
# Complex filtering
|
|
178
|
+
high_impact = store.metadata['citations'] > 100
|
|
179
|
+
recent = store.metadata['year'] > 2020
|
|
180
|
+
combined_mask = high_impact & recent
|
|
181
|
+
filtered_vectors = np.array(store.vectors)[combined_mask]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Contributing
|
|
185
|
+
|
|
186
|
+
### Setup Development Environment
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
git clone https://github.com/tvanreenen/numpy-vector-store.git
|
|
190
|
+
cd numpy-vector-store
|
|
191
|
+
uv sync --frozen --group dev
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Before Submitting a Pull Request
|
|
195
|
+
|
|
196
|
+
Please ensure:
|
|
197
|
+
|
|
198
|
+
1. **Code Quality**: Run `uv run ruff check` - should show no issues
|
|
199
|
+
2. **Formatting**: Run `uv run ruff format` - should show "files left unchanged"
|
|
200
|
+
3. **Type Checking**: Run `uv run mypy src/` - should show no errors
|
|
201
|
+
4. **Tests**: Run `uv run pytest` - all tests should pass
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
numpy_vector_store/__init__.py,sha256=0_LXVUazbViKSm_aaZvRX7kEIti9YoIATGV31ycifKo,326
|
|
2
|
+
numpy_vector_store/vector_store.py,sha256=-FFqRg0-tG6xJdz8eg0aaUdIqq5WB9LUf3FTLfdTQRI,9399
|
|
3
|
+
numpy_vector_store-0.1.0.dist-info/METADATA,sha256=kK2_AnERaUVSWtEY3SCT9nWDpVt1NPNZLqg8l1Nc424,6478
|
|
4
|
+
numpy_vector_store-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
5
|
+
numpy_vector_store-0.1.0.dist-info/licenses/LICENSE,sha256=SUVvvB3m6p_RRGYL6l9PPdlUfg39dxYNqeF0b9-z_bg,1070
|
|
6
|
+
numpy_vector_store-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tim VanReenen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|