Skip to content

Add zvec as a dense ANN backend#1130

Merged
davidmezzetti merged 5 commits into
neuml:masterfrom
morgan-coded:feat/zvec-dense-ann-backend
Jul 14, 2026
Merged

Add zvec as a dense ANN backend#1130
davidmezzetti merged 5 commits into
neuml:masterfrom
morgan-coded:feat/zvec-dense-ann-backend

Conversation

@morgan-coded

Copy link
Copy Markdown
Contributor

Dense embeddings can now use zvec as an embedded, path-based ANN index. This keeps the first pass dense-only, with sparse indexing and the current default backend unchanged. The shared harness covers save/load, append, missing-ID deletes, and search with positional IDs; I also ran the full dense and sparse ANN suites (31 tests with 3 existing macOS skips), the optional-dependency test, and the changed-file lint hooks. In a seeded local 10,000 × 240 run, zvec matched the exact NumPy top result for all 100 queries. Closes #1040

Copilot AI review requested due to automatic review settings July 12, 2026 16:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@davidmezzetti
davidmezzetti self-requested a review July 13, 2026 00:17
Comment thread src/python/txtai/ann/dense/zvec.py Outdated
@davidmezzetti

Copy link
Copy Markdown
Member

Thanks for the PR. At first glance this looks good with just a couple thoughts.

@morgan-coded

Copy link
Copy Markdown
Contributor Author

Thanks, that makes sense. Dropped the extra metadata and config restore since dimensions and offset already persist through the embeddings config. zvec 0.5.1 writes a directory-backed collection and only reopens it by path, so I kept the uncompressed TAR to fit ANN.save(path)'s single-file contract. Happy to use a different convention if you have one.

@davidmezzetti

Copy link
Copy Markdown
Member

Thanks for clarifying it requires multiple files.

TAR files have had security issues I've had to deal with in the past. Please model this after this class: https://github.com/neuml/txtai/blob/master/src/python/txtai/graph/networkx.py using ArchiveFactory.

@morgan-coded

Copy link
Copy Markdown
Contributor Author

Thanks, that makes sense. Switched the zvec save/load to ArchiveFactory with the TAR handling modeled on networkx.py, so packing, path/symlink validation, and the data filter now come from txtai's archive code. Could you take another look?

@davidmezzetti

Copy link
Copy Markdown
Member

Thanks, can you sync your fork with the latest? I had to debug a number of recent build errors unrelated to this PR.

@morgan-coded
morgan-coded force-pushed the feat/zvec-dense-ann-backend branch from 0b7af69 to b362c65 Compare July 14, 2026 01:49
@morgan-coded

Copy link
Copy Markdown
Contributor Author

Thanks, can you sync your fork with the latest? I had to debug a number of recent build errors unrelated to this PR.

Synced the branch with the latest master and reran the ANN tests, optional-dependency test, and lint hooks. Everything passed locally. It should be good now

@davidmezzetti

Copy link
Copy Markdown
Member

Please see the build error. It looks like we need to skip copying at least the LOCK file if not more. I can't find a ton on the file format but if there are other files to skip that would be good too.

@morgan-coded

Copy link
Copy Markdown
Contributor Author

Excluded both zvec runtime LOCK files from the saved tar and recreated the required empty root lock before opening a loaded collection. Kept the rest of the zvec files in the archive; local tests are passing.

@davidmezzetti

Copy link
Copy Markdown
Member

I'll run the build here but this is starting to get messy.

@davidmezzetti

Copy link
Copy Markdown
Member

Can you call collection.close -> save -> then reopen instead of all this additional code to work around lockfiles? The last change just seems messy.

@davidmezzetti

Copy link
Copy Markdown
Member

Are you sure you don't need to call self.backend.close vs just setting null? I don't know what platform you're developing on but Windows is just very finicky with locks.

@morgan-coded

Copy link
Copy Markdown
Contributor Author

zvec's Collection (0.5.1) exposes no close() — the only teardown is destroy, which drops the data. Setting self.backend to None releases the collection and its file locks, the same release the existing close() performs before it removes the directory. Windows is genuinely finicky here, so the CI run on this commit is the real test; the save/reopen round-trips locally, and if Windows still trips on the lock I'll follow up.

@morgan-coded

Copy link
Copy Markdown
Contributor Author

Developed this on macOS, so I ran testZvec and testZvecCustom on a Windows 11 machine (Python 3.12, zvec 0.5.1) to check the lock directly: both pass, save/load round-trips with no LOCK error — dropping the backend reference does release the lock on Windows. CI should confirm the same.

@morgan-coded

Copy link
Copy Markdown
Contributor Author

The Windows job gets through the save with no LOCK error, so that part's confirmed. The red build status is unrelated to this change — pylint's name-length check flags a method in testworkflow.py, plus a testscoring.testsparse multiprocessing flake.

@davidmezzetti

Copy link
Copy Markdown
Member

Thank you for the hard work on this. I'll merge it in and debug anything further, if anything.

@davidmezzetti
davidmezzetti merged commit 14cbe51 into neuml:master Jul 14, 2026
1 of 4 checks passed
@davidmezzetti davidmezzetti added this to the v9.12.0 milestone Jul 14, 2026
@morgan-coded

Copy link
Copy Markdown
Contributor Author

Thanks for merging this. If anything surfaces when you dig in further, let me know and I'll pick it back up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants