Files
lancedb/AGENTS.md
Will Jones 5a1015ba72 docs(python): fill gaps in the Python API reference (#3746)
`docs/src/python/python.md` is the whole Python API reference, but it is
maintained by hand and had drifted from the public API. Anything not
listed there simply doesn't get rendered, so a number of public,
documented, tested APIs were invisible to users — most notably branch
management, where `diff` and `merge` live.

I audited every public symbol reachable from `lancedb` and its
subpackages against the `:::` directives on the page. This adds the
missing ones:

- **Branching** — `Branches`, `AsyncBranches` (`list` / `create` /
`checkout` / `delete` / `diff` / `merge`)
- **Tables** — `TableStatistics` (returned by `Table.stats()`; the
fragment-level stats classes were already listed)
- **Full text queries** — `FullTextQuery`, `MatchQuery`, `PhraseQuery`,
`BoostQuery`, `MultiMatchQuery`, `BooleanQuery`, `FullTextOperator`,
`Occur`
- **Querying** — `LanceEmptyQueryBuilder`, `LanceTakeQueryBuilder`,
`AsyncTakeQuery`
- **Indices** — `Fm` (the FM-index for substring search), `IndexConfig`
- **Blobs** — `blob`, `BlobType`, `BlobFile`
- **Namespaces** — `connect_namespace`, `connect_namespace_async`, and
both namespace connection classes
- **Remote config** — `TlsConfig`, `HeaderProvider`, `OAuthConfig`,
`OAuthFlowType`
- **Rerankers** — the `Reranker` base class plus `JinaReranker`,
`RRFReranker`, `MRRReranker`, `AnswerdotaiRerankers`,
`VoyageAIReranker`, `WatsonxReranker` (5 of 12 were listed)
- **Embeddings** — `get_registry`, `register`, and the 14 embedding
functions that were missing (3 of 17 were listed)
- **PyTorch** — `StreamingDataset` and the permutation API it is built
on
- **Misc** — `Session`, `tokenize`, `FtsToken`, `pydantic.Vector`,
`pydantic.MultiVector`, `instrument_lancedb_metrics`, and the two
exception types

It also repairs cross-references in docstrings that no longer resolve:
links into guide pages that have since moved to lancedb.com
(`querying-an-ann-index`, `experimental-full-text-search`),
`lance.dataset` references with no inventory behind them, and the
relative targets `[Table](Table)` and `[PyArrow Table](pyarrow.Table)`.

Deliberately left out: concrete implementation classes reached through
their abstract base (`LanceTable`, `LanceDBConnection`,
`RemoteDBConnection`), query base classes already covered by
`inherited_members: true`, and internal plumbing such as
`FullTextSearchQuery` and `ColumnOrdering`.

## Testing

The docs job only runs on pushes to `main`, so I built the site locally
and compared against a build of `upstream/main`: every added entry
resolves, and no symbol that was rendered before stopped being rendered
when the four packages moved to automodule. `mkdocs build --strict`
exits 0 on this branch, against 61 warnings on `main`.

## Also in this PR

`lancedb.index`, `lancedb.embeddings`, `lancedb.remote` and
`lancedb.rerankers` are now rendered by a single mkdocstrings directive
each, driven by the module's `__all__`, rather than a hand-maintained
list. These four are where most of the drift was, and `__all__` is
harder to forget than a docs page. `lancedb.embeddings` had no
`__all__`; without one mkdocstrings renders no members at all for a
re-export package, so one is added. AGENTS.md gains a section on how the
page is wired up and how to build the docs locally.

Rendering all that code for the first time surfaced ~100 more build
warnings, which would have made #3707 (turning on `mkdocs build
--strict`) harder to land, so the warning backlog is cleared here too.
97 of the 158 warnings were one systematic false positive — griffe
cannot see the generated `__init__` of a pydantic dataclass, so every
documented parameter looks unknown — switched off via
`warn_unknown_params`. The remaining 61 came from 15 docstrings with
real bugs: prose trailing a `Parameters` section (we were rendering
parameters called `The`, `you` and `To`), types dropped because numpydoc
needs spaces around the colon, `num_partitions, default sqrt(num_rows)`
parsing as a list of names and inventing a `default` parameter, and one
parameter indented five spaces. `mkdocs build --strict` now exits 0.

---

#3747 (the coverage test that keeps this from happening again) is
stacked on this branch, so review it after this one.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:50:05 -07:00

7.6 KiB

LanceDB is a database designed for retrieval, including vector, full-text, and hybrid search. It is a wrapper around Lance. There are two backends: local (in-process like SQLite) and remote (against LanceDB Cloud).

The core of LanceDB is written in Rust. There are bindings in Python, Typescript, and Java.

Project layout:

  • rust/lancedb: The LanceDB core Rust implementation.
  • python: The Python bindings, using PyO3.
  • nodejs: The Typescript bindings, using napi-rs
  • java: The Java bindings

Common commands:

  • Check for compiler errors: cargo check --quiet --features remote --tests --examples
  • Run tests: cargo test --quiet --features remote --tests
  • Run specific test: cargo test --quiet --features remote -p <package_name> --test <test_name>
  • Lint: cargo clippy --quiet --features remote --tests --examples
  • Format Rust: cargo fmt --all
  • Format Python: ruff format .
  • Lint Python: ruff check .
  • Bootstrap Python dev env: cd python && uv run --extra tests --extra dev maturin develop --extras tests,dev
  • Run Python tests: cd python && uv run --extra tests pytest python/tests -vv --durations=10 -m "not slow and not s3_test"
  • Run specific Python test: cd python && uv run --extra tests pytest python/tests/<test_file>.py::<test_name> -q

For Python validation, prefer the uv-managed environment declared by python/uv.lock. Do not treat system python, global pytest, or missing editable-install errors as final blockers; bootstrap or enter the uv environment instead. If lancedb._lancedb is missing or stale, or if Rust/PyO3 binding code changed, rebuild the Python extension with the bootstrap command above before running tests.

Before committing changes, run formatting for every language you touched. At minimum:

  • Rust changes: run cargo fmt --all.
  • Python changes: run ruff format . and ruff check . from the repository root, and run targeted tests through cd python && uv run ....
  • TypeScript changes: run the relevant npm/pnpm lint, format, build, and docs commands in nodejs.

Before creating a PR, the exact value passed to gh pr create --title must follow Conventional Commits, such as fix: support nested field paths in native index creation or feat(python): add dataset multiprocessing support. Do not use a plain natural language summary like Support nested field paths in native index creation as the PR title. The semantic-release check uses the PR title and body as the merge commit message, so a non-conventional PR title will fail CI. After creating a PR, read the remote PR title back and fix it immediately if it is not conventional.

Coding tips

  • When writing Rust doctests for things that require a connection or table reference, write them as a function instead of a fully executable test. This allows type checking to run but avoids needing a full test environment. For example:
    /// ```
    /// use lance_index::scalar::FullTextSearchQuery;
    /// use lancedb::query::{QueryBase, ExecutableQuery};
    ///
    /// # use lancedb::Table;
    /// # async fn query(table: &Table) -> Result<(), Box<dyn std::error::Error>> {
    /// let results = table.query()
    ///     .full_text_search(FullTextSearchQuery::new("hello world".into()))
    ///     .execute()
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    

Example plan: adding a new method on Table

Adding a new method involves first adding it to the Rust core, then exposing it in the Python and TypeScript bindings. There are both local and remote tables. Remote tables are implemented via a HTTP API and require the remote cargo feature flag to be enabled. Python has both sync and async methods.

Rust core changes:

  1. Add method on Table struct in rust/lancedb/src/table.rs (calls BaseTable trait).
  2. Add method to BaseTable trait in rust/lancedb/src/table.rs.
  3. Implement new trait method on NativeTable in rust/lancedb/src/table.rs.
    • Test with unit test in rust/lancedb/src/table.rs.
  4. Implement new trait method on RemoteTable in rust/lancedb/src/remote/table.rs.
    • Test with unit test in rust/lancedb/src/remote/table.rs against mocked endpoint.

Python bindings changes:

  1. Add PyO3 method binding in python/src/table.rs. Run make develop to compile bindings.
  2. Add types for PyO3 method in python/python/lancedb/_lancedb.pyi.
  3. Add method to AsyncTable class in python/python/lancedb/table.py.
  4. Add abstract method to Table abstract base class in python/python/lancedb/table.py.
  5. Add concrete sync method to LanceTable class in python/python/lancedb/table.py.
    • Should use LOOP.run() to call the corresponding AsyncTable method.
  6. Add concrete sync method to RemoteTable class in python/python/lancedb/remote/table.py.
  7. Add unit test in python/tests/test_table.py.
  8. If you added a new public class or module-level function (not just a method on an existing class), expose it in the API reference. See "Python API reference" below.

TypeScript bindings changes:

  1. Add napi-rs method binding on Table in nodejs/src/table.rs.
  2. Run npm run build to generate TypeScript definitions.
  3. Add typescript method on abstract class Table in nodejs/src/table.ts.
  4. Add concrete method on LocalTable class in nodejs/src/native_table.ts.
    • Note: despite the name, this class is also used for remote tables.
  5. Add test in nodejs/__test__/table.test.ts.
  6. Run npm run docs to generate TypeScript documentation.

Python API reference

docs/src/python/python.md is the entire Python API reference. It is maintained by hand, and anything not listed there is not rendered at all, so new public classes and module-level functions have to be added explicitly. How depends on the module:

  • lancedb.index, lancedb.embeddings, lancedb.remote, and lancedb.rerankers are rendered by a single directive each, driven by the module's __all__. Add the new name to __all__ and it appears; forget, and it is silently omitted.
  • Everything else (lancedb, lancedb.table, lancedb.query, lancedb.db, ...) is listed symbol by symbol. Add a ::: lancedb.<module>.<Name> line to the matching section, and remember that the page separates synchronous and asynchronous APIs.

Deliberately undocumented: concrete implementations reached through an abstract base (LanceTable, LanceDBConnection, RemoteDBConnection), query base classes already covered by inherited_members, and internal helpers.

Cross-references in docstrings use mkdocstrings syntax, [text][lancedb.table.Table]. Plain relative links such as [Table](Table) do not resolve. To check your work:

pip install -r docs/requirements.txt
cd docs && PYTHONPATH=. mkdocs build

The docs site only builds on pushes to main, so this is not covered by PR CI.

Review Guidelines

Please consider the following when reviewing code contributions.

Rust API design

  • Design public APIs so they can be evolved easily in the future without breaking changes. Often this means using builder patterns or options structs instead of long argument lists.
  • For public APIs, prefer inputs that use Into<T> or AsRef<T> traits to allow more flexible inputs. For example, use name: Into<String> instead of name: String, so we don't have to write func("my_string".to_string()).

Testing

  • Ensure all new public APIs have documentation and examples.
  • Ensure that all bugfixes and features have corresponding tests. We do not merge code without tests.

Documentation

  • New features must include updates to the rust documentation comments. Link to relevant structs and methods to increase the value of documentation.