-
Notifications
You must be signed in to change notification settings - Fork 320
vector_store: add VDB abstraction and canonical record contract #1822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,76 @@ | ||||||||||||||||||||||||||||||||||||||||||
| # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. | ||||||||||||||||||||||||||||||||||||||||||
| # All rights reserved. | ||||||||||||||||||||||||||||||||||||||||||
| # SPDX-License-Identifier: Apache-2.0 | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| """LanceDB implementation of the :class:`VectorStore` interface.""" | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||||||||
| from typing import Any, Sequence | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| from nemo_retriever.params.models import LanceDbParams | ||||||||||||||||||||||||||||||||||||||||||
| from nemo_retriever.vector_store.lancedb_utils import infer_vector_dim, lancedb_schema | ||||||||||||||||||||||||||||||||||||||||||
| from nemo_retriever.vector_store.vdb import VectorStore | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| logger = logging.getLogger(__name__) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| class LanceDBBackend(VectorStore): | ||||||||||||||||||||||||||||||||||||||||||
| """LanceDB vector store backend. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Lazily connects and creates the table on the first :meth:`write_rows` | ||||||||||||||||||||||||||||||||||||||||||
| call so that the embedding dimension can be inferred from the data. | ||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def __init__(self, params: LanceDbParams | None = None) -> None: | ||||||||||||||||||||||||||||||||||||||||||
| self._params = params or LanceDbParams() | ||||||||||||||||||||||||||||||||||||||||||
| self._db: Any = None | ||||||||||||||||||||||||||||||||||||||||||
| self._table: Any = None | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def open_table(self) -> None: | ||||||||||||||||||||||||||||||||||||||||||
| """Open an existing LanceDB table without creating it. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Used by the driver to run post-pipeline finalization (e.g. index | ||||||||||||||||||||||||||||||||||||||||||
| creation) after distributed workers have written all rows. | ||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||
| import lancedb | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| self._db = lancedb.connect(uri=self._params.lancedb_uri) | ||||||||||||||||||||||||||||||||||||||||||
| self._table = self._db.open_table(self._params.table_name) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def create_table(self, *, dim: int, **kwargs: Any) -> None: | ||||||||||||||||||||||||||||||||||||||||||
| import lancedb | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| self._db = lancedb.connect(uri=self._params.lancedb_uri) | ||||||||||||||||||||||||||||||||||||||||||
| schema = lancedb_schema(vector_dim=dim) | ||||||||||||||||||||||||||||||||||||||||||
| mode = "overwrite" if self._params.overwrite else "create" | ||||||||||||||||||||||||||||||||||||||||||
| self._table = self._db.create_table( | ||||||||||||||||||||||||||||||||||||||||||
| self._params.table_name, | ||||||||||||||||||||||||||||||||||||||||||
| schema=schema, | ||||||||||||||||||||||||||||||||||||||||||
| mode=mode, | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def write_rows(self, rows: Sequence[dict[str, Any]], **kwargs: Any) -> None: | ||||||||||||||||||||||||||||||||||||||||||
| if not rows: | ||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||
| if self._table is None: | ||||||||||||||||||||||||||||||||||||||||||
| self.create_table(dim=infer_vector_dim(list(rows))) | ||||||||||||||||||||||||||||||||||||||||||
| self._table.add(list(rows)) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def create_index(self, **kwargs: Any) -> None: | ||||||||||||||||||||||||||||||||||||||||||
| if self._table is None: | ||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||
| if not self._params.create_index: | ||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| from nemo_retriever.vector_store.lancedb_store import create_lancedb_index | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||
| create_lancedb_index(self._table, cfg=self._params) | ||||||||||||||||||||||||||||||||||||||||||
| except RuntimeError: | ||||||||||||||||||||||||||||||||||||||||||
| logger.warning( | ||||||||||||||||||||||||||||||||||||||||||
| "Index creation failed (likely too few rows for %d partitions); skipping.", | ||||||||||||||||||||||||||||||||||||||||||
| self._params.num_partitions, | ||||||||||||||||||||||||||||||||||||||||||
| exc_info=True, | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
+76
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The bare
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: nemo_retriever/src/nemo_retriever/vector_store/lancedb_backend.py
Line: 68-76
Comment:
**`RuntimeError` swallowed without traceback**
The bare `except RuntimeError:` catches any `RuntimeError` thrown by `create_lancedb_index` — not only the KMeans partition-size failure. A real infrastructure error (e.g. connection failure, schema mismatch) would log a misleading "too few rows" message and silently succeed, leaving callers with a table that has no index and no indication of what went wrong. Add `exc_info=True` and bind the exception so it can be inspected:
```suggestion
try:
create_lancedb_index(self._table, cfg=cfg)
except RuntimeError as exc:
# KMeans cannot train when the dataset is smaller than num_partitions.
# This is expected for dev/test datasets; log and continue.
logger.warning(
"Index creation failed (likely too few rows for %d partitions); skipping. Error: %s",
self._params.num_partitions,
exc,
exc_info=True,
)
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you recreating this backend? Remember part of the motivation here is to show that we can consume VDBOperators that were created for the legacy system. One of your tests should be can I consume and correctly use the LanceDB operator from legacy.