numpy-vector-store 0.3.2__tar.gz → 0.4.0__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.
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/.github/workflows/checks.yml +5 -6
- numpy_vector_store-0.4.0/CHANGELOG.md +160 -0
- numpy_vector_store-0.4.0/MIGRATION.md +169 -0
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/PKG-INFO +88 -36
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/README.md +85 -32
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/ROADMAP.md +101 -16
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/pyproject.toml +4 -5
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/src/numpy_vector_store/__init__.py +1 -1
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/src/numpy_vector_store/vector_store.py +251 -34
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/tests/test_vector_store.py +538 -10
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/uv.lock +4 -110
- numpy_vector_store-0.3.2/CHANGELOG.md +0 -73
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/.github/FUNDING.yml +0 -0
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/.github/workflows/publish-pypi.yml +0 -0
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/.github/workflows/publish-testpypi.yml +0 -0
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/.gitignore +0 -0
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/LICENSE +0 -0
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/justfile +0 -0
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/src/numpy_vector_store/py.typed +0 -0
- {numpy_vector_store-0.3.2 → numpy_vector_store-0.4.0}/tests/__init__.py +0 -0
|
@@ -55,7 +55,6 @@ jobs:
|
|
|
55
55
|
fail-fast: false
|
|
56
56
|
matrix:
|
|
57
57
|
python-version:
|
|
58
|
-
- "3.10"
|
|
59
58
|
- "3.11"
|
|
60
59
|
- "3.12"
|
|
61
60
|
- "3.13"
|
|
@@ -84,7 +83,7 @@ jobs:
|
|
|
84
83
|
run: uv run --locked pytest
|
|
85
84
|
|
|
86
85
|
minimum-numpy:
|
|
87
|
-
name: Test minimum NumPy on Python 3.
|
|
86
|
+
name: Test minimum NumPy on Python 3.11
|
|
88
87
|
runs-on: ubuntu-latest
|
|
89
88
|
|
|
90
89
|
steps:
|
|
@@ -94,13 +93,13 @@ jobs:
|
|
|
94
93
|
- name: Set up Python
|
|
95
94
|
uses: actions/setup-python@v6
|
|
96
95
|
with:
|
|
97
|
-
python-version: "3.
|
|
96
|
+
python-version: "3.11"
|
|
98
97
|
|
|
99
98
|
- name: Set up uv
|
|
100
99
|
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
|
101
100
|
with:
|
|
102
101
|
version: "0.11.32"
|
|
103
|
-
python-version: "3.
|
|
102
|
+
python-version: "3.11"
|
|
104
103
|
enable-cache: true
|
|
105
104
|
|
|
106
105
|
- name: Run tests with the minimum NumPy version
|
|
@@ -108,8 +107,8 @@ jobs:
|
|
|
108
107
|
uv run
|
|
109
108
|
--isolated
|
|
110
109
|
--no-project
|
|
111
|
-
--python 3.
|
|
110
|
+
--python 3.11
|
|
112
111
|
--with-editable .
|
|
113
|
-
--with numpy==1.
|
|
112
|
+
--with numpy==1.23.2
|
|
114
113
|
--with pytest==8.4.2
|
|
115
114
|
python -m pytest
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
This changelog records user-visible changes to NumPy Vector Store. Earlier
|
|
4
|
+
release notes remain available on the
|
|
5
|
+
[GitHub releases page](https://github.com/tvanreenen/numpy-vector-store/releases).
|
|
6
|
+
|
|
7
|
+
## 0.4.0 - 2026-08-09
|
|
8
|
+
|
|
9
|
+
This release makes persistence explicit, self-describing, and safer to update.
|
|
10
|
+
The earlier archive format stored vectors and metadata but omitted the settings
|
|
11
|
+
needed to interpret those vectors correctly. Version 0.4 records that
|
|
12
|
+
configuration in every new archive and introduces a lifecycle that clearly
|
|
13
|
+
separates creating, opening, saving, and reloading a store.
|
|
14
|
+
|
|
15
|
+
### Explicit persistence lifecycle
|
|
16
|
+
|
|
17
|
+
- Add `VectorStore.open(path)` to construct a store from a versioned archive.
|
|
18
|
+
The archive supplies its own dimensions and normalization mode, so callers no
|
|
19
|
+
longer need to repeat configuration that may be wrong.
|
|
20
|
+
- Let `save(path)` perform the first save or a Save As operation and bind that
|
|
21
|
+
destination. Later `save()` calls update the bound archive.
|
|
22
|
+
- Add `reload()` as a deliberate refresh from disk. It always attempts to read
|
|
23
|
+
the bound archive and leaves current in-memory state unchanged if reading or
|
|
24
|
+
validation fails.
|
|
25
|
+
- Keep creating a new in-memory store separate from opening one on disk. This
|
|
26
|
+
makes file access and persistence boundaries visible in application code.
|
|
27
|
+
|
|
28
|
+
### Versioned, self-describing archives
|
|
29
|
+
|
|
30
|
+
- Write archive format version 1 with `format_version`, `dimensions`,
|
|
31
|
+
`normalize`, `vectors`, and `metadata` fields.
|
|
32
|
+
- Validate the complete archive before changing live store state, including
|
|
33
|
+
field names, scalar configuration, array dtypes and shapes, row counts,
|
|
34
|
+
finite vector values, and normalized-store zero-vector rules.
|
|
35
|
+
- Reject unsupported format versions and malformed archives clearly rather
|
|
36
|
+
than inferring missing configuration or partially applying valid fields.
|
|
37
|
+
- Continue preserving each metadata item as one opaque row payload.
|
|
38
|
+
|
|
39
|
+
### Safer archive replacement
|
|
40
|
+
|
|
41
|
+
- Write each save to a uniquely named temporary archive in the destination
|
|
42
|
+
directory, close it, and then replace the destination with `os.replace`.
|
|
43
|
+
- Preserve the previous complete archive when writing or replacement fails and
|
|
44
|
+
clean up temporary files after failures.
|
|
45
|
+
- Bind a new Save As destination only after its archive has been written
|
|
46
|
+
successfully.
|
|
47
|
+
|
|
48
|
+
This provides an atomic visibility boundary: a reader opening the destination
|
|
49
|
+
sees the previous complete archive or the new complete archive instead of a
|
|
50
|
+
partially written file. It does not provide file locking, multi-writer
|
|
51
|
+
coordination, or a universal power-loss durability guarantee.
|
|
52
|
+
|
|
53
|
+
### Short migration window
|
|
54
|
+
|
|
55
|
+
- Keep constructor `file_path=`, instance `load()`, and direct context-manager
|
|
56
|
+
persistence for version 0.4 with `FutureWarning`. They will be removed in
|
|
57
|
+
0.5.
|
|
58
|
+
- Make the deprecated context manager save only after a successful block. If
|
|
59
|
+
the block raises, it does not save or suppress the exception.
|
|
60
|
+
- Keep a configuration-aware reader for older archives containing only
|
|
61
|
+
`vectors` and `metadata`. Loading one warns, and its next save rewrites it as
|
|
62
|
+
format version 1.
|
|
63
|
+
- Intentionally make `open()` reject an unversioned archive because that file
|
|
64
|
+
cannot report its original dimensions or normalization semantics.
|
|
65
|
+
- Add a dedicated [persistence migration guide](MIGRATION.md) with side-by-side
|
|
66
|
+
API replacements and one-time legacy archive conversion instructions.
|
|
67
|
+
|
|
68
|
+
The legacy API and unversioned archive reader are removed in 0.5. Applications
|
|
69
|
+
should migrate an old archive once with 0.4 or recreate it from source data;
|
|
70
|
+
indefinite compatibility with the incomplete two-array format is not planned.
|
|
71
|
+
|
|
72
|
+
### Runtime compatibility
|
|
73
|
+
|
|
74
|
+
- Support Python 3.11 through 3.14. Python 3.10 remains supported by the 0.3
|
|
75
|
+
release series but is not supported by 0.4.
|
|
76
|
+
- Raise the minimum NumPy version from 1.21.3 to 1.23.2, the earliest release
|
|
77
|
+
that supports Python 3.11.
|
|
78
|
+
- Exercise every supported Python version in CI and test NumPy 1.23.2 in a
|
|
79
|
+
dedicated minimum-dependency job.
|
|
80
|
+
|
|
81
|
+
### Upgrade notes and boundaries
|
|
82
|
+
|
|
83
|
+
- Search, insertion, retrieval, clearing, normalization, and metadata behavior
|
|
84
|
+
are unchanged from 0.3.2.
|
|
85
|
+
- Code using `VectorStore.open()`, `save(path)`, `save()`, and `reload()` is on
|
|
86
|
+
the persistence API intended for 0.5.
|
|
87
|
+
- Code using a transitional entry point continues to work in 0.4 but emits a
|
|
88
|
+
warning so the required 0.5 migration is visible during testing.
|
|
89
|
+
- Metadata still uses NumPy's pickle-backed object-array loading. Only open
|
|
90
|
+
archives produced by your application or another trusted source.
|
|
91
|
+
- Mutable public state, repeated-add performance, deterministic tie ordering,
|
|
92
|
+
and a formal thread-safety contract remain planned for later releases.
|
|
93
|
+
|
|
94
|
+
## 0.3.2 - 2026-07-27
|
|
95
|
+
|
|
96
|
+
This reliability and performance patch makes existing vector storage, search,
|
|
97
|
+
metadata, and persistence behavior safer and more predictable. It does not
|
|
98
|
+
intentionally break valid existing usage or change the `.npz` archive format.
|
|
99
|
+
|
|
100
|
+
### Numerical reliability
|
|
101
|
+
|
|
102
|
+
- Reject vectors, queries, and search thresholds that contain non-finite values
|
|
103
|
+
or cannot remain finite when represented as `float32`. Invalid input now
|
|
104
|
+
fails before it can corrupt stored state or ranking.
|
|
105
|
+
- Calculate norms and raw metric intermediates with `float64` where `float32`
|
|
106
|
+
could overflow or underflow. Large and very small finite vectors can now be
|
|
107
|
+
normalized and compared reliably.
|
|
108
|
+
- Allow zero vectors in stores created with `normalize=False`, where they are
|
|
109
|
+
valid for dot-product and Euclidean search.
|
|
110
|
+
- Raise a clear error if a raw cosine search includes a zero vector, because
|
|
111
|
+
cosine similarity is undefined for that row.
|
|
112
|
+
- Avoid duplicate full-size `float64` buffers when calculating raw Euclidean
|
|
113
|
+
distance.
|
|
114
|
+
|
|
115
|
+
### Persistence
|
|
116
|
+
|
|
117
|
+
- Resolve a path without an `.npz` suffix to the same archive for both saving
|
|
118
|
+
and loading. For example, `file_path="vectors"` consistently uses
|
|
119
|
+
`vectors.npz`.
|
|
120
|
+
- Allow `load()` to be retried when the persistence file did not exist during
|
|
121
|
+
an earlier attempt.
|
|
122
|
+
- Reset load state in `clear()` so a subsequent explicit `load()` can restore
|
|
123
|
+
the saved rows.
|
|
124
|
+
- Keep repeated `load()` calls idempotent after a successful load.
|
|
125
|
+
|
|
126
|
+
### Metadata
|
|
127
|
+
|
|
128
|
+
- Preserve each item in the outer metadata sequence as one opaque row payload.
|
|
129
|
+
Tuples and lists are no longer mistaken for extra NumPy array dimensions.
|
|
130
|
+
- Support dictionary, dataclass, tuple, list, string, integer, and other scalar
|
|
131
|
+
payloads consistently through insertion, search results, saving, and loading.
|
|
132
|
+
- Continue rejecting explicitly multidimensional NumPy metadata arrays rather
|
|
133
|
+
than silently flattening ambiguous input.
|
|
134
|
+
|
|
135
|
+
### Search memory use
|
|
136
|
+
|
|
137
|
+
- Search the stored vector matrix directly when `within_rows` is omitted,
|
|
138
|
+
avoiding an unnecessary full-matrix copy on every unfiltered query.
|
|
139
|
+
- Preserve original store indexes and metadata when `within_rows` selects a
|
|
140
|
+
filtered subset.
|
|
141
|
+
- Document that filtered searches allocate a temporary matrix proportional to
|
|
142
|
+
the selected row count and vector dimensions.
|
|
143
|
+
|
|
144
|
+
### Compatibility and validation
|
|
145
|
+
|
|
146
|
+
- Test Python 3.10 through 3.14 in GitHub Actions.
|
|
147
|
+
- Test the minimum supported NumPy version in a dedicated Python 3.10 job.
|
|
148
|
+
- Raise the minimum NumPy requirement from 1.20 to 1.21.3 so it is compatible
|
|
149
|
+
with the oldest supported Python version.
|
|
150
|
+
- Require linting, formatting, type checking, and the full Python test matrix
|
|
151
|
+
before publishing to PyPI.
|
|
152
|
+
|
|
153
|
+
### Upgrade notes
|
|
154
|
+
|
|
155
|
+
- No public method signatures or persisted field names changed.
|
|
156
|
+
- Existing trusted `.npz` archives with `vectors` and `metadata` remain
|
|
157
|
+
readable.
|
|
158
|
+
- Environments using NumPy 1.20 must upgrade to NumPy 1.21.3 or newer.
|
|
159
|
+
- Inputs that previously produced `nan`, `inf`, or unreliable rankings now
|
|
160
|
+
raise `ValueError` instead.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Persistence migration guide
|
|
2
|
+
|
|
3
|
+
Version 0.4 introduces a persistence lifecycle that separates creating a new
|
|
4
|
+
store, opening an existing archive, saving, and deliberately refreshing from
|
|
5
|
+
disk. It also provides a one-release bridge for applications and archives using
|
|
6
|
+
the earlier API.
|
|
7
|
+
|
|
8
|
+
The transition is intentionally short. Version 0.4 emits `FutureWarning` for
|
|
9
|
+
constructor `file_path=`, instance `load()`, direct context-manager use, and
|
|
10
|
+
unversioned archives. Version 0.5 removes those entry points and the
|
|
11
|
+
unversioned archive reader.
|
|
12
|
+
|
|
13
|
+
## Creating and saving a new store
|
|
14
|
+
|
|
15
|
+
Previously, the destination was supplied while constructing the store:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
store = VectorStore(dimensions=1536, file_path="vectors.npz")
|
|
19
|
+
store.add(vectors, metadata)
|
|
20
|
+
store.save()
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Create the in-memory store first, then bind its destination with the first
|
|
24
|
+
save:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
store = VectorStore(dimensions=1536)
|
|
28
|
+
store.add(vectors, metadata)
|
|
29
|
+
store.save("vectors.npz")
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Later `save()` calls update the bound archive. Supplying another path performs
|
|
33
|
+
a Save As operation and binds the new destination after the write succeeds.
|
|
34
|
+
Calling `save()` before a store is bound raises `ValueError` rather than
|
|
35
|
+
silently leaving the data unsaved.
|
|
36
|
+
|
|
37
|
+
## Opening an existing archive
|
|
38
|
+
|
|
39
|
+
The old API required callers to repeat configuration that should belong to the
|
|
40
|
+
archive:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
store = VectorStore(
|
|
44
|
+
dimensions=1536,
|
|
45
|
+
file_path="vectors.npz",
|
|
46
|
+
normalize=True,
|
|
47
|
+
)
|
|
48
|
+
store.load()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Open a version 1 archive directly:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
store = VectorStore.open("vectors.npz")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`open()` restores `dimensions` and `normalize` from the archive, validates its
|
|
58
|
+
contents, loads its rows, and binds its path. Applications no longer need to
|
|
59
|
+
keep archive configuration separately or risk loading the same vectors with
|
|
60
|
+
different semantics.
|
|
61
|
+
|
|
62
|
+
The generic parameter still describes application metadata. It can be kept
|
|
63
|
+
when useful:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from dataclasses import dataclass
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True)
|
|
70
|
+
class ChunkMetadata:
|
|
71
|
+
source: str
|
|
72
|
+
chunk_index: int
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
store = VectorStore[ChunkMetadata].open("vectors.npz")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`ChunkMetadata` is an example application type, not a class provided by this
|
|
79
|
+
library.
|
|
80
|
+
|
|
81
|
+
## Refreshing from disk
|
|
82
|
+
|
|
83
|
+
Use `reload()` when another process may have changed the bound archive and the
|
|
84
|
+
current in-memory changes should be discarded:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
store = VectorStore.open("vectors.npz")
|
|
88
|
+
|
|
89
|
+
# Later, after the file may have changed:
|
|
90
|
+
store.reload()
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Unlike transitional `load()`, `reload()` always attempts to read. It raises if
|
|
94
|
+
the store is unbound, the file is missing, or the archive is invalid. A failed
|
|
95
|
+
reload leaves the current in-memory vectors and metadata unchanged.
|
|
96
|
+
|
|
97
|
+
## Replacing context-manager persistence
|
|
98
|
+
|
|
99
|
+
The earlier context manager saved automatically on exit:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
with VectorStore(dimensions=1536, file_path="vectors.npz") as store:
|
|
103
|
+
store.add(vectors, metadata)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Use an explicit save after the work succeeds:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
store = VectorStore(dimensions=1536)
|
|
110
|
+
store.add(vectors, metadata)
|
|
111
|
+
store.save("vectors.npz")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Normal Python control flow already prevents the final line from running if
|
|
115
|
+
`add()` raises. The persistence boundary is visible, and readers do not need to
|
|
116
|
+
remember an implicit exit side effect.
|
|
117
|
+
|
|
118
|
+
During 0.4, the deprecated context manager saves only after normal completion.
|
|
119
|
+
It does not save while an exception is propagating and does not suppress the
|
|
120
|
+
exception. There is no planned replacement autosave context manager.
|
|
121
|
+
|
|
122
|
+
## Migrating an archive created before 0.4
|
|
123
|
+
|
|
124
|
+
Older archives contain only `vectors` and `metadata`. They do not record their
|
|
125
|
+
dimensions or whether vectors use normalized or raw semantics, so `open()`
|
|
126
|
+
cannot construct a correct store from them.
|
|
127
|
+
|
|
128
|
+
Use the 0.4 compatibility API once with the archive's original configuration:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
legacy = VectorStore(
|
|
132
|
+
dimensions=1536,
|
|
133
|
+
file_path="legacy-vectors.npz",
|
|
134
|
+
normalize=True,
|
|
135
|
+
)
|
|
136
|
+
legacy.load()
|
|
137
|
+
legacy.save()
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This code emits transition warnings by design. The final `save()` rewrites the
|
|
141
|
+
archive as format version 1 with `format_version`, `dimensions`, `normalize`,
|
|
142
|
+
`vectors`, and `metadata`. It can then use the preferred API:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
store = VectorStore.open("legacy-vectors.npz")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Applications that can recreate archives from source vectors and metadata may
|
|
149
|
+
choose to do that instead. The unversioned reader is removed in 0.5 rather than
|
|
150
|
+
maintained as a long-term compatibility format.
|
|
151
|
+
|
|
152
|
+
## Removal schedule
|
|
153
|
+
|
|
154
|
+
| Transitional behavior | 0.4 | 0.5 |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| Constructor `file_path=` | Works with `FutureWarning` | Removed |
|
|
157
|
+
| Instance `load()` | Works with `FutureWarning` | Removed |
|
|
158
|
+
| Direct context-manager persistence | Saves only on successful exit and warns | Removed |
|
|
159
|
+
| Unversioned two-array archives | Load with known configuration and warn | Reader removed |
|
|
160
|
+
| `open()`, `save(path)`, `save()`, and `reload()` | Preferred | Supported |
|
|
161
|
+
|
|
162
|
+
## Persistence boundaries that do not change
|
|
163
|
+
|
|
164
|
+
Metadata is stored in a pickle-backed NumPy object array. Archives remain
|
|
165
|
+
trusted input and must not be opened from untrusted or unverifiable sources.
|
|
166
|
+
|
|
167
|
+
Saves use same-directory temporary files and atomic replacement, but the
|
|
168
|
+
library does not add file locking, coordinate concurrent writers, or promise
|
|
169
|
+
power-loss durability across every operating system and filesystem.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: numpy-vector-store
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A fast, lightweight, and zero-setup in-memory vector store powered by NumPy
|
|
5
5
|
Project-URL: Homepage, https://github.com/tvanreenen/numpy-vector-store
|
|
6
6
|
Project-URL: Repository, https://github.com/tvanreenen/numpy-vector-store
|
|
@@ -13,13 +13,12 @@ Classifier: Development Status :: 3 - Alpha
|
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
-
Requires-Python: >=3.
|
|
22
|
-
Requires-Dist: numpy>=1.
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: numpy>=1.23.2
|
|
23
22
|
Description-Content-Type: text/markdown
|
|
24
23
|
|
|
25
24
|
# NumPy Vector Store
|
|
@@ -30,7 +29,7 @@ A fast, lightweight, zero-setup in-memory vector store powered by NumPy.
|
|
|
30
29
|
- **Fast exact vector search** using vectorized NumPy operations
|
|
31
30
|
- **Simple typed API** returning `VectorHit(index, value, metadata)`
|
|
32
31
|
- **Composable filtering** by passing prefiltered row indexes with `within_rows`
|
|
33
|
-
- **Portable persistence** as trusted local `.npz` files
|
|
32
|
+
- **Portable persistence** as versioned, self-describing trusted local `.npz` files
|
|
34
33
|
- **No framework opinions**: bring your own embeddings, chunking, async, and metadata model
|
|
35
34
|
|
|
36
35
|
## Why?
|
|
@@ -210,54 +209,102 @@ for hit in hits:
|
|
|
210
209
|
|
|
211
210
|
## Persistence
|
|
212
211
|
|
|
213
|
-
|
|
212
|
+
Create a new store normally, then supply its destination on the first save:
|
|
214
213
|
|
|
215
214
|
```python
|
|
216
|
-
store = VectorStore[dict[str, str]](dimensions=1536
|
|
215
|
+
store = VectorStore[dict[str, str]](dimensions=1536)
|
|
217
216
|
store.add(embeddings, metadata)
|
|
218
|
-
store.save()
|
|
217
|
+
store.save("vectors.npz")
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`save(path)` writes the archive and binds that path to the store. Later
|
|
221
|
+
`save()` calls update the bound archive. Passing another path performs a Save
|
|
222
|
+
As operation; the new path becomes the binding only after the write succeeds.
|
|
223
|
+
|
|
224
|
+
Open an existing store directly from its self-describing archive:
|
|
219
225
|
|
|
220
|
-
|
|
221
|
-
|
|
226
|
+
```python
|
|
227
|
+
store = VectorStore[dict[str, str]].open("vectors.npz")
|
|
222
228
|
```
|
|
223
229
|
|
|
230
|
+
`open()` restores `dimensions` and `normalize` from the archive and binds its
|
|
231
|
+
path. Use `reload()` when the file may have changed externally and you
|
|
232
|
+
explicitly want to discard current in-memory changes:
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
store.reload()
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Unlike the transitional `load()` method, `reload()` always rereads the bound
|
|
239
|
+
archive and raises if the store is unbound, the file is missing, or the archive
|
|
240
|
+
is invalid. A failed reload leaves the current in-memory vectors and metadata
|
|
241
|
+
unchanged.
|
|
242
|
+
|
|
224
243
|
The `.npz` suffix may be omitted. An extensionless path such as `"vectors"` is
|
|
225
|
-
resolved to `"vectors.npz"` for
|
|
244
|
+
resolved to `"vectors.npz"` for saving, opening, and reloading.
|
|
226
245
|
|
|
227
|
-
|
|
246
|
+
Raw-vector configuration is also restored from the archive:
|
|
228
247
|
|
|
229
248
|
```python
|
|
230
249
|
store = VectorStore[dict[str, str]](
|
|
231
250
|
dimensions=1536,
|
|
232
|
-
file_path="raw-vectors.npz",
|
|
233
251
|
normalize=False,
|
|
234
252
|
)
|
|
235
253
|
store.add(raw_vectors, metadata)
|
|
236
|
-
store.save()
|
|
254
|
+
store.save("raw-vectors.npz")
|
|
237
255
|
|
|
238
|
-
loaded = VectorStore[dict[str, str]](
|
|
239
|
-
|
|
240
|
-
file_path="raw-vectors.npz",
|
|
241
|
-
normalize=False,
|
|
242
|
-
)
|
|
243
|
-
loaded.load()
|
|
256
|
+
loaded = VectorStore[dict[str, str]].open("raw-vectors.npz")
|
|
257
|
+
assert loaded.normalize is False
|
|
244
258
|
```
|
|
245
259
|
|
|
246
|
-
|
|
260
|
+
Archives written by 0.4 use format version 1 and contain `format_version`,
|
|
261
|
+
`dimensions`, `normalize`, `vectors`, and `metadata`. The stored configuration
|
|
262
|
+
prevents an archive from being loaded with different dimensions or normalization
|
|
263
|
+
semantics.
|
|
264
|
+
|
|
265
|
+
Opening and reloading validate the complete schema, array dtypes and shapes, row
|
|
266
|
+
counts, finite vector values, and zero-norm behavior before changing in-memory
|
|
267
|
+
state. Opaque metadata values remain individual row payloads across persistence
|
|
268
|
+
round trips.
|
|
269
|
+
|
|
270
|
+
Each save writes a uniquely named temporary archive in the destination
|
|
271
|
+
directory, closes it, and then replaces the destination with `os.replace`.
|
|
272
|
+
Readers opening the destination path therefore see either the previous complete
|
|
273
|
+
archive or the new complete archive rather than a partially written file. If
|
|
274
|
+
writing or replacement fails, the previous destination remains in place and the
|
|
275
|
+
temporary file is removed.
|
|
276
|
+
|
|
277
|
+
Atomic replacement is not file locking or multi-writer coordination. Concurrent
|
|
278
|
+
writers can still replace one another, and the library does not promise that a
|
|
279
|
+
successful save has reached durable hardware storage across every operating
|
|
280
|
+
system or power failure.
|
|
281
|
+
|
|
282
|
+
Older archives containing only `vectors` and `metadata` remain readable in 0.4
|
|
283
|
+
through the configuration-aware legacy API:
|
|
247
284
|
|
|
248
285
|
```python
|
|
249
|
-
|
|
250
|
-
|
|
286
|
+
legacy = VectorStore[dict[str, str]](
|
|
287
|
+
dimensions=1536,
|
|
288
|
+
file_path="legacy-vectors.npz",
|
|
289
|
+
normalize=True,
|
|
290
|
+
)
|
|
291
|
+
legacy.load()
|
|
292
|
+
legacy.save()
|
|
251
293
|
```
|
|
252
294
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
295
|
+
Loading a legacy archive emits a `FutureWarning`, and saving rewrites it as
|
|
296
|
+
format version 1. `open()` intentionally rejects unversioned archives because
|
|
297
|
+
they do not contain enough configuration to construct a store safely. The
|
|
298
|
+
legacy reader will be removed in 0.5; migrate an archive once with 0.4 or
|
|
299
|
+
recreate it from source data.
|
|
300
|
+
|
|
301
|
+
Constructor `file_path=`, instance `load()`, and direct context-manager
|
|
302
|
+
persistence remain available with `FutureWarning` during the 0.4 transition.
|
|
303
|
+
The explicit lifecycle shown above is the preferred API and will be the only
|
|
304
|
+
persistence API in 0.5. See the [persistence migration guide](MIGRATION.md) for
|
|
305
|
+
side-by-side replacements and the one-time legacy archive conversion.
|
|
306
|
+
|
|
307
|
+
Metadata persistence uses `allow_pickle=True` for flexible Python payloads, so
|
|
261
308
|
only load files generated by your own application or another trusted local
|
|
262
309
|
process. Loading untrusted `.npz` files is not a supported security model.
|
|
263
310
|
|
|
@@ -268,14 +315,19 @@ the API stabilizes. Changes are documented in the [changelog](CHANGELOG.md) and
|
|
|
268
315
|
GitHub release notes. Deprecated APIs will keep warning for at least one point
|
|
269
316
|
release before removal.
|
|
270
317
|
|
|
271
|
-
|
|
272
|
-
in
|
|
273
|
-
|
|
274
|
-
|
|
318
|
+
Version 0.4 supports Python 3.11 through 3.14 and NumPy 1.23.2 or newer. These
|
|
319
|
+
versions are listed in the package metadata and exercised in CI, including a
|
|
320
|
+
dedicated check against the minimum NumPy version. Python 3.10 remains supported
|
|
321
|
+
by the 0.3 release series but is not supported by 0.4.
|
|
322
|
+
|
|
323
|
+
The project generally retains stable CPython versions until their upstream
|
|
324
|
+
end-of-life, adds new versions after its dependencies and CI support them, and
|
|
325
|
+
drops versions only in minor releases.
|
|
275
326
|
|
|
276
327
|
See the [changelog](CHANGELOG.md) for release history and the
|
|
277
328
|
[project roadmap](ROADMAP.md) for the planned path to stable API and persistence
|
|
278
|
-
contracts.
|
|
329
|
+
contracts. Persistence users upgrading from the 0.3 API should also read the
|
|
330
|
+
[migration guide](MIGRATION.md).
|
|
279
331
|
|
|
280
332
|
## Contributing
|
|
281
333
|
|