v0.15.0 — Performance & reliability
Non-breaking performance and bug-fix release on top of a dependency-update wave. Vectra's own API is fully backward-compatible; only direct callers of bumped SDKs (most notably openai 4 → 6) need to review their code.
Highlights
- Re-upserting unchanged documents is a no-op instead of O(chunks) embedding calls.
- Faster transactions, faster top-K queries, faster bulk delete.
- Fixed a long-standing bug where
upsertItemleft a stale precomputed norm, silently degrading cosine rankings. - Fixed a Windows-only
FolderWatchercrash for paths underos.tmpdir()(and anywhere else 8.3 short names resolve). - Test coverage is up to 97.7% statements / 91.5% branches (from ~94.6% / 87.1%).
Performance
Skip-if-unchanged document upsert. LocalDocumentIndex.upsertDocument now hashes text + docType + metadata (canonicalized, SHA-256) and stores the hash on the catalog. If you re-upsert a URI with byte-identical content, the call returns the existing document handle without re-chunking or re-embedding. Measured: a 100-file no-op sync that previously took ~505s of pure re-embed overhead now finishes in under a second.
Old catalogs bootstrap on first upsert per URI, then short-circuit. No catalog version bump. Opt out with { force: true } as the 5th argument (e.g., after rotating embeddings models).
Engine improvements:
beginUpdateno longer deep-clones vectors — shallow array copy saves O(N·D) memory per upsert.queryItemsuses a bounded min-heap for top-K — O(N log K) instead of O(N log N).queryItemsloads external metadata in parallel.deleteDocumentremoves all chunks in one pass via the new publicLocalIndex.deleteItems(ids)helper — O(N) instead of O(N·M).
Bug fixes
Stale precomputed norm after upsertItem. Upsert previously kept the old norm when the vector changed, skewing every subsequent cosine ranking against that item. Indexes built with prior versions self-heal on the next upsert per item.
FolderWatcher Windows crash with 8.3 short-name paths. The watcher now resolves paths through fs.realpath before calling fs.watch, avoiding a libuv assertion (!_wcsnicmp in fs-event.c) on paths under os.tmpdir(). It also no longer uses fs.watch(..., { recursive: true }) — it walks the tree and watches each directory individually, adding/removing watchers as subdirs appear or disappear.
Behavior note: URIs stored in an index for paths that previously contained 8.3 short names will now be in their canonical long-name form. If you store Vectra URIs externally, paths under os.tmpdir() (or similar) may differ.
Testing & internals
- New
src/internals/transformersLoader.tstest seam —@huggingface/transformers4.x marks its ESM exports as non-configurable, which broke the priorsinon.stub(module, 'pipeline')approach in tests. Production behavior unchanged. - Linux CI now correctly runs all spec files (test scripts quote the glob).
Dependency updates
Runtime — major bumps (review direct usage): openai 4.104.0 → 6.33.0, @huggingface/transformers 3.8.1 → 4.0.1, dotenv 16.5.0 → 17.4.1, protobufjs 7.5.4 → 8.0.0, uuid 11.1.0 → 13.0.0, yargs 17.7.2 → 18.0.0.
Runtime — minor/patch: wink-nlp 2.4.0, cheerio 1.2.0, turndown 7.2.4.
Dev/tooling: typescript 5.9.3, eslint 10.2.0, mocha 11.7.5, nyc 18.0.0, webpack-cli 7.0.2, @types/node 25.5.2, @types/uuid 11.0.0, @types/yargs 17.0.35.
GitHub Actions: checkout v6, setup-node v6, deploy-pages v5, upload-pages-artifact v5, configure-pages v6.
Upgrading from v0.14.x
No required code changes. Three things to watch for:
- Direct
openaiSDK usage alongsideOpenAIEmbeddings— Vectra's wrapper is unchanged, but the underlying SDK jumped 4 → 6. - URIs from paths under
os.tmpdir()(or anywhere else 8.3 short names resolve) — now in canonical long-name form. - Cosine rankings post-
upsertItemin v0.14.x — self-heal on next upsert per item, or force a refresh sooner.