Compare commits

...

43 Commits

Author SHA1 Message Date
Will Jones 011def461c docs(python): fix cross-references that resolved to the wrong page
`mkdocs build --strict` only catches references it cannot resolve. A bare
anchor such as `[limit][]` or `[vector search][search]` is matched by
autorefs against any heading on the site, so six of them silently linked
into the JavaScript reference instead. The relative links in
`permutation.py` and `remote/errors.py` pointed at in-page anchors and
paths that do not exist.

Targets that still exist here or in an imported inventory now use
mkdocstrings references; the guide pages deleted in #2770 use their
lancedb.com URLs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:19:38 -07:00
Will Jones ed6be12ad6 docs: clear the mkdocs warning backlog so --strict passes
`mkdocs build` emitted 61 warnings on main, and rendering the previously
undocumented classes in this PR pushed that to 158. That backlog is what
blocks turning on strict mode (#3707), so clear it here rather than leave
it worse than we found it.

Most of it was one systematic false positive: griffe cannot see the
generated `__init__` of a pydantic dataclass, so every documented
parameter looked unknown. `warn_unknown_params` turns that check off.

The rest were real docstring bugs, in 15 docstrings:

* Prose trailing a `Parameters` section is read as parameter names, which
  invented parameters called `The`, `you` and `To`. Moved into `Notes` or
  the summary.
* numpydoc only reads a type when the colon has spaces around it. Where
  the documented name is a pydantic attribute rather than a signature
  parameter, griffe has no signature to fall back on and the type was
  dropped. Affects nine embedding classes.
* `num_partitions, default sqrt(num_rows)` and friends parse as a list of
  names, rendering a bogus `default` parameter.
* One parameter indented five spaces instead of four.

`nodejs/CONTRIBUTING.md` links to the repo-root CONTRIBUTING.md, which
does not resolve once typedoc copies the file into `docs/src/js/_media/`;
an absolute URL works from both places.

`mkdocs build --strict` now exits 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 14:15:20 -07:00
Will Jones ac2b689cdb docs(python): render index/embeddings/remote/rerankers from __all__
Four packages are now rendered by a single mkdocstrings directive each,
driven by the module's `__all__`, instead of a hand-maintained list of
symbols. These were where most of the drift was: 7 of 12 rerankers and
14 of 17 embedding functions had never been listed.

`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 describing how the reference page is wired up
and how to check a docs build locally, plus a step in the "adding a new
method on Table" checklist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 13:59:23 -07:00
Will Jones 4fc8114871 docs(python): add missing public APIs to the Python reference
The Python API reference page had drifted from the public API. Branch
management (`Branches` / `AsyncBranches`, which own `diff` and `merge`),
structured full-text query classes, take queries, blob helpers,
namespace connections, most rerankers and embedding functions, the
PyTorch dataloader, and several other public symbols were never listed,
so they did not appear in the rendered docs.

Also fixes docstring cross-references that pointed at guide pages which
have since moved off this site, and at unresolvable relative targets
(`[Table](Table)`, `[PyArrow Table](pyarrow.Table)`).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 13:46:45 -07:00
Will Jones 03b26d585b fix: deflake test_read_consistency_interval (#3713)
`test_read_consistency_interval` asserted that a table opened with a
100ms `read_consistency_interval` still read stale data immediately
after a concurrent write. The cache timestamp is set when the table is
opened and reads within the interval do not refresh it, so that
assertion only held if the intervening open/count/commit/count sequence
finished within 100ms of real wall-clock time. On a loaded CI runner it
did not: the TTL expired, `count_rows` refreshed synchronously, and the
test failed with `left: 1, right: 0`. This broke the Rust workflow on
`main` at 0bc08160 (a Python-only commit).

This pins the `background_cache` mock clock once `table2` has seeded its
cache, and advances it explicitly in place of `tokio::time::sleep`, so
the test controls when the interval elapses. Same approach as #3547.
With the clock pinned there is no real sleep left to be imprecise, so
the `cfg(not(target_os = "windows"))` guard is dropped and the test now
runs on Windows too.

Verified by inserting a stall before the write: 120ms reproduces the
original failure deterministically, and with this change the test still
passes with a 500ms stall.

Fixes #3712
2026-07-29 13:06:41 -07:00
Yang Cen f7feed48c3 feat(fts): support custom stop-word lists (#3734)
## What

Expose custom FTS stop-word lists in the Python and TypeScript public
APIs, including their standalone tokenize helpers and remote index
creation.

This PR supports concrete string lists only. It does not add file or
LanceDB-table stop-word sources.

## Why

Rust already exposes Lance's custom stop-word list option. The Python
and TypeScript APIs did not pass it through, and local index details did
not retain the full tokenizer parameters needed by index-backed
tokenization after reopening a table.

## How

- Add `custom_stop_words` / `customStopWords` to the Python and
TypeScript FTS and tokenize options.
- Preserve `None` / `undefined`, empty lists, and list contents without
normalization.
- Load the persisted FTS segment parameters when returning local index
details.
- Serialize the concrete list in remote create-index requests.
- Keep Python and TypeScript tests thin; behavior, persistence, query
tokenization, and remote JSON coverage live primarily in Rust.

## Validation

- `cargo check --quiet --features remote --tests --examples`
- `cargo clippy --quiet --features remote --tests --examples`
- `cargo test --quiet --features remote --tests`
- Python extension rebuild with `uv` and `maturin`
- Targeted Python tests: 4 passed
- Python `ruff format --check` and `ruff check`
- TypeScript build, typecheck, Biome lint, generated docs, and targeted
tests

---------

Co-authored-by: Yang Cen <yangcen@Yangs-Mac-mini.local>
2026-07-29 17:40:12 +08:00
Lance Release e5f489818b Bump version: 0.37.0-beta.0 → 0.37.1-beta.0 2026-07-29 07:12:34 +00:00
buduoqiu 98a52267a2 feat(python): configure streaming transform parallelism (#3699)
## Summary

- add a keyword-only `transform_parallelism` option to
`StreamingDataset`
- preserve CPU auto-detection by default and fall back to one worker
when unavailable
- apply the configured limit to both the transform executor and
concurrency semaphore
- document and test explicit, default, fallback, and invalid values

## Testing

- `uv run --extra tests --with torch pytest
python/tests/test_elastic_dataloader.py -q` (`136 passed`)
- `uvx ruff check python/lancedb/streaming.py
python/tests/test_elastic_dataloader.py`
- `uvx ruff format --check python/lancedb/streaming.py
python/tests/test_elastic_dataloader.py`
- `git diff --check origin/main...HEAD`

Closes #3695

Co-authored-by: buduoqiu <yaodong-shen@users.noreply.github.com>
2026-07-28 15:38:34 -07:00
Will Jones ff50e698cf ci: cut Actions cost by moving builds to free runners and fixing caches (#3735)
Standard GitHub-hosted runners are free on public repos, so all Actions
spend here is on the `*-8x-*` / `4x` larger runners. Measured over 30
days at current (post-Jan-2026) larger-runner rates, that is ~$1,400/mo,
and `npm-publish` is ~70% of it.

## Changes

**Fat LTO was forcing builds onto large runners.** `[profile.release]`
in `.cargo/config.toml` sets `lto = "fat"` with `codegen-units = 1`,
which is single-threaded and the peak-memory step. The macOS
`npm-publish` build was 111 of its 113 minutes in one `napi build` step,
making it the critical path of the whole publish pipeline. The ThinLTO
override already applied to Windows now covers macOS too, and both
Windows builds move from `windows-2025-8x-x64` to the free standard
`windows-2025`.

**The npm-publish cargo cache never existed.** There are zero caches
with its key prefix. The key was static, so `actions/cache` (which only
writes on a miss) could never refresh it, and a multi-GB release
`target/` per target could never fit the repo's 10 GB budget anyway. Now
caches only the crate registry, keyed on `Cargo.lock`. The docker builds
also mounted `.cargo/registry/*` while the cache saved `.cargo-cache`,
so containers re-downloaded the registry every run.

**Cache eviction thrash.** Repo cache usage is 10.4 GB against GitHub's
10 GB cap, so every PR run evicted main's warm entries. `rust.yml` and
`nodejs.yml` now restore everywhere but only save from `main`.

**npm-publish moves to nightly + tags** instead of every push to main
(~90/month). The cross-compiled targets do need watching, so
`report-failure` now fires on scheduled runs, and dedupes onto an
existing open issue rather than filing one per night.

**rust.yml aarch64-pc-windows-msvc** cross-compiled its tests and then
skipped them, paying full codegen and link cost for a compile check.
`windows-11-arm` is now GA and free on public repos, so it builds and
tests natively. Its test step also passes `--target` — without it cargo
used `target/ci/` rather than `target/<triple>/ci/` and rebuilt the
entire dependency graph a second time.

**pypi-publish.yml had no concurrency group**, so force-pushes left a
~74 minute Windows job running.

## What is cost vs. wall-clock

| Change | Cost | Wall-clock |
|---|---|---|
| Windows npm-publish → free runners | **−$570/mo** | slower per job
(8→4 cores) |
| npm-publish nightly | **−$125/mo** | — |
| pypi-publish concurrency | small | — |
| macOS ThinLTO | $0 (already free) | **−~50 min** per release |
| rust aarch64 Windows native | $0 (already free) | **−~25 min** |
| rust `--target` on test step | $0 | large, avoids a second full build
|
| rust-cache `save-if` | small | faster via real cache hits |

## Risks

- The two Windows builds now have 4 cores instead of 8 and ~14 GB of
free disk. If they fail, it is most likely disk rather than memory;
fallback is `windows-2025-4x-x64`, which still halves that line.
- `windows-11-arm` has a thinner toolset (choco/vcpkg/protoc under
emulation) and this enables a test step that has never run, so it may
surface real aarch64 failures. That is the point, but it is the change
most likely to need iteration.
- ThinLTO applies to published macOS and Windows binaries, typically
within a few percent of fat LTO. Linux release builds are untouched.

## Follow-ups

- `python.yml` `pydantic1x` (37 min) and `Doctest` (33 min) each rebuild
the extension from source via `pip install -e .` with no Rust cache;
they should consume the wheel the `linux` job already builds. Worth
~$235/mo and ~70 min of compute per run. Separate PR.
- The three `ubuntu-2404-8x-x64` npm-publish builds (~$420/mo at the old
cadence) are the remaining large-runner spend;
`aarch64-unknown-linux-gnu` could run natively on free
`ubuntu-24.04-arm`. Worth doing after this lands so the ThinLTO change
can be validated first.
- The wheel composite actions declare `python-minor-version` as required
but never use it, and every caller omits it (actionlint warns).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 14:38:21 -07:00
kid b799ebaa69 fix(node): reject non-string Arrow metadata (#3728)
## Summary

- validate Arrow metadata keys and values independently at runtime
- reject malformed foreign schemas before constructing a local Arrow
schema
- cover valid and invalid metadata entries across Arrow 15–18

## Testing

- `node_modules/.bin/jest --runInBand __test__/arrow.test.ts -t "schema
metadata"`
- `node_modules/.bin/jest --runInBand __test__/arrow.test.ts`
- `node node_modules/@biomejs/biome/bin/biome format --write
lancedb/sanitize.ts __test__/arrow.test.ts`
- `pnpm lint`
- `pnpm build`
- `pnpm run docs`

Fixes #3729
2026-07-28 13:36:08 -07:00
kid 72fc660f9e feat(python): expose AsyncTable.to_lance (#3730)
## Summary

- expose the existing async Lance dataset conversion as
`AsyncTable.to_lance`
- preserve table version, branch, and refreshed storage options when
opening the dataset
- route internal async pandas/query paths through the public API
- cover normal tables, checked-out versions, branches, and forwarded
dataset options

## Testing

- `cd python && uv run --no-sync pytest python/tests/test_table.py -q`
- `cd python && uv run --no-sync pytest python/tests/test_query.py -q`
- `cd python && uv run --no-sync pytest --doctest-modules
python/lancedb/table.py -q`
- `uv run --project python --no-sync ruff format --check
python/python/lancedb/table.py python/python/lancedb/query.py
python/python/tests/test_table.py`
- `uv run --project python --no-sync ruff check .`

Fixes #1387
2026-07-28 13:31:06 -07:00
dependabot[bot] 1ebde1f06c chore(deps): bump arrow from 58.3.0 to 58.4.0 (#3722)
Bumps [arrow](https://github.com/apache/arrow-rs) from 58.3.0 to 58.4.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/apache/arrow-rs/releases">arrow's
releases</a>.</em></p>
<blockquote>
<h2>arrow 58.4.0</h2>
<!-- raw HTML omitted -->
<h1>Changelog</h1>
<h2><a href="https://github.com/apache/arrow-rs/tree/58.4.0">58.4.0</a>
(2026-07-17)</h2>
<p><a
href="https://github.com/apache/arrow-rs/compare/58.3.0...58.4.0">Full
Changelog</a></p>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>[58_maintenance] [parquet] Allow more encryption algorithms (<a
href="https://redirect.github.com/apache/arrow-rs/issues/9203">#9203</a>)
<a
href="https://redirect.github.com/apache/arrow-rs/pull/10351">#10351</a>
[<a
href="https://github.com/apache/arrow-rs/labels/parquet">parquet</a>]
(<a href="https://github.com/mbutrovich">mbutrovich</a>)</li>
<li>[58_maintenance] Backport cargo audit fixes <a
href="https://redirect.github.com/apache/arrow-rs/pull/10369">#10369</a>
(<a href="https://github.com/alamb">alamb</a>)</li>
<li>[58_maintenance] chore: Ignore py03 vulnerabilities until upgrade <a
href="https://redirect.github.com/apache/arrow-rs/pull/10370">#10370</a>
(<a href="https://github.com/alamb">alamb</a>)</li>
<li>[58_maintenance] Add test for `parquet-testing/bad_data/ARROW-<a
href="https://redirect.github.com/apache/arrow-rs/issues/47662">GH-47662</a>.parquet`
(<a
href="https://redirect.github.com/apache/arrow-rs/issues/10077">#10077</a>)
<a
href="https://redirect.github.com/apache/arrow-rs/pull/10371">#10371</a>
[<a
href="https://github.com/apache/arrow-rs/labels/parquet">parquet</a>]
(<a href="https://github.com/alamb">alamb</a>)</li>
</ul>
<p>* <em>This Changelog was automatically generated by <a
href="https://github.com/github-changelog-generator/github-changelog-generator">github_changelog_generator</a></em></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/apache/arrow-rs/blob/58.4.0/CHANGELOG.md">arrow's
changelog</a>.</em></p>
<blockquote>
<h2><a href="https://github.com/apache/arrow-rs/tree/58.4.0">58.4.0</a>
(2026-07-17)</h2>
<p><a
href="https://github.com/apache/arrow-rs/compare/58.3.0...58.4.0">Full
Changelog</a></p>
<p><strong>Merged pull requests:</strong></p>
<ul>
<li>[58_maintenance] [parquet] Allow more encryption algorithms (<a
href="https://redirect.github.com/apache/arrow-rs/issues/9203">#9203</a>)
<a
href="https://redirect.github.com/apache/arrow-rs/pull/10351">#10351</a>
[<a
href="https://github.com/apache/arrow-rs/labels/parquet">parquet</a>]
(<a href="https://github.com/mbutrovich">mbutrovich</a>)</li>
<li>[58_maintenance] Backport cargo audit fixes <a
href="https://redirect.github.com/apache/arrow-rs/pull/10369">#10369</a>
(<a href="https://github.com/alamb">alamb</a>)</li>
<li>[58_maintenance] chore: Ignore py03 vulnerabilities until upgrade <a
href="https://redirect.github.com/apache/arrow-rs/pull/10370">#10370</a>
(<a href="https://github.com/alamb">alamb</a>)</li>
<li>[58_maintenance] Add test for `parquet-testing/bad_data/ARROW-<a
href="https://redirect.github.com/apache/arrow-rs/issues/47662">GH-47662</a>.parquet`
(<a
href="https://redirect.github.com/apache/arrow-rs/issues/10077">#10077</a>)
<a
href="https://redirect.github.com/apache/arrow-rs/pull/10371">#10371</a>
[<a
href="https://github.com/apache/arrow-rs/labels/parquet">parquet</a>]
(<a href="https://github.com/alamb">alamb</a>)</li>
</ul>
<p>* <em>This Changelog was automatically generated by <a
href="https://github.com/github-changelog-generator/github-changelog-generator">github_changelog_generator</a></em></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/arrow-rs/commit/0ff81c1215cc026a1de93ce3d2078df1ecba6f09"><code>0ff81c1</code></a>
[58_maintenance] Update changelog for <a
href="https://redirect.github.com/apache/arrow-rs/issues/10371">#10371</a>
(<a
href="https://redirect.github.com/apache/arrow-rs/issues/10372">#10372</a>)</li>
<li><a
href="https://github.com/apache/arrow-rs/commit/95d7231227e1ce7a1ec049ab2d45a6cffd7a50f9"><code>95d7231</code></a>
[58_maintenance] Add test for `parquet-testing/bad_data/ARROW-<a
href="https://redirect.github.com/apache/arrow-rs/issues/47662">GH-47662</a>.parque...</li>
<li><a
href="https://github.com/apache/arrow-rs/commit/4544deaa434bbf8e7fe930bf9497fd36e8e737d1"><code>4544dea</code></a>
Prepare for <code>58.4.0</code> release (<a
href="https://redirect.github.com/apache/arrow-rs/issues/10367">#10367</a>)</li>
<li><a
href="https://github.com/apache/arrow-rs/commit/32e8c1809642647ddf87703c410c4713df166281"><code>32e8c18</code></a>
chore: Ignore py03 vulnerabilities until upgrade (<a
href="https://redirect.github.com/apache/arrow-rs/issues/10370">#10370</a>)</li>
<li><a
href="https://github.com/apache/arrow-rs/commit/c12030f29639f9ac36fdfedd7d00f3b6b6bbd2c1"><code>c12030f</code></a>
[58_maintenance] Backport cargo audit fixes (<a
href="https://redirect.github.com/apache/arrow-rs/issues/10369">#10369</a>)</li>
<li><a
href="https://github.com/apache/arrow-rs/commit/01046eed275d4fabfd922f6a8924410102ab1802"><code>01046ee</code></a>
[58_maintenance] [parquet] Allow more encryption algorithms (<a
href="https://redirect.github.com/apache/arrow-rs/issues/9203">#9203</a>)
(<a
href="https://redirect.github.com/apache/arrow-rs/issues/10351">#10351</a>)</li>
<li><a
href="https://github.com/apache/arrow-rs/commit/adb77a16adff42fface41664dc2a3cb564f45fcf"><code>adb77a1</code></a>
[58_maintenance] Fix MSRV CI check (pin tonic to 0.14.5, install
cargo-msrv -...</li>
<li>See full diff in <a
href="https://github.com/apache/arrow-rs/compare/58.3.0...58.4.0">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 13:28:54 -07:00
Dan Tasse 29c030f865 fix: accept either timestamp or timestamp_millis for versions (#3733)
`list_versions()` against a remote table on a server that uses
lance-namespace was failing. The server was returning
`timestamp_millis`, while db-catalog deployments were returning
`timestamp`, and the client was only accepting `timestamp`. So, updated
the client to accept both. (assuming we're migrating over time;
eventually we can turn off the `timestamp` code path I suppose.)
2026-07-28 10:44:49 -04:00
Xuanwo ff6ff09998 feat: support batched blob range reads (#3703)
## Summary

Lance can now plan multiple byte ranges for the same blob in one
`read_blob_ranges` operation, but LanceDB users currently cannot expose
a complete set of logical ranges to that planner.

This complements `BlobFile`: file-like consumers such as PyAV can
continue to discover ranges dynamically, while callers that already know
the ranges for a batch can submit them together.

## Motivating example

A training table may store a large video blob together with a small
application-level clip index:

```text
video: blob
clips: [{offset, length}, ...]
```

The caller can select the videos and clips for a batch, obtain their row
IDs from the query, and read all of the selected windows together:

```python
rows = (
    table.search()
    .select(["clips"])
    .with_row_id(True)
    .limit(64)
    .to_arrow()
    .to_pylist()
)

requests = []
for row in rows:
    clip = sample_clip(row["clips"])
    requests.append(
        (row["_rowid"], clip["offset"], clip["length"])
    )

chunks = table.fetch_blob_ranges("video", requests)
```

Here, `_rowid` comes from the LanceDB query, while `offset` and `length`
come from the application's clip index and are relative to that row's
video blob. The caller describes only the logical reads; Lance still
handles validation, source grouping, coalescing, scheduling, and byte
backpressure.

Lance v10.0.0-beta.5 returns one logical result per blob selector or
range request and explicitly distinguishes null blobs from valid empty
values. LanceDB consumes that aligned result contract directly and only
adds a cardinality check for unresolved row IDs.

This PR exposes batched blob-range reads on local Rust and Python
tables. Results preserve request identity, duplicates, null slots, and
valid empty ranges while allowing Lance to execute the physical reads
out of order. Scheduler buffer sizing remains an internal Lance concern,
so the LanceDB API does not expose `io_buffer_size`.

Cloud tables continue to report this operation as unsupported until
there is a corresponding remote API.
2026-07-27 15:08:47 -07:00
Vivek 119b9baf90 fix: preserve row count in MetadataEraserExec for zero-column batches (#3717)
SELECT COUNT(*) FROM t WHERE <predicate> — and any query that plans an
empty-projection scan — panics the executing query task:

InvalidArgumentError("must either specify a row count or at least one
column")

Root cause

MetadataEraserExec wraps every LanceDB table scan to strip schema-level
metadata, rebuilding each batch in execute():

RecordBatch::try_new(schema.clone(), batch.columns().to_vec()).unwrap()

RecordBatch::try_new infers the row count from the columns. COUNT(*)
with a filter is planned with an empty projection, so the scan emits
zero-column batches — there are no columns to infer a length from,
try_new returns Err, and the .unwrap() panics.

(This is specific to the empty-projection case: COUNT(*) with no filter
is answered from statistics and never scans, and COUNT(<col>) projects a
column — both already work.)
2026-07-27 12:25:11 -07:00
LanceDB Robot ba4558a64f chore: update lance dependency to v10.0.0-beta.5 (#3718)
Updates the Rust workspace Lance dependencies and Java lance-core
dependency to v10.0.0-beta.5. No compatibility fixes were required;
full-workspace Clippy passes with warnings denied. Lance tag:
https://github.com/lance-format/lance/releases/tag/v10.0.0-beta.5
2026-07-27 15:27:40 +08:00
Heng Ge f655f62e09 feat(query): add use_lsm to read MemWAL LSM data (#3489)
## What

MemWAL LSM **read** support. When a table has an LSM write spec
(`set_lsm_write_spec`), `merge_insert` upserts live in the MemWAL
active/frozen memtables and flushed SSTables until an external
compaction merges them into the base table, so a normal scan returns
**stale** data. This routes reads through Lance's `LsmScanner` so
queries also surface that in-flight data, deduplicated by primary key
(newest generation wins).

## How

- Adds a **`use_lsm: Option<bool>`** query flag, symmetric with the
`merge_insert` flag:
- **unset** — auto-route through the LSM scanner when the table carries
a write spec
- **`use_lsm(true)`** — force the LSM path; error if there is no spec
    - **`use_lsm(false)`** — read the base table only (the escape hatch)
- Plain scan, single-column full-text search, and single-vector ANN all
run through one `LsmScanner` (assembled from on-disk shard manifests
plus the cached writer's in-memory memtables), so a `where` predicate is
honored as a **prefilter** uniformly — including for vector search.
- **Compaction-aware snapshots:** an SSTable generation is dropped only
once it is both compacted into the base table and covered by the arm's
base-index catch-up (`index_catchup`); plain scans use the compaction
watermark alone.
- Query shapes the scanner cannot honor hard-error with guidance to set
`use_lsm(false)`: hybrid, multi/binary vectors, `with_row_id`,
reranking, `order_by`, dynamic/Substrait projection or filters,
`distance_range`, `use_index(false)`, postfilter, take-by-row-id/offset,
reads from a time-traveled version, and an unmaintained or ambiguous
(multiple) FTS/vector index. Namespace-pushdown queries fall back to
local execution when a spec is present; WAL-only writers are handled.
- Exposed across the Rust core and the Python (`use_lsm`) and TypeScript
(`useLsm`) bindings, including `TakeQuery`.

Rebased from Lance `7.2.0-beta.3` to `10.0.0-beta.3`.
2026-07-25 23:45:27 -07:00
Will Jones bf15655c83 chore: unify SDK versions and release tags on a single line (#3714)
Python was versioned and tagged separately from the Rust, Java, and
Node.js SDKs, and had drifted three minor versions ahead (0.36 vs 0.33).
Users had no way to tell which Python version corresponded to which Rust
or Node release, and the gap had no meaning behind it.

This unifies the two tracks so there is one version and one tag for all
four SDKs.

## Version

The shared version is set to `0.37.0-beta.0`. Python continues its own
sequence (highest published: 0.36 → 0.37) while Rust, Java, and Node.js
jump 0.33 → 0.37 to meet it. Picking Python's next minor means Python
users see no discontinuity at all, and only the other SDKs skip forward.

Note that `main` trails the `release/v0.32` branch on both lines (main
is at 0.32.0-beta.3 / 0.35.0-beta.3; the release branch carries
0.33.0-beta.0 / 0.36.0-beta.0), so 0.37 is chosen to clear the highest
tag on either branch. Every index stays monotonic:

| index | publishes | last published | next |
|---|---|---|---|
| PyPI | stable only | 0.34.0 | 0.37.0 |
| Fury | previews | 0.36.0b0 | 0.37.0-beta.1 |
| npm | both | 0.33.0-beta.0 | 0.37.0-beta.1 |
| crates.io | stable only | 0.31.0 | 0.37.0 |
| Maven | both | 0.33.0-beta.0 | 0.37.0-beta.1 |

A one-time jump for three SDKs, versus explaining the offset
indefinitely.

## Mechanism

* `python/.bumpversion.toml` is removed. `python/Cargo.toml` — the
source of the Python package version, since `pyproject.toml` declares
`dynamic = ["version"]` — becomes a tracked file of the root config. Its
`cargo update -p lancedb-python` pre-commit hook is dropped as
redundant: `ci/update_lockfiles.sh` already refreshes every workspace
member version in `Cargo.lock`.
* `pypi-publish.yml` triggers on `v*` instead of `python-v*`, so one tag
releases all four packages. `ci/bump_version.sh` and
`make-release-commit.yml` lose their now-dead tag-prefix and
per-language plumbing, including the `python` / `other` dispatch inputs.
* The two byte-identical GH release jobs in `npm-publish.yml` and
`pypi-publish.yml` are replaced by a single `gh-release.yml`. One
release per tag, named `LanceDB vX.Y.Z`, instead of separate "Python
LanceDB" and "Node/Rust LanceDB" releases for the same commit.

The trade-off: there is no longer a way to ship a Python-only patch
without also releasing crates.io, Maven, and npm. That is the cost of
making drift structurally impossible.

## Beta releases marked "Latest" (#3666)

Both GH release jobs used:

```yaml
prerelease: ${{ contains('beta', github.ref) }}
```

The arguments are reversed. `contains(search, item)` asks whether
*`search`* contains *`item`*, so this evaluated "does the literal string
`'beta'` contain `refs/tags/python-v0.35.0-beta.2`?" — always `false`.
Every beta was published as a full release, and GitHub awards "Latest"
to the newest non-prerelease.

The new workflow derives the flag from the parsed version rather than
the raw ref, and sets `make_latest` explicitly:

```yaml
prerelease: ${{ steps.extract_version.outputs.prerelease }}
make_latest: ${{ steps.extract_version.outputs.prerelease == 'false' }}
```

npm was never affected (`--tag preview` uses correct bash), and PyPI
already excludes pre-releases from resolution.

This only fixes releases published from here on. Already-published betas
need a one-time backfill:

```shell
gh api --paginate /repos/lancedb/lancedb/releases \
  --jq '.[] | select(.prerelease == false) | select(.tag_name | test("beta")) | .id' \
  | xargs -I{} gh api -X PATCH /repos/lancedb/lancedb/releases/{} -F prerelease=true
```

## Verification

Ran `ci/bump_version.sh` end-to-end against this branch with the release
tooling installed:

* `preview` → tags `v0.37.0-beta.1` (previous tag `v0.33.0-beta.0`
detected, `pre_n` bump)
* `stable` → tags `v0.37.0`
* Both paths update `.bumpversion.toml`, `rust/lancedb/Cargo.toml`,
`nodejs/Cargo.toml`, `python/Cargo.toml`, `nodejs/package.json`, the 7
`nodejs/npm/*/package.json` files, both Java poms, and
`docs/src/java/java.md` together
* `check_breaking_changes.py` resolves the last stable as `v0.31.0`, so
the minor-version gate passes

All five touched workflows parse as valid YAML and the pre-commit hooks
pass.

## Notes for review

* This targets `main` only, so it takes effect at the next
release-branch cut. The in-flight `release/v0.32` branch still carries
`v0.33.0-beta.0` / `python-v0.36.0-beta.0`; if we want the imminent
stable to be 0.37.0, this needs to be applied there too.
* Historical `python-v*` tags are left alone. The changelog builder
scans `^v`, which does not match them, so the first unified release's
notes will compute `fromTag` from the Rust/Node line only — a one-time
gap in the Python-side changelog.
* Pre-existing and not addressed here: `ci/update_lockfiles.sh --amend`
amends the commit that `bump-my-version` has already tagged, so the
lockfile update lands outside the tag on stable releases.

Fixes #3666
2026-07-25 09:22:35 -07:00
Lance Release a00edef0e6 Bump version: 0.32.0-beta.2 → 0.32.0-beta.3 2026-07-24 22:04:02 +00:00
Lance Release 1b2670443e Bump version: 0.35.0-beta.2 → 0.35.0-beta.3 2026-07-24 22:03:30 +00:00
Yang Cen 9dc5ec03aa feat(fts): add block size configuration (#3691)
## What changed

- add `block_size` to Python FTS configuration and the deprecated
local/remote helpers
- add `blockSize` to the TypeScript FTS options and propagate it through
the NAPI binding
- serialize the value as `block_size` for remote index creation
- document the existing Rust builder API and generate the TypeScript API
reference
- add local, remote, metadata, search, and invalid-value regression
coverage

## Why

Lance supports configuring the number of documents per compressed FTS
posting block, but LanceDB's Python and TypeScript APIs did not expose
the setting. This made the experimental FTS V3 layout unavailable
through those clients and allowed the value to be dropped before index
creation.

## How it works

The default remains `128`. Supported values are `128` and `256`;
selecting `256` uses the experimental FTS V3 format. Invalid values are
rejected by the Lance builder and surfaced as Python or JavaScript
errors.

## Validation

- `cargo check --quiet --features remote --tests --examples`
- `cargo +1.94.0 clippy --quiet --features remote --tests --examples --
-D warnings`
- targeted Rust local and remote index tests
- Rust doctests: 34 passed
- Python Ruff checks, doctest, and targeted local/remote tests: 5 passed
- TypeScript build, Biome lint, generated docs, and targeted Jest tests:
9 passed
- `git diff --check`

## Limitations

The Java client remains unchanged because its external remote REST model
does not currently expose `block_size`.

Co-authored-by: Yang Cen <yangcen@Yangs-Mac-mini.local>
2026-07-24 15:02:38 -07:00
Andrew Chen 18760f74cd fix: crash in AnswerdotaiRerankers/ColbertReranker for return_score="all" (#3671)
## What

`AnswerdotaiRerankers(return_score="all").rerank_hybrid(...)` (and
`ColbertReranker`, which subclasses it without overriding
`rerank_hybrid`) raises:

```
pyarrow.lib.ArrowInvalid: Invalid sort key column: No match for FieldRef.Name(_relevance_score) in _rowid: int64 ...
```

## Why

```python
combined_results = self.merge_results(vector_results, fts_results)
combined_results = self._rerank(combined_results, query)
if self.score == "relevance":
    combined_results = self._keep_relevance_score(combined_results)
elif self.score == "all":
    combined_results = self._merge_and_keep_scores(vector_results, fts_results)
```

When `score == "all"`, `combined_results` is unconditionally overwritten
by `_merge_and_keep_scores(vector_results, fts_results)` **after**
`_rerank()` already computed and appended `_relevance_score` —
discarding it. The following `sort_by("_relevance_score", ...)` then has
nothing to sort on.

Every sibling reranker that supports `return_score="all"`
(`cross_encoder`, `openai`, `cohere`, `jinaai`, `voyageai`, `watsonx`)
instead calls `_merge_and_keep_scores()` **before** `_rerank()`. This
file is the one place the ordering got inverted when `"all"` support was
added (#2509) — a copy/paste inconsistency across the six files that PR
touched. Fix mirrors the pattern already used (and tested) by the other
five rerankers.

Also drops the now-stale `"Only 'relevance' is supported for now"`
docstring line on both classes, left over from before `"all"` support
existed.

## Testing

Added `test_answerdotai_reranker_return_all`, mirroring the existing
`test_cross_encoder_reranker_return_all`. Verified locally with the real
built Rust extension: red (reproduces the exact `ArrowInvalid` above) →
green, using the actual `rerank_hybrid`/`_rerank`/`base.py` code path
with the model call mocked out — my local environment's
`rerankers==0.10.0` fails to load the real ColBERT model against the
available `transformers` version (`AttributeError: 'ColBERTModel' object
has no attribute 'all_tied_weights_keys'`), which I confirmed also
breaks the **pre-existing**, unmodified
`test_colbert_reranker`/`test_answerdotai_reranker` baseline tests
identically — an unrelated local dependency-version issue, not a
regression from this change. `ruff check`/`ruff format` clean; full
`test_rerankers.py` run: 9 passed / 8 skipped / 3 failed (the 3 failures
are exactly those two pre-existing tests plus my new one, all failing at
model-loading time for the same unrelated reason before reaching the
changed code).

---
Disclosure: this PR was drafted with AI assistance (Claude); I reviewed,
tested, and take responsibility for the change.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 15:02:23 -07:00
LanceDB Robot c9d07ef6fc chore: update lance dependency to v10.0.0-beta.3 (#3710)
Updates the Rust workspace and Java lance-core dependencies to [Lance
v10.0.0-beta.3](https://github.com/lance-format/lance/releases/tag/v10.0.0-beta.3).

Includes compatibility updates for Lance’s nullable blob payload and
handle APIs.
2026-07-24 15:01:30 -07:00
Eran Dagan 0bc081608a fix(python): allow selection of _rowid in Permutation (#3133)
Closes #3132
2026-07-22 14:11:57 -07:00
Prashanth Rao d6f9f8560e docs(java): fill Java API reference gaps (#3615)
## Summary

This updates the Java API reference to close the documentation gaps that
can be fixed from the current Java source and generated namespace API.

The patch adds an empty table example, shows how to wrap returned Arrow
IPC query bytes in a reusable `ArrowFileReader` helper, and documents
the Java index operations that are currently exposed by the namespace
client: vector indexes, scalar indexes, full text search indexes, and
listing indexes.

## Issue Links

Fixes https://github.com/lancedb/docs/issues/157
Fixes https://github.com/lancedb/docs/issues/160

Partially addresses https://github.com/lancedb/docs/issues/159 by
documenting the index parameters currently exposed by Java. The
requested `num_partitions` example is still blocked because
`CreateTableIndexRequest` does not expose IVF training parameters yet.

Not included: https://github.com/lancedb/docs/issues/158. The current
Java docs and source remain remote namespace oriented, so local DB
connection documentation should wait until the Java local DB API is
available and can be verified.

## Validation

- Built the Java core module with OpenJDK 17:
  `./mvnw -pl lancedb-core -am -DskipTests compile`
- Checked the Markdown diff:
  `git diff --check -- docs/src/java/java.md`

The Java build succeeds. It still reports pre-existing checkstyle
warnings in the namespace client builder, but the Maven build is green.
2026-07-22 17:05:58 -04:00
Dan Tasse 0bd0944062 feat: branch skill updates for merge (#3685)
Skill updates for branch merging. Terra/Sol can do an end-to-end "create
3 branches, add a column, generate embeddings, merge the best" workflow
now.
2026-07-22 13:29:06 -04:00
dependabot[bot] 91f775c093 chore(deps): bump the rust-minor-patch group across 1 directory with 19 updates (#3700)
Bumps the rust-minor-patch group with 11 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [async-trait](https://github.com/dtolnay/async-trait) | `0.1.89` |
`0.1.91` |
| [datafusion](https://github.com/apache/datafusion) | `54.0.0` |
`54.1.0` |
| [regex](https://github.com/rust-lang/regex) | `1.13.0` | `1.13.1` |
| [tokio](https://github.com/tokio-rs/tokio) | `1.52.3` | `1.53.1` |
| [serde](https://github.com/serde-rs/serde) | `1.0.228` | `1.0.229` |
| [serde_json](https://github.com/serde-rs/json) | `1.0.150` | `1.0.151`
|
| [uuid](https://github.com/uuid-rs/uuid) | `1.23.5` | `1.24.0` |
| [anyhow](https://github.com/dtolnay/anyhow) | `1.0.103` | `1.0.104` |
| [napi](https://github.com/napi-rs/napi-rs) | `3.10.5` | `3.11.0` |
| [napi-derive](https://github.com/napi-rs/napi-rs) | `3.5.10` | `3.6.0`
|
| [libc](https://github.com/rust-lang/libc) | `0.2.186` | `0.2.189` |


Updates `async-trait` from 0.1.89 to 0.1.91
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/async-trait/releases">async-trait's
releases</a>.</em></p>
<blockquote>
<h2>0.1.90</h2>
<ul>
<li>Update to syn 3</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/dtolnay/async-trait/commit/d049ee02a2d50b72e03d07f06311e23bf5b512a8"><code>d049ee0</code></a>
Release 0.1.91</li>
<li><a
href="https://github.com/dtolnay/async-trait/commit/7a0961f275432c40cc5e7aa011362e4b50d763b1"><code>7a0961f</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/async-trait/issues/301">#301</a>
from dtolnay/mutability</li>
<li><a
href="https://github.com/dtolnay/async-trait/commit/740f86f23d176229011f2389c8a206ef5ba547e7"><code>740f86f</code></a>
Ignore mut_mut pedantic clippy lint in test</li>
<li><a
href="https://github.com/dtolnay/async-trait/commit/4699cd320a8aaaf06a2a369cb9e1f2964b14b71c"><code>4699cd3</code></a>
Fix mutability for by-reference receivers</li>
<li><a
href="https://github.com/dtolnay/async-trait/commit/6dd3573df95878d34fcfc0ab9c242aeab3140f82"><code>6dd3573</code></a>
Add regression test for issue 300</li>
<li><a
href="https://github.com/dtolnay/async-trait/commit/2371797a3938808bd7e1f4f9abd0eed51bd99634"><code>2371797</code></a>
Release 0.1.90</li>
<li><a
href="https://github.com/dtolnay/async-trait/commit/d03f075ecc2b9fcbf6757f3654a7974a518a144e"><code>d03f075</code></a>
Merge pull request <a
href="https://redirect.github.com/dtolnay/async-trait/issues/299">#299</a>
from dtolnay/syn3</li>
<li><a
href="https://github.com/dtolnay/async-trait/commit/6cf42c104d1c02aa97d4fc62ff117f8d6b05eacb"><code>6cf42c1</code></a>
Update to syn 3</li>
<li><a
href="https://github.com/dtolnay/async-trait/commit/b9daabad756580d31bd2b9221ea599db51bf6cdd"><code>b9daaba</code></a>
Ignore match_same_arms pedantic clippy lint</li>
<li><a
href="https://github.com/dtolnay/async-trait/commit/aa706d127114e57dc163238af947ba495b0b86d2"><code>aa706d1</code></a>
Update actions/upload-artifact@v6 -&gt; v7</li>
<li>Additional commits viewable in <a
href="https://github.com/dtolnay/async-trait/compare/0.1.89...0.1.91">compare
view</a></li>
</ul>
</details>
<br />

Updates `datafusion` from 54.0.0 to 54.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/datafusion/commit/0d1f2ebe2cc97c91b736bc0a160b5b73cf40437a"><code>0d1f2eb</code></a>
[branch-54] chore: Update version 54.1.0, add changelog (<a
href="https://redirect.github.com/apache/datafusion/issues/23689">#23689</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/fcbc9bda1fd004c349fa70f61f8c8b40448294ca"><code>fcbc9bd</code></a>
[branch-54] Handle nulls in type coercion of higher-order UDFs,
map_extract, ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/61b0f76a32b95560b49d5453f021b3ffdb9bb255"><code>61b0f76</code></a>
[branch-54] Resolve lost wakeup in SpillPoolReader with multiple conc…
(<a
href="https://redirect.github.com/apache/datafusion/issues/23654">#23654</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/2142d5b2a737fa01836906d2526265035964ac99"><code>2142d5b</code></a>
[branch-54] chore: fix cargo audit (<a
href="https://redirect.github.com/apache/datafusion/issues/23607">#23607</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/c735a49a3634b01a0a6e80fe13ef1059998b3329"><code>c735a49</code></a>
[branch-54] fix: don't duplicate volatile expressions when pushing
projection...</li>
<li><a
href="https://github.com/apache/datafusion/commit/82acf50e23103aa01d16518765c0b3bdf10c60f2"><code>82acf50</code></a>
[branch-54] perf: avoid intermediate slice allocation in Spark slice
function...</li>
<li><a
href="https://github.com/apache/datafusion/commit/887b065e7393ac8912a5c6f5b2b44799407b8f33"><code>887b065</code></a>
[branch-54] fix: preserve no-filter SMJ matches across pending outer
batches ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/06c4d41c3a894cd4576a764cde1486ca062c4f12"><code>06c4d41</code></a>
[branch-54] fix: handle <code>IS TRUE</code> correctly in
<code>EliminateOuterJoin</code> (backport...</li>
<li><a
href="https://github.com/apache/datafusion/commit/7fd1b0dfa1b96b25d8ac357aefb921fcbdc60ce1"><code>7fd1b0d</code></a>
[branch-54] fix: Correctly compute nullability in recursive CTE schemas
(back...</li>
<li><a
href="https://github.com/apache/datafusion/commit/9d41b02983058d692373c7ce18f6bba3b3afd5cc"><code>9d41b02</code></a>
[branch-54] fix: regex simplification of anchored patterns produces
wrong res...</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/datafusion/compare/54.0.0...54.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `datafusion-catalog` from 54.0.0 to 54.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/datafusion/commit/0d1f2ebe2cc97c91b736bc0a160b5b73cf40437a"><code>0d1f2eb</code></a>
[branch-54] chore: Update version 54.1.0, add changelog (<a
href="https://redirect.github.com/apache/datafusion/issues/23689">#23689</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/fcbc9bda1fd004c349fa70f61f8c8b40448294ca"><code>fcbc9bd</code></a>
[branch-54] Handle nulls in type coercion of higher-order UDFs,
map_extract, ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/61b0f76a32b95560b49d5453f021b3ffdb9bb255"><code>61b0f76</code></a>
[branch-54] Resolve lost wakeup in SpillPoolReader with multiple conc…
(<a
href="https://redirect.github.com/apache/datafusion/issues/23654">#23654</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/2142d5b2a737fa01836906d2526265035964ac99"><code>2142d5b</code></a>
[branch-54] chore: fix cargo audit (<a
href="https://redirect.github.com/apache/datafusion/issues/23607">#23607</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/c735a49a3634b01a0a6e80fe13ef1059998b3329"><code>c735a49</code></a>
[branch-54] fix: don't duplicate volatile expressions when pushing
projection...</li>
<li><a
href="https://github.com/apache/datafusion/commit/82acf50e23103aa01d16518765c0b3bdf10c60f2"><code>82acf50</code></a>
[branch-54] perf: avoid intermediate slice allocation in Spark slice
function...</li>
<li><a
href="https://github.com/apache/datafusion/commit/887b065e7393ac8912a5c6f5b2b44799407b8f33"><code>887b065</code></a>
[branch-54] fix: preserve no-filter SMJ matches across pending outer
batches ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/06c4d41c3a894cd4576a764cde1486ca062c4f12"><code>06c4d41</code></a>
[branch-54] fix: handle <code>IS TRUE</code> correctly in
<code>EliminateOuterJoin</code> (backport...</li>
<li><a
href="https://github.com/apache/datafusion/commit/7fd1b0dfa1b96b25d8ac357aefb921fcbdc60ce1"><code>7fd1b0d</code></a>
[branch-54] fix: Correctly compute nullability in recursive CTE schemas
(back...</li>
<li><a
href="https://github.com/apache/datafusion/commit/9d41b02983058d692373c7ce18f6bba3b3afd5cc"><code>9d41b02</code></a>
[branch-54] fix: regex simplification of anchored patterns produces
wrong res...</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/datafusion/compare/54.0.0...54.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `datafusion-common` from 54.0.0 to 54.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/datafusion/commit/0d1f2ebe2cc97c91b736bc0a160b5b73cf40437a"><code>0d1f2eb</code></a>
[branch-54] chore: Update version 54.1.0, add changelog (<a
href="https://redirect.github.com/apache/datafusion/issues/23689">#23689</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/fcbc9bda1fd004c349fa70f61f8c8b40448294ca"><code>fcbc9bd</code></a>
[branch-54] Handle nulls in type coercion of higher-order UDFs,
map_extract, ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/61b0f76a32b95560b49d5453f021b3ffdb9bb255"><code>61b0f76</code></a>
[branch-54] Resolve lost wakeup in SpillPoolReader with multiple conc…
(<a
href="https://redirect.github.com/apache/datafusion/issues/23654">#23654</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/2142d5b2a737fa01836906d2526265035964ac99"><code>2142d5b</code></a>
[branch-54] chore: fix cargo audit (<a
href="https://redirect.github.com/apache/datafusion/issues/23607">#23607</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/c735a49a3634b01a0a6e80fe13ef1059998b3329"><code>c735a49</code></a>
[branch-54] fix: don't duplicate volatile expressions when pushing
projection...</li>
<li><a
href="https://github.com/apache/datafusion/commit/82acf50e23103aa01d16518765c0b3bdf10c60f2"><code>82acf50</code></a>
[branch-54] perf: avoid intermediate slice allocation in Spark slice
function...</li>
<li><a
href="https://github.com/apache/datafusion/commit/887b065e7393ac8912a5c6f5b2b44799407b8f33"><code>887b065</code></a>
[branch-54] fix: preserve no-filter SMJ matches across pending outer
batches ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/06c4d41c3a894cd4576a764cde1486ca062c4f12"><code>06c4d41</code></a>
[branch-54] fix: handle <code>IS TRUE</code> correctly in
<code>EliminateOuterJoin</code> (backport...</li>
<li><a
href="https://github.com/apache/datafusion/commit/7fd1b0dfa1b96b25d8ac357aefb921fcbdc60ce1"><code>7fd1b0d</code></a>
[branch-54] fix: Correctly compute nullability in recursive CTE schemas
(back...</li>
<li><a
href="https://github.com/apache/datafusion/commit/9d41b02983058d692373c7ce18f6bba3b3afd5cc"><code>9d41b02</code></a>
[branch-54] fix: regex simplification of anchored patterns produces
wrong res...</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/datafusion/compare/54.0.0...54.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `datafusion-execution` from 54.0.0 to 54.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/datafusion/commit/0d1f2ebe2cc97c91b736bc0a160b5b73cf40437a"><code>0d1f2eb</code></a>
[branch-54] chore: Update version 54.1.0, add changelog (<a
href="https://redirect.github.com/apache/datafusion/issues/23689">#23689</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/fcbc9bda1fd004c349fa70f61f8c8b40448294ca"><code>fcbc9bd</code></a>
[branch-54] Handle nulls in type coercion of higher-order UDFs,
map_extract, ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/61b0f76a32b95560b49d5453f021b3ffdb9bb255"><code>61b0f76</code></a>
[branch-54] Resolve lost wakeup in SpillPoolReader with multiple conc…
(<a
href="https://redirect.github.com/apache/datafusion/issues/23654">#23654</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/2142d5b2a737fa01836906d2526265035964ac99"><code>2142d5b</code></a>
[branch-54] chore: fix cargo audit (<a
href="https://redirect.github.com/apache/datafusion/issues/23607">#23607</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/c735a49a3634b01a0a6e80fe13ef1059998b3329"><code>c735a49</code></a>
[branch-54] fix: don't duplicate volatile expressions when pushing
projection...</li>
<li><a
href="https://github.com/apache/datafusion/commit/82acf50e23103aa01d16518765c0b3bdf10c60f2"><code>82acf50</code></a>
[branch-54] perf: avoid intermediate slice allocation in Spark slice
function...</li>
<li><a
href="https://github.com/apache/datafusion/commit/887b065e7393ac8912a5c6f5b2b44799407b8f33"><code>887b065</code></a>
[branch-54] fix: preserve no-filter SMJ matches across pending outer
batches ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/06c4d41c3a894cd4576a764cde1486ca062c4f12"><code>06c4d41</code></a>
[branch-54] fix: handle <code>IS TRUE</code> correctly in
<code>EliminateOuterJoin</code> (backport...</li>
<li><a
href="https://github.com/apache/datafusion/commit/7fd1b0dfa1b96b25d8ac357aefb921fcbdc60ce1"><code>7fd1b0d</code></a>
[branch-54] fix: Correctly compute nullability in recursive CTE schemas
(back...</li>
<li><a
href="https://github.com/apache/datafusion/commit/9d41b02983058d692373c7ce18f6bba3b3afd5cc"><code>9d41b02</code></a>
[branch-54] fix: regex simplification of anchored patterns produces
wrong res...</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/datafusion/compare/54.0.0...54.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `datafusion-expr` from 54.0.0 to 54.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/datafusion/commit/0d1f2ebe2cc97c91b736bc0a160b5b73cf40437a"><code>0d1f2eb</code></a>
[branch-54] chore: Update version 54.1.0, add changelog (<a
href="https://redirect.github.com/apache/datafusion/issues/23689">#23689</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/fcbc9bda1fd004c349fa70f61f8c8b40448294ca"><code>fcbc9bd</code></a>
[branch-54] Handle nulls in type coercion of higher-order UDFs,
map_extract, ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/61b0f76a32b95560b49d5453f021b3ffdb9bb255"><code>61b0f76</code></a>
[branch-54] Resolve lost wakeup in SpillPoolReader with multiple conc…
(<a
href="https://redirect.github.com/apache/datafusion/issues/23654">#23654</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/2142d5b2a737fa01836906d2526265035964ac99"><code>2142d5b</code></a>
[branch-54] chore: fix cargo audit (<a
href="https://redirect.github.com/apache/datafusion/issues/23607">#23607</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/c735a49a3634b01a0a6e80fe13ef1059998b3329"><code>c735a49</code></a>
[branch-54] fix: don't duplicate volatile expressions when pushing
projection...</li>
<li><a
href="https://github.com/apache/datafusion/commit/82acf50e23103aa01d16518765c0b3bdf10c60f2"><code>82acf50</code></a>
[branch-54] perf: avoid intermediate slice allocation in Spark slice
function...</li>
<li><a
href="https://github.com/apache/datafusion/commit/887b065e7393ac8912a5c6f5b2b44799407b8f33"><code>887b065</code></a>
[branch-54] fix: preserve no-filter SMJ matches across pending outer
batches ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/06c4d41c3a894cd4576a764cde1486ca062c4f12"><code>06c4d41</code></a>
[branch-54] fix: handle <code>IS TRUE</code> correctly in
<code>EliminateOuterJoin</code> (backport...</li>
<li><a
href="https://github.com/apache/datafusion/commit/7fd1b0dfa1b96b25d8ac357aefb921fcbdc60ce1"><code>7fd1b0d</code></a>
[branch-54] fix: Correctly compute nullability in recursive CTE schemas
(back...</li>
<li><a
href="https://github.com/apache/datafusion/commit/9d41b02983058d692373c7ce18f6bba3b3afd5cc"><code>9d41b02</code></a>
[branch-54] fix: regex simplification of anchored patterns produces
wrong res...</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/datafusion/compare/54.0.0...54.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `datafusion-functions` from 54.0.0 to 54.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/datafusion/commit/0d1f2ebe2cc97c91b736bc0a160b5b73cf40437a"><code>0d1f2eb</code></a>
[branch-54] chore: Update version 54.1.0, add changelog (<a
href="https://redirect.github.com/apache/datafusion/issues/23689">#23689</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/fcbc9bda1fd004c349fa70f61f8c8b40448294ca"><code>fcbc9bd</code></a>
[branch-54] Handle nulls in type coercion of higher-order UDFs,
map_extract, ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/61b0f76a32b95560b49d5453f021b3ffdb9bb255"><code>61b0f76</code></a>
[branch-54] Resolve lost wakeup in SpillPoolReader with multiple conc…
(<a
href="https://redirect.github.com/apache/datafusion/issues/23654">#23654</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/2142d5b2a737fa01836906d2526265035964ac99"><code>2142d5b</code></a>
[branch-54] chore: fix cargo audit (<a
href="https://redirect.github.com/apache/datafusion/issues/23607">#23607</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/c735a49a3634b01a0a6e80fe13ef1059998b3329"><code>c735a49</code></a>
[branch-54] fix: don't duplicate volatile expressions when pushing
projection...</li>
<li><a
href="https://github.com/apache/datafusion/commit/82acf50e23103aa01d16518765c0b3bdf10c60f2"><code>82acf50</code></a>
[branch-54] perf: avoid intermediate slice allocation in Spark slice
function...</li>
<li><a
href="https://github.com/apache/datafusion/commit/887b065e7393ac8912a5c6f5b2b44799407b8f33"><code>887b065</code></a>
[branch-54] fix: preserve no-filter SMJ matches across pending outer
batches ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/06c4d41c3a894cd4576a764cde1486ca062c4f12"><code>06c4d41</code></a>
[branch-54] fix: handle <code>IS TRUE</code> correctly in
<code>EliminateOuterJoin</code> (backport...</li>
<li><a
href="https://github.com/apache/datafusion/commit/7fd1b0dfa1b96b25d8ac357aefb921fcbdc60ce1"><code>7fd1b0d</code></a>
[branch-54] fix: Correctly compute nullability in recursive CTE schemas
(back...</li>
<li><a
href="https://github.com/apache/datafusion/commit/9d41b02983058d692373c7ce18f6bba3b3afd5cc"><code>9d41b02</code></a>
[branch-54] fix: regex simplification of anchored patterns produces
wrong res...</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/datafusion/compare/54.0.0...54.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `datafusion-physical-plan` from 54.0.0 to 54.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/datafusion/commit/0d1f2ebe2cc97c91b736bc0a160b5b73cf40437a"><code>0d1f2eb</code></a>
[branch-54] chore: Update version 54.1.0, add changelog (<a
href="https://redirect.github.com/apache/datafusion/issues/23689">#23689</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/fcbc9bda1fd004c349fa70f61f8c8b40448294ca"><code>fcbc9bd</code></a>
[branch-54] Handle nulls in type coercion of higher-order UDFs,
map_extract, ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/61b0f76a32b95560b49d5453f021b3ffdb9bb255"><code>61b0f76</code></a>
[branch-54] Resolve lost wakeup in SpillPoolReader with multiple conc…
(<a
href="https://redirect.github.com/apache/datafusion/issues/23654">#23654</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/2142d5b2a737fa01836906d2526265035964ac99"><code>2142d5b</code></a>
[branch-54] chore: fix cargo audit (<a
href="https://redirect.github.com/apache/datafusion/issues/23607">#23607</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/c735a49a3634b01a0a6e80fe13ef1059998b3329"><code>c735a49</code></a>
[branch-54] fix: don't duplicate volatile expressions when pushing
projection...</li>
<li><a
href="https://github.com/apache/datafusion/commit/82acf50e23103aa01d16518765c0b3bdf10c60f2"><code>82acf50</code></a>
[branch-54] perf: avoid intermediate slice allocation in Spark slice
function...</li>
<li><a
href="https://github.com/apache/datafusion/commit/887b065e7393ac8912a5c6f5b2b44799407b8f33"><code>887b065</code></a>
[branch-54] fix: preserve no-filter SMJ matches across pending outer
batches ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/06c4d41c3a894cd4576a764cde1486ca062c4f12"><code>06c4d41</code></a>
[branch-54] fix: handle <code>IS TRUE</code> correctly in
<code>EliminateOuterJoin</code> (backport...</li>
<li><a
href="https://github.com/apache/datafusion/commit/7fd1b0dfa1b96b25d8ac357aefb921fcbdc60ce1"><code>7fd1b0d</code></a>
[branch-54] fix: Correctly compute nullability in recursive CTE schemas
(back...</li>
<li><a
href="https://github.com/apache/datafusion/commit/9d41b02983058d692373c7ce18f6bba3b3afd5cc"><code>9d41b02</code></a>
[branch-54] fix: regex simplification of anchored patterns produces
wrong res...</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/datafusion/compare/54.0.0...54.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `datafusion-physical-expr` from 54.0.0 to 54.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/datafusion/commit/0d1f2ebe2cc97c91b736bc0a160b5b73cf40437a"><code>0d1f2eb</code></a>
[branch-54] chore: Update version 54.1.0, add changelog (<a
href="https://redirect.github.com/apache/datafusion/issues/23689">#23689</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/fcbc9bda1fd004c349fa70f61f8c8b40448294ca"><code>fcbc9bd</code></a>
[branch-54] Handle nulls in type coercion of higher-order UDFs,
map_extract, ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/61b0f76a32b95560b49d5453f021b3ffdb9bb255"><code>61b0f76</code></a>
[branch-54] Resolve lost wakeup in SpillPoolReader with multiple conc…
(<a
href="https://redirect.github.com/apache/datafusion/issues/23654">#23654</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/2142d5b2a737fa01836906d2526265035964ac99"><code>2142d5b</code></a>
[branch-54] chore: fix cargo audit (<a
href="https://redirect.github.com/apache/datafusion/issues/23607">#23607</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/c735a49a3634b01a0a6e80fe13ef1059998b3329"><code>c735a49</code></a>
[branch-54] fix: don't duplicate volatile expressions when pushing
projection...</li>
<li><a
href="https://github.com/apache/datafusion/commit/82acf50e23103aa01d16518765c0b3bdf10c60f2"><code>82acf50</code></a>
[branch-54] perf: avoid intermediate slice allocation in Spark slice
function...</li>
<li><a
href="https://github.com/apache/datafusion/commit/887b065e7393ac8912a5c6f5b2b44799407b8f33"><code>887b065</code></a>
[branch-54] fix: preserve no-filter SMJ matches across pending outer
batches ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/06c4d41c3a894cd4576a764cde1486ca062c4f12"><code>06c4d41</code></a>
[branch-54] fix: handle <code>IS TRUE</code> correctly in
<code>EliminateOuterJoin</code> (backport...</li>
<li><a
href="https://github.com/apache/datafusion/commit/7fd1b0dfa1b96b25d8ac357aefb921fcbdc60ce1"><code>7fd1b0d</code></a>
[branch-54] fix: Correctly compute nullability in recursive CTE schemas
(back...</li>
<li><a
href="https://github.com/apache/datafusion/commit/9d41b02983058d692373c7ce18f6bba3b3afd5cc"><code>9d41b02</code></a>
[branch-54] fix: regex simplification of anchored patterns produces
wrong res...</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/datafusion/compare/54.0.0...54.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `datafusion-sql` from 54.0.0 to 54.1.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/apache/datafusion/commit/0d1f2ebe2cc97c91b736bc0a160b5b73cf40437a"><code>0d1f2eb</code></a>
[branch-54] chore: Update version 54.1.0, add changelog (<a
href="https://redirect.github.com/apache/datafusion/issues/23689">#23689</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/fcbc9bda1fd004c349fa70f61f8c8b40448294ca"><code>fcbc9bd</code></a>
[branch-54] Handle nulls in type coercion of higher-order UDFs,
map_extract, ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/61b0f76a32b95560b49d5453f021b3ffdb9bb255"><code>61b0f76</code></a>
[branch-54] Resolve lost wakeup in SpillPoolReader with multiple conc…
(<a
href="https://redirect.github.com/apache/datafusion/issues/23654">#23654</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/2142d5b2a737fa01836906d2526265035964ac99"><code>2142d5b</code></a>
[branch-54] chore: fix cargo audit (<a
href="https://redirect.github.com/apache/datafusion/issues/23607">#23607</a>)</li>
<li><a
href="https://github.com/apache/datafusion/commit/c735a49a3634b01a0a6e80fe13ef1059998b3329"><code>c735a49</code></a>
[branch-54] fix: don't duplicate volatile expressions when pushing
projection...</li>
<li><a
href="https://github.com/apache/datafusion/commit/82acf50e23103aa01d16518765c0b3bdf10c60f2"><code>82acf50</code></a>
[branch-54] perf: avoid intermediate slice allocation in Spark slice
function...</li>
<li><a
href="https://github.com/apache/datafusion/commit/887b065e7393ac8912a5c6f5b2b44799407b8f33"><code>887b065</code></a>
[branch-54] fix: preserve no-filter SMJ matches across pending outer
batches ...</li>
<li><a
href="https://github.com/apache/datafusion/commit/06c4d41c3a894cd4576a764cde1486ca062c4f12"><code>06c4d41</code></a>
[branch-54] fix: handle <code>IS TRUE</code> correctly in
<code>EliminateOuterJoin</code> (backport...</li>
<li><a
href="https://github.com/apache/datafusion/commit/7fd1b0dfa1b96b25d8ac357aefb921fcbdc60ce1"><code>7fd1b0d</code></a>
[branch-54] fix: Correctly compute nullability in recursive CTE schemas
(back...</li>
<li><a
href="https://github.com/apache/datafusion/commit/9d41b02983058d692373c7ce18f6bba3b3afd5cc"><code>9d41b02</code></a>
[branch-54] fix: regex simplification of anchored patterns produces
wrong res...</li>
<li>Additional commits viewable in <a
href="https://github.com/apache/datafusion/compare/54.0.0...54.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `regex` from 1.13.0 to 1.13.1
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/regex/blob/master/CHANGELOG.md">regex's
changelog</a>.</em></p>
<blockquote>
<h1>1.13.1 (2026-07-15)</h1>
<p>This is a release that fixes a bug where incorrect regex match
offsets could be
reported. Note that this doesn't impact whether a match occurs or not,
just
where it occurs. The match offsets are still valid for slicing, they
just may
not refer to the correct leftmost-first match. See
<a
href="https://redirect.github.com/rust-lang/regex/pull/1364">#1364</a>
for (many) more details.</p>
<p>Bug fixes:</p>
<ul>
<li><a
href="https://redirect.github.com/rust-lang/regex/issues/1354">#1354</a>:
Fixes previously unsound reverse suffix and inner optimizations.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/rust-lang/regex/commit/2b527599eb9eea0dcc288c704584f242f26a5c61"><code>2b52759</code></a>
1.13.1, redux</li>
<li><a
href="https://github.com/rust-lang/regex/commit/40e98238fff903f3e1ec95bbdb487185dd60504a"><code>40e9823</code></a>
1.13.1</li>
<li><a
href="https://github.com/rust-lang/regex/commit/75fcb962d6ea1c456f6f023c9537a66389413a85"><code>75fcb96</code></a>
changelog: 1.13.1</li>
<li><a
href="https://github.com/rust-lang/regex/commit/64ad0b618e043b791ed5385dd5504a436da1ddae"><code>64ad0b6</code></a>
automata: fix bug in reverse suffix/inner optimization</li>
<li><a
href="https://github.com/rust-lang/regex/commit/fa91c31a4291c9dda6afe19829e6fe2e3bbc2da5"><code>fa91c31</code></a>
automata: fix a bug caught by Codex review</li>
<li><a
href="https://github.com/rust-lang/regex/commit/30390ec3e8889aad830337cdf3a7a01ae195ae73"><code>30390ec</code></a>
automata: formatting tweaks</li>
<li><a
href="https://github.com/rust-lang/regex/commit/821a8eb1ad7860ddc788fe36f495036df63cfc35"><code>821a8eb</code></a>
automata: refactor reverse suffix/inner search slightly</li>
<li><a
href="https://github.com/rust-lang/regex/commit/10afd704d88d00ddfcd10218883a81b3ae5e4831"><code>10afd70</code></a>
automata: expose the extracted literals for inner literal
extraction</li>
<li><a
href="https://github.com/rust-lang/regex/commit/8c34f41d3c5a0e16ce17dfb964587cb48625a8d5"><code>8c34f41</code></a>
automata: avoid reverse suffix optimization for non-leftmost-first</li>
<li><a
href="https://github.com/rust-lang/regex/commit/5524f02430d2d118d5c34fde54136d08376de711"><code>5524f02</code></a>
test: add regression tests for failed reverse suffix/inner
optimizations</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-lang/regex/compare/1.13.0...1.13.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `tokio` from 1.52.3 to 1.53.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tokio-rs/tokio/releases">tokio's
releases</a>.</em></p>
<blockquote>
<h2>Tokio v1.53.1</h2>
<h1>1.53.1 (July 20th, 2026)</h1>
<h3>Fixed</h3>
<ul>
<li>signal: restore MSRV by removing <code>OnceLock::wait</code> from
the Windows handler (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8300">#8300</a>)</li>
</ul>
<h3>Fixed (unstable)</h3>
<ul>
<li>time: fix alt timer cancellation and insertion race (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8252">#8252</a>)</li>
</ul>
<h3>Documented</h3>
<ul>
<li>runtime: remove dead link definition in Runtime::block_on (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8301">#8301</a>)</li>
</ul>
<p><a
href="https://redirect.github.com/tokio-rs/tokio/issues/8252">#8252</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/8252">tokio-rs/tokio#8252</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8300">#8300</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/8300">tokio-rs/tokio#8300</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8301">#8301</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/8301">tokio-rs/tokio#8301</a></p>
<h2>Tokio v1.53.0</h2>
<h1>1.53.0 (July 17th, 2026)</h1>
<h3>Added</h3>
<ul>
<li>fs: implement <code>From&lt;OwnedFd&gt;</code> and
<code>From&lt;OwnedHandle&gt;</code> for <code>File</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8266">#8266</a>)</li>
<li>metrics: add task schedule latency metric (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7986">#7986</a>)</li>
<li>net: add <code>SocketAddr</code> methods to Unix sockets (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8144">#8144</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>io: add <code>#[inline]</code> to IO trait impls for in-memory types
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8242">#8242</a>)</li>
<li>net: implement UCred::pid on FreeBSD (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8086">#8086</a>)</li>
<li>net: support Nuttx target os (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8259">#8259</a>)</li>
<li>signal: refactor global variables on Windows (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8231">#8231</a>)</li>
<li>sync: <code>mpsc::{Receiver,UnboundedReceiver}</code> now drops
waker on drop, even if there are still senders (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8095">#8095</a>)</li>
<li>taskdump: support taskdumps on s390x (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8192">#8192</a>)</li>
<li>time: add <code>#[track_caller]</code> to <code>timeout_at()</code>
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8077">#8077</a>)</li>
<li>time: consolidate mutex locks on spurious poll (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8124">#8124</a>)</li>
<li>time: defer waker clone on spurious poll (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8107">#8107</a>)</li>
<li>time: move lazy-registration state into <code>Sleep</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8132">#8132</a>)</li>
<li>tracing: remove unnecessary span clone (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8126">#8126</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>io: do not treat zero-length reads as EOF in <code>Chain</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8251">#8251</a>)</li>
<li>net: use getpeereid for QNX peer credentials (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8270">#8270</a>)</li>
<li>runtime: avoid illegal state in <code>FastRand</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8078">#8078</a>)</li>
<li>sync: wake mpsc receiver when a queued <code>reserve[_many]</code>
returns permits (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8260">#8260</a>)</li>
<li>taskdump: skip double wake on
<code>Trace::capture</code>/<code>Trace::trace_with</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8043">#8043</a>)</li>
<li>time: avoid stack overflow in runtime constructor (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8093">#8093</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/tokio-rs/tokio/commit/75fef53d0a8590c2d1dbb63672aa7b7d1ef51155"><code>75fef53</code></a>
chore: prepare Tokio v1.53.1 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8303">#8303</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/ae9d01121377cdbef32b9d5e8559843cce9f927e"><code>ae9d011</code></a>
signal: restore MSRV by removing OnceLock::wait from the Windows handler
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8300">#8300</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/eb4988dc2ecb85d2617971fbbabc84938c141bfd"><code>eb4988d</code></a>
time: fix the loom test of the race between cancellation/insertion (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8302">#8302</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/91d3b4c0bccf2234fc3ed19e605e2cd402f19437"><code>91d3b4c</code></a>
time: fix alt timer cancellation and insertion race (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8252">#8252</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/a46338401b9e0ffc9bd68c31100ee99cee717481"><code>a463384</code></a>
runtime: remove dead link definition in <code>Runtime::block_on</code>
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8301">#8301</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/be689a35f5ade5a39e507f79d3ec85cdab27806f"><code>be689a3</code></a>
chore: prepare Tokio v1.53.0 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8294">#8294</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/50f76c71ec7203013f7f0cda59deaa9016e93939"><code>50f76c7</code></a>
chore: prepare tokio-macros v2.7.1 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8295">#8295</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/f61fccad3cd598cce743fc511a983364b77af92a"><code>f61fcca</code></a>
Merge 'tokio-1.52.4' into 'master' (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8290">#8290</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/efdba5fcf02c4b93d379114df136b994c3b21445"><code>efdba5f</code></a>
chore: prepare Tokio v1.52.4 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8289">#8289</a>)</li>
<li><a
href="https://github.com/tokio-rs/tokio/commit/b0ba02e75507518baed6718b0c37105e430f3a93"><code>b0ba02e</code></a>
Merge 'tokio-1.51.4' into 'tokio-1.52.x' (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/8288">#8288</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-1.52.3...tokio-1.53.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `serde` from 1.0.228 to 1.0.229
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/serde/releases">serde's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.229</h2>
<ul>
<li>Update to syn 3</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/serde-rs/serde/commit/7fc3b4c30c94f73a96ebd1553f2b090d928fc3a8"><code>7fc3b4c</code></a>
Release 1.0.229</li>
<li><a
href="https://github.com/serde-rs/serde/commit/6d6e9a11101354ce769a3438a088b6b9305c1863"><code>6d6e9a1</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/3085">#3085</a>
from dtolnay/syn3</li>
<li><a
href="https://github.com/serde-rs/serde/commit/6dec3b751126c8338cac0fe8085612d695e4ecf3"><code>6dec3b7</code></a>
Update to syn 3</li>
<li><a
href="https://github.com/serde-rs/serde/commit/cfe669241065984177ff63af8b45058e6e9b499d"><code>cfe6692</code></a>
Resolve mut_mut pedantic clippy lint</li>
<li><a
href="https://github.com/serde-rs/serde/commit/1023d077510b4aef36a41ef56fdb7798568a2654"><code>1023d07</code></a>
Update actions/upload-artifact@v6 -&gt; v7</li>
<li><a
href="https://github.com/serde-rs/serde/commit/dd682c2c86aa7629e77c1ccd93212d3729f4c66d"><code>dd682c2</code></a>
Update actions/checkout@v6 -&gt; v7</li>
<li><a
href="https://github.com/serde-rs/serde/commit/5f0f18b9211732f2d82f73b5a43e4f5ff3701251"><code>5f0f18b</code></a>
Update ui test suite to nightly-2026-06-01</li>
<li><a
href="https://github.com/serde-rs/serde/commit/63a1498f0e7be991ffac5939bdd202ca16e9a23f"><code>63a1498</code></a>
Regenerate stderr with trybuild normalization fixes</li>
<li><a
href="https://github.com/serde-rs/serde/commit/fa7da4a93567ed347ad0735c28e439fca688ef26"><code>fa7da4a</code></a>
Fix unused_features warning</li>
<li><a
href="https://github.com/serde-rs/serde/commit/6b1a17851ea3d86a56aa116ca1cbf428f8d5f22d"><code>6b1a178</code></a>
Unpin CI miri toolchain</li>
<li>Additional commits viewable in <a
href="https://github.com/serde-rs/serde/compare/v1.0.228...v1.0.229">compare
view</a></li>
</ul>
</details>
<br />

Updates `serde_json` from 1.0.150 to 1.0.151
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/json/releases">serde_json's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.151</h2>
<ul>
<li>Add RawValue::from_string_unchecked (<a
href="https://redirect.github.com/serde-rs/json/issues/1331">#1331</a>,
thanks <a
href="https://github.com/WonderLawrence"><code>@​WonderLawrence</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/serde-rs/json/commit/de8500740cdcabffb9734f503e4889def823cf10"><code>de85007</code></a>
Release 1.0.151</li>
<li><a
href="https://github.com/serde-rs/json/commit/3b2b3c5f28c20ed988bd081a4147c535e7e65c74"><code>3b2b3c5</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/json/issues/1331">#1331</a>
from WonderLawrence/rawvalue-from-string-unchecked</li>
<li><a
href="https://github.com/serde-rs/json/commit/0406d96860e9d8b9252e2002fa3e626ae48ca1b0"><code>0406d96</code></a>
Debug-assert well-formedness and no-whitespace in
from_string_unchecked</li>
<li><a
href="https://github.com/serde-rs/json/commit/cf16f75d81e28c723323bfc60a68fc02d2994fff"><code>cf16f75</code></a>
Add RawValue::from_string_unchecked</li>
<li><a
href="https://github.com/serde-rs/json/commit/827a315bf2198558f0325b07bcc1e2cd973aba2f"><code>827a315</code></a>
Update actions/upload-artifact@v6 -&gt; v7</li>
<li><a
href="https://github.com/serde-rs/json/commit/cea36a5c017ebffdeb95d0cd0f1aad473bfab758"><code>cea36a5</code></a>
Update actions/checkout@v6 -&gt; v7</li>
<li>See full diff in <a
href="https://github.com/serde-rs/json/compare/v1.0.150...v1.0.151">compare
view</a></li>
</ul>
</details>
<br />

Updates `uuid` from 1.23.5 to 1.24.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/uuid-rs/uuid/releases">uuid's
releases</a>.</em></p>
<blockquote>
<h2>v1.24.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(fmt): support encoding into MaybeUninit buffers by <a
href="https://github.com/weifanglab"><code>@​weifanglab</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/892">uuid-rs/uuid#892</a></li>
<li>Prepare for 1.24.0 release by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/896">uuid-rs/uuid#896</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/weifanglab"><code>@​weifanglab</code></a> made
their first contribution in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/892">uuid-rs/uuid#892</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/uuid-rs/uuid/compare/v1.23.5...v1.24.0">https://github.com/uuid-rs/uuid/compare/v1.23.5...v1.24.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/uuid-rs/uuid/commit/6a8aeab3d02838f6fef71e69cdfda963e8c4158b"><code>6a8aeab</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/896">#896</a> from
uuid-rs/cargo/v1.24.0</li>
<li><a
href="https://github.com/uuid-rs/uuid/commit/e6db8ec0879fc9e703efc1911512c111f86e540d"><code>e6db8ec</code></a>
prepare for 1.24.0 release</li>
<li><a
href="https://github.com/uuid-rs/uuid/commit/606f2365c706ccd0309d3263b381f5378b004e4d"><code>606f236</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/892">#892</a> from
weifanglab/main</li>
<li><a
href="https://github.com/uuid-rs/uuid/commit/ab848dbdf652c91af3ed5a413d3edd74bc2ebcfb"><code>ab848db</code></a>
feat(fmt): support encoding into MaybeUninit buffers</li>
<li><a
href="https://github.com/uuid-rs/uuid/commit/6fa1a1e38afa7536bad4cd0febf689338f65c220"><code>6fa1a1e</code></a>
feat(fmt): support encoding into MaybeUninit buffers</li>
<li>See full diff in <a
href="https://github.com/uuid-rs/uuid/compare/v1.23.5...v1.24.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `anyhow` from 1.0.103 to 1.0.104
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dtolnay/anyhow/releases">anyhow's
releases</a>.</em></p>
<blockquote>
<h2>1.0.104</h2>
<ul>
<li>Update <code>syn</code> dev-dependency to version 3</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/dtolnay/anyhow/commit/1dbe1862aae650423e3361fbd20b7d17c5109cc3"><code>1dbe186</code></a>
Release 1.0.104</li>
<li><a
href="https://github.com/dtolnay/anyhow/commit/f6479f8e5e10761d7fecde0970cff363dc644d92"><code>f6479f8</code></a>
Update to syn 3</li>
<li>See full diff in <a
href="https://github.com/dtolnay/anyhow/compare/1.0.103...1.0.104">compare
view</a></li>
</ul>
</details>
<br />

Updates `napi` from 3.10.5 to 3.11.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/napi-rs/napi-rs/releases">napi's
releases</a>.</em></p>
<blockquote>
<h2>napi-v3.11.0</h2>
<h3>Added</h3>
<ul>
<li>unforgeable <code>#[napi]</code> class identity via Node object type
tags (<a
href="https://redirect.github.com/napi-rs/napi-rs/pull/3405">#3405</a>)</li>
<li><em>(napi)</em> add pluggable async runtime backend (<a
href="https://redirect.github.com/napi-rs/napi-rs/pull/3352">#3352</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><em>(napi)</em> release JsDeferred tsfn on null-env teardown drain
(<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3404">#3404</a>
follow-up) (<a
href="https://redirect.github.com/napi-rs/napi-rs/pull/3408">#3408</a>)</li>
<li><em>(napi)</em> guard JsDeferred against env teardown (<a
href="https://redirect.github.com/napi-rs/napi-rs/pull/3404">#3404</a>)</li>
<li><em>(napi)</em> register the async runtime env cleanup hook per
registration (<a
href="https://redirect.github.com/napi-rs/napi-rs/pull/3400">#3400</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li><em>(napi)</em> share tracing callsite (<a
href="https://redirect.github.com/napi-rs/napi-rs/pull/3409">#3409</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/679eb79f5cf3c7c6b2850f4ab46092126f23dc5c"><code>679eb79</code></a>
chore: release (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3401">#3401</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/762a0e389a0196d7446666ee5ef8468994dcac4f"><code>762a0e3</code></a>
chore(release): publish</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/587ae146a0172f7e8c0d8a22f7126fe51b21b4f2"><code>587ae14</code></a>
perf(napi): share tracing callsite (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3409">#3409</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/02d8ccdbc1eceed9cc3c7e61af61c34b97ff6af2"><code>02d8ccd</code></a>
fix(napi): release JsDeferred tsfn on null-env teardown drain (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3404">#3404</a>
follow-u...</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/b63520443469b6a217dbc32a78c5b6524d4b932c"><code>b635204</code></a>
fix(napi): guard JsDeferred against env teardown (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3404">#3404</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/729ebed8f432aadbc1ec400744a8fca01e7cd262"><code>729ebed</code></a>
feat: unforgeable <code>#[napi]</code> class identity via Node object
type tags (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3405">#3405</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/0a4681d3ffa0348ae4524c1c92f4f2fbe631eecd"><code>0a4681d</code></a>
fix(cli): don't force-build crates whose optional napi-derive dependency
is d...</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/392ec4026623bca357c5c2131ca12fd1ac5ebed0"><code>392ec40</code></a>
chore(deps): update dependency c8 to v12 (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3403">#3403</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/d618d7e8cd74ed1082b270c60caaabda354f6f95"><code>d618d7e</code></a>
feat(napi): add pluggable async runtime backend (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3352">#3352</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/1817ed2371c34efacefdf810b54faf517ebde69b"><code>1817ed2</code></a>
fix(napi): register the async runtime env cleanup hook per registration
(<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3400">#3400</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/napi-rs/napi-rs/compare/napi-v3.10.5...napi-v3.11.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `napi-derive` from 3.5.10 to 3.6.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/napi-rs/napi-rs/releases">napi-derive's
releases</a>.</em></p>
<blockquote>
<h2>napi-derive-v3.6.0</h2>
<h3>Added</h3>
<ul>
<li>unforgeable <code>#[napi]</code> class identity via Node object type
tags (<a
href="https://redirect.github.com/napi-rs/napi-rs/pull/3405">#3405</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/679eb79f5cf3c7c6b2850f4ab46092126f23dc5c"><code>679eb79</code></a>
chore: release (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3401">#3401</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/762a0e389a0196d7446666ee5ef8468994dcac4f"><code>762a0e3</code></a>
chore(release): publish</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/587ae146a0172f7e8c0d8a22f7126fe51b21b4f2"><code>587ae14</code></a>
perf(napi): share tracing callsite (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3409">#3409</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/02d8ccdbc1eceed9cc3c7e61af61c34b97ff6af2"><code>02d8ccd</code></a>
fix(napi): release JsDeferred tsfn on null-env teardown drain (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3404">#3404</a>
follow-u...</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/b63520443469b6a217dbc32a78c5b6524d4b932c"><code>b635204</code></a>
fix(napi): guard JsDeferred against env teardown (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3404">#3404</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/729ebed8f432aadbc1ec400744a8fca01e7cd262"><code>729ebed</code></a>
feat: unforgeable <code>#[napi]</code> class identity via Node object
type tags (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3405">#3405</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/0a4681d3ffa0348ae4524c1c92f4f2fbe631eecd"><code>0a4681d</code></a>
fix(cli): don't force-build crates whose optional napi-derive dependency
is d...</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/392ec4026623bca357c5c2131ca12fd1ac5ebed0"><code>392ec40</code></a>
chore(deps): update dependency c8 to v12 (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3403">#3403</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/d618d7e8cd74ed1082b270c60caaabda354f6f95"><code>d618d7e</code></a>
feat(napi): add pluggable async runtime backend (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3352">#3352</a>)</li>
<li><a
href="https://github.com/napi-rs/napi-rs/commit/1817ed2371c34efacefdf810b54faf517ebde69b"><code>1817ed2</code></a>
fix(napi): register the async runtime env cleanup hook per registration
(<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3400">#3400</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/napi-rs/napi-rs/compare/napi-derive-v3.5.10...napi-derive-v3.6.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `libc` from 0.2.186 to 0.2.189
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/libc/releases">libc's
releases</a>.</em></p>
<blockquote>
<h2>0.2.189</h2>
<h3>Added</h3>
<ul>
<li>Emscripten: Add <code>pthread_sigmask</code>, <code>sigwait</code>,
<code>sigwaitinfo</code>, <code>sigtimedwait</code>,
<code>faccessat</code>, and <code>pthread_kill</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/5270">#5270</a>)</li>
<li>Linux SPARC: Enable the <code>clone3</code> syscall (<a
href="https://redirect.github.com/rust-lang/libc/pull/4980">#4980</a>)</li>
<li>Solarish: Add <code>CLOCK_PROCESS_CPUTIME_ID</code> and
<code>CLOCK_THREAD_CPUTIME_ID</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/5274">#5274</a>)</li>
</ul>
<h3>Deprecated</h3>
<ul>
<li>Deprecate <code>CLONE_INTO_CGROUP</code> and
<code>CLONE_CLEAR_SIGHAND</code>. These overflow their types and will be
changed to a larger size in the future. (<a
href="https://github.com/rust-lang/libc/commit/8c6e6710458db4d6aa0766f6f84bbf13f640237e">8c6e6710458d</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Musl riscv32: Rename padding fields to avoid a conflict and fix the
build (<a
href="https://github.com/rust-lang/libc/commit/2499ff0ad9936a036e78a4e0991445efee383564">2499ff0ad993</a>)</li>
<li>NuttX: Fix <code>wchar_t</code> definition under Arm (<a
href="https://redirect.github.com/rust-lang/libc/pull/5245">#5245</a>)</li>
<li>Windows: Add back link names for <code>time</code>-related symbols
(<a
href="https://redirect.github.com/rust-lang/libc/pull/5300">#5300</a>)</li>
</ul>
<h2>0.2.188</h2>
<h3>Changed</h3>
<ul>
<li>Restore <code>Send</code> and <code>Sync</code> for <code>DIR</code>
(<a
href="https://github.com/rust-lang/libc/commit/35b062263401733cd89065c6a553640f2ba51ff1">35b062263401</a>)</li>
</ul>
<p>These were removed in 0.2.187 because <code>libc</code> does not
actually make <code>Send</code> and <code>Sync</code>
guarantees about <code>DIR</code> (or other extern types), but this
caused some crates to break.
The traits are added back for now to allow time to migrate, but will be
removed again
in the future; please make sure your crates are not relying on
<code>libc::DIR: Send</code> or
<code>libc::DIR: Sync</code>.</p>
<h2>0.2.187</h2>
<p>This release contains a number of improvements related to 64-bit
<code>time_t</code> configuration.
Of note the existing <code>RUST_LIBC_UNSTABLE_*</code> environment
variables have been replaced
with configuration options. The new way to use these is:</p>
<pre lang="sh"><code>RUSTFLAGS='--cfg=libc_unstable_musl_v1_2_3' cargo
...
RUSTFLAGS='--cfg=libc_unstable_gnu_time_bits=&quot;64&quot;' cargo ...
</code></pre>
<p>Being able to set this via <code>RUSTFLAGS</code> makes it easier to
only apply configuration to
specific targets (and notably, not the host if build scripts are
used).</p>
<p>There are two other notable changes:</p>
<ul>
<li>
<p>The 32-bit <code>windows-gnu</code> targets now respect
<code>libc_unstable_gnu_time_bits</code></p>
</li>
<li>
<p>uClibc now supports a similar configuration option:</p>
<pre lang="sh"><code>RUSTFLAGS='--cfg=libc_unstable_uclibc_time64'
</code></pre>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rust-lang/libc/blob/0.2.189/CHANGELOG.md">libc's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/rust-lang/libc/compare/0.2.188...0.2.189">0.2.189</a>
- 2026-07-21</h2>
<h3>Added</h3>
<ul>
<li>Emscripten: Add <code>pthread_sigmask</code>, <code>sigwait</code>,
<code>sigwaitinfo</code>, <code>sigtimedwait</code>,
<code>faccessat</code>, and <code>pthread_kill</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/5270">#5270</a>)</li>
<li>Linux SPARC: Enable the <code>clone3</code> syscall (<a
href="https://redirect.github.com/rust-lang/libc/pull/4980">#4980</a>)</li>
<li>Solarish: Add <code>CLOCK_PROCESS_CPUTIME_ID</code> and
<code>CLOCK_THREAD_CPUTIME_ID</code> (<a
href="https://redirect.github.com/rust-lang/libc/pull/5274">#5274</a>)</li>
</ul>
<h3>Deprecated</h3>
<ul>
<li>Deprecate <code>CLONE_INTO_CGROUP</code> and
<code>CLONE_CLEAR_SIGHAND</code>. These overflow their types and will be
changed to a larger size in the future. (<a
href="https://github.com/rust-lang/libc/commit/8c6e6710458db4d6aa0766f6f84bbf13f640237e">8c6e6710458d</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Musl riscv32: Rename padding fields to avoid a conflict and fix the
build (<a
href="https://github.com/rust-lang/libc/commit/2499ff0ad9936a036e78a4e0991445efee383564">2499ff0ad993</a>)</li>
<li>NuttX: Fix <code>wchar_t</code> definition under Arm (<a
href="https://redirect.github.com/rust-lang/libc/pull/5245">#5245</a>)</li>
<li>Windows: Add back link names for <code>time</code>-related symbols
(<a
href="https://redirect.github.com/rust-lang/libc/pull/5300">#5300</a>)</li>
</ul>
<h2><a
href="https://github.com/rust-lang/libc/compare/0.2.187...0.2.188">0.2.188</a>
- 2026-07-21</h2>
<h3>Changed</h3>
<ul>
<li>Restore <code>Send</code> and <code>Sync</code> for <code>DIR</code>
(<a
href="https://github.com/rust-lang/libc/commit/35b062263401733cd89065c6a553640f2ba51ff1">35b062263401</a>)</li>
</ul>
<p>These were removed in 0.2.187 because <code>libc</code> does not
actually make <code>Send</code> and <code>Sync</code>
guarantees about <code>DIR</code> (or other extern types), but this
caused some crates to break.
The traits are added back for now to allow time to migrate, but will be
removed again
in the future; please make sure your crates are not relying on
<code>libc::DIR: Send</code> or
<code>libc::DIR: Sync</code>.</p>
<h2><a
href="https://github.com/rust-lang/libc/compare/0.2.186...0.2.187">0.2.187</a>
- 2026-07-20</h2>
<p>This release contains a number of improvements related to 64-bit
<code>time_t</code> configuration.
Of note the existing <code>RUST_LIBC_UNSTABLE_*</code> environment
variables have been replaced
with configuration options. The new way to use these is:</p>
<pre lang="sh"><code>RUSTFLAGS='--cfg=libc_unstable_musl_v1_2_3' cargo
...
RUSTFLAGS='--cfg=libc_unstable_gnu_time_bits=&quot;64&quot;' cargo ...
</code></pre>
<p>Being able to set this via <code>RUSTFLAGS</code> makes it easier to
only apply configuration to
specific targets (and notably, not the host if build scripts are
used).</p>
<p>There are two other notable changes:</p>
<ul>
<li>The 32-bit <code>windows-gnu</code> targets now respect
<code>libc_unstable_gnu_time_bits</code></li>
<li>uClibc now supports a similar configuration option:</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/rust-lang/libc/commit/ef0906e20828777175f65caa7e681a0ce33c559a"><code>ef0906e</code></a>
libc: Release 0.2.189</li>
<li><a
href="https://github.com/rust-lang/libc/commit/5a79f7642911e17cf9629857b88503e50d433fc4"><code>5a79f76</code></a>
riscv32-musl: Rename padding fields to avoid a conflict</li>
<li><a
href="https://github.com/rust-lang/libc/commit/3e51062f4249054264ae11363d8efbb652f9ab2e"><code>3e51062</code></a>
psp: Fix <code>overflowing_literals</code> warnings</li>
<li><a
href="https://github.com/rust-lang/libc/commit/e352fdd17b43c5e2a911041512c4d62953121018"><code>e352fdd</code></a>
emscripten: add pthread_sigmask, sigwait, sigwaitinfo, sigtimedwait,
faccessa...</li>
<li><a
href="https://github.com/rust-lang/libc/commit/63221b314d46bccaea33bdba2f3d75f25dc9c739"><code>63221b3</code></a>
macros: Require <code>safe</code> in <code>safe_f!</code>
invocations</li>
<li><a
href="https://github.com/rust-lang/libc/commit/707ab528fc31619d80ca8ee5fd714ff7285e818e"><code>707ab52</code></a>
macros: Require <code>unsafe</code> in <code>f!</code> invocations</li>
<li><a
href="https://github.com/rust-lang/libc/commit/8e40c9404b8127d5dd3d6f015c1da1f12b7dd44b"><code>8e40c94</code></a>
Enable clone3() syscall on sparc-linux and sparc64-linux</li>
<li><a
href="https://github.com/rust-lang/libc/commit/8427909fb3c9890bd89c787e8ab18673032b0360"><code>8427909</code></a>
windows: Add back link names for <code>time</code>-related symbols</li>
<li><a
href="https://github.com/rust-lang/libc/commit/b4863fa4c31a95524339a6ee89aa5df042a33745"><code>b4863fa</code></a>
nuttx: fix wchar_t definition under arm</li>
<li><a
href="https://github.com/rust-lang/libc/commit/41c683da26d2c74a69205ca1e5e87a415aa313c8"><code>41c683d</code></a>
nuttx: mirror type definitions</li>
<li>Additional commits viewable in <a
href="https://github.com/rust-lang/libc/compare/0.2.186...0.2.189">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 09:21:16 -07:00
LanceDB Robot 2ce88f8e02 chore: update lance dependency to v9.1.0-beta.8 (#3702)
Updates Rust workspace Lance dependencies and Java lance-core to
v9.1.0-beta.8. Removes MemWAL writer settings that are no longer exposed
by Lance.

Lance tag:
https://github.com/lance-format/lance/releases/tag/v9.1.0-beta.8
2026-07-21 23:37:32 -05:00
kid ac99e4dce5 fix(node): sanitize Map fields across Arrow versions (#3650)
## Summary

- reconstruct foreign Arrow Map schemas from their single sanitized
entries field
- reject malformed Map types with anything other than one child
- preserve the complete Map schema and `keysSorted` value through
empty-table creation and IPC round trips across Arrow 15–18

## Testing

- `./node_modules/.bin/jest --runInBand __test__/arrow.test.ts
__test__/sanitize.test.ts`
- `pnpm lint`
- `pnpm build`
- `pnpm run docs`

Fixes #2337
2026-07-21 09:28:57 -07:00
Expyron 82231bf66d chore: replace lazy_static with LazyLock (#3679) 2026-07-21 09:28:37 -07:00
Mateusz Szewczyk 8d2fea9151 chore(python): refactor legacy code in WatsonxEmbeddings component (#3660)
## What

- Replace legacy model names in `WatsonxEmbeddings` with the current
supported set:
  - `ibm/granite-embedding-278m-multilingual` (new default, 768-dim)
  - `ibm/slate-125m-english-rtrvr-v2` (768-dim)
  - `ibm/slate-30m-english-rtrvr-v2` (384-dim)
  - `intfloat/multilingual-e5-large` (1024-dim)
  - `sentence-transformers/all-minilm-l6-v2` (384-dim)
- Add `space_id` field — mutually exclusive with `project_id`, mirrors
the
  existing pattern in `WatsonxReranker`
- `project_id` / `space_id` resolution now falls back to
`WATSONX_PROJECT_ID` /
  `WATSONX_SPACE_ID` env vars; exactly one must be supplied

## Why

The previously hardcoded models (`ibm/slate-125m-english-rtrvr`,
`sentence-transformers/all-minilm-l12-v2`) are legacy and no longer
listed as
supported by the watsonx.ai platform. `space_id` scoping was already
supported
by `WatsonxReranker` but was missing from the embeddings counterpart.

---------

Co-authored-by: Will Jones <willjones127@gmail.com>
2026-07-21 09:28:06 -07:00
Xuanwo 2f27aa377b chore: update lance dependency to v9.1.0-beta.7 (#3698)
Updates the Rust workspace Lance dependencies and Java lance-core from
v9.1.0-beta.5 to v9.1.0-beta.7, including the generated Cargo lockfile.
No LanceDB compatibility changes were required for this release. See the
[Lance v9.1.0-beta.7
release](https://github.com/lance-format/lance/releases/tag/v9.1.0-beta.7).
2026-07-21 14:57:07 +08:00
LanceDB Robot 1bf6b3ea7e chore: update lance dependency to v9.1.0-beta.5 (#3696)
Updates the Rust workspace Lance dependencies and Java lance-core from
v9.1.0-beta.4 to v9.1.0-beta.5.

No compatibility fixes were required. Triggering tag:
https://github.com/lance-format/lance/releases/tag/v9.1.0-beta.5

---------

Co-authored-by: Lu Qiu <luqiujob@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 19:40:49 -07:00
Xuanwo 8450683b2a chore: update lance dependency to v9.1.0-beta.4 (#3690) 2026-07-20 22:22:07 +08:00
Drew Gallardo 65cd142c7e feat: add remote branch diff and merge client APIs (#3686)
This PR adds some support for `diff` / `merge` in the remote client as
for local tables we stay `NotSupported` until
https://github.com/lance-format/lance/issues/7263.


This wires the two review-and-land calls against the remote REST API:
- `POST /v1/table/{id}/branches/diff`
- `POST /v1/table/{id}/branches/merge`

Rust gets typed results (`BranchDiff`, `MergeBranchResult`). Python
returns the wire JSON, same shape as the REST response.

Merge here means promoting a branch's added columns onto `main`.

### Behavior
- Remote only. Local raises `NotSupported`.
- A rejected merge is not an exception. HTTP 409 still returns `Ok` / a
dict with `status="rejected"` and blockers in `diff.mergeBlockers`.
- Unknown blocker / status codes parse as `Unknown` so a newer server
does not break older clients.
- `MergePreview` tolerates missing fields for the same reason.
- Merge requests are not retried. 409 is final and carries the body you
need.

### Example
```python

table = db.open_table("images")

table.branches.create("exp")
exp = table.branches.checkout("exp")

exp.add_columns({"tag": "cast('draft' as string)"})

diff = table.branches.diff("exp")
preview = table.branches.merge("exp", dry_run=True)
result = table.branches.merge("exp", dry_run=False)

if result["status"] == "merged":
    print("landed at", result["mainVersionAfter"])
elif result["status"] == "rejected":
    print(result["diff"]["mergeBlockers"])
```

### Testing
cargo test -p lancedb --features remote diff_branch
cargo test -p lancedb --features remote merge_branch

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-18 12:38:05 -07:00
Will Jones 5d0a1ef66c fix(rust): bound remote insert request size to avoid ingestion timeouts (#3630)
## Problem

On the remote (LanceDB Cloud) write path, each write partition is
uploaded as a **single** `/insert?upload_id=...` request that stays open
until the whole partition has been streamed and the server has written
it to object storage. For large bulk ingests a partition can be many GB,
so a single request can run longer than the client read timeout (default
300s), surfacing as:

```
lancedb.remote.errors.HttpError: operation timed out
```

The server already supports staging **multiple** parts under one
`upload_id` (each `/insert` writes a separate transaction that
`complete` merges atomically), but the client never used that — it sent
one part per partition.

## Change

Split each partition into multiple parts of at most
`max_bytes_per_request` (Arrow IPC, LZ4-compressed) bytes, each uploaded
as its own `/insert?upload_id=...&upload_part_id=...` request. This
bounds how long any single request stays open, independent of total data
size or write parallelism.

Key properties:
- **Still streamed, not buffered.** Each part's body is driven through a
bounded channel while the request is in flight (`futures::join!` of a
producer + the send), so peak memory stays at a couple of batches per
partition regardless of the part size. Backpressure from a
slow/throttled server still propagates upstream.
- **Correct part accounting.** An empty partition still sends exactly
one (schema-only) part so `complete` has a transaction to commit; a size
cut landing exactly on the end of input does not emit a trailing empty
part.
- **Multipart only.** The single-request (non-multipart) path is
unchanged.

## Config

New `ClientConfig::max_bytes_per_request: Option<usize>`, also settable
via the `LANCE_CLIENT_MAX_BYTES_PER_REQUEST` environment variable.
**Default 1 GiB** (`Some(0)` disables splitting → one request per
partition). Python users pick up the default/env automatically through
the remote client.

## Tests

- `test_multipart_chunked_splits_into_parts`: a 1-byte budget puts each
batch in its own part → N requests, each carrying the shared `upload_id`
and a distinct `upload_part_id`.
- `test_multipart_single_part_when_under_budget`: a large budget keeps
the partition in a single request.
- Verified end-to-end against a live remote table: a forced-chunked
multipart add (many parts) assembles to the correct row count.

Related to ENT-1883.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 14:37:41 -07:00
Vitaliy 82906ecfee fix(python): raise clear ValueError when vector column cannot be infe… (#3567)
## Summary

Fixes #1653.

`infer_vector_column_name` in `util.py` could silently return `None`
when `query is None` and `query_type` is not `"fts"` or `"hybrid"`. This
`None` then propagated into downstream code, causing a cryptic
`TypeError: expected bytes, NoneType found` rather than a clear error
message.

## Changes

- **Removes the no-op `try/except Exception as e: raise e`** around
`inf_vector_column_query` (it was catching and immediately re-raising
without adding any value)
- - **Adds a `None` guard** after the inference block: if
`vector_column_name` is still `None` at this point, raise a clear
`ValueError` pointing the user to pass `vector_column_name` explicitly
## Before / After

**Before:** cryptic `TypeError: expected bytes, NoneType found` deep in
schema lookup code

**After:**
```
ValueError: No vector column found in the schema. Please specify the vector column name explicitly via the `vector_column_name` parameter.
```

---------

Co-authored-by: Will Jones <willjones127@gmail.com>
2026-07-17 08:58:19 -07:00
Dan Tasse ab3041e01e feat: skill references to work with jobs (incl server connection) (#3683)
Some additions to our lancedb skill to enable agents to use the jobs
methods that we recently added. Eval tests (below, with and without
these additions to the skill) suggest that they're helping, mostly to
find the right method calls. These are a little unusual because they
require REST server connection, they're not yet implemented in the SDKs.

```
┌─────────────────────┬───────────┬────────────┬─────────────┬──────────┬───────────┬──────────┬───────────┐
│        eval         │ grade w/o │ grade with │ improvement │ time w/o │ time with │ cost w/o │ cost with │
├─────────────────────┼───────────┼────────────┼─────────────┼──────────┼───────────┼──────────┼───────────┤
│ 8-list-running-jobs │ 2.5/3     │ 3/3        │ +0.5        │ 123s     │ 29s       │ $0.58    │ $0.18     │
├─────────────────────┼───────────┼────────────┼─────────────┼──────────┼───────────┼──────────┼───────────┤
│ 9-describe-job      │ 1/5       │ 5/5        │ +4.0        │ 159s     │ 52s       │ $0.62    │ $0.25     │
├─────────────────────┼───────────┼────────────┼─────────────┼──────────┼───────────┼──────────┼───────────┤
│ 10-cancel-job       │ 3/3       │ 3/3        │ +0.0        │ 99s      │ 35s       │ $0.55    │ $0.21     │
├─────────────────────┼───────────┼────────────┼─────────────┼──────────┼───────────┼──────────┼───────────┤
│ TOTAL               │ 6.5/11    │ 11/11      │ +4.5        │ 381s     │ 116s      │ $1.75    │ $0.65     │
└─────────────────────┴───────────┴────────────┴─────────────┴──────────┴───────────┴──────────┴───────────┘
```
Failure reasons are because the agent didn't know the right method to
call, spent all its turns guessing REST calls, tried to inspect lancedb
code, but didn't find the answer in here.
2026-07-17 11:03:40 -04:00
Will Jones 7813907eb7 fix(python): bound scanner memory for wide-row bulk ingestion (#3625)
## Problem

`table.add(dataset)` with a `pyarrow.dataset.Dataset` OOMs the client
during bulk ingestion of wide rows (e.g. embedding columns), even
against a remote table where the upload itself is streaming.

The cause is in `to_scannable`: a `Dataset` is scanned with pyarrow's
default scanner settings (`batch_size=131072` rows,
`batch_readahead=16`, `fragment_readahead=4`). pyarrow's internal
threads prefetch that read-ahead window independently of LanceDB's
backpressure, so for wide rows a large fraction of the dataset is held
in memory. On the remote path this is then multiplied across the
multipart write partitions (one in-flight batch per partition, up to
CPU-core count).

Reproduced on a 10 GB / 1.55M-row dataset with two 768-dim float32
embeddings: peak client RSS ~11.7 GB for the scan alone (6.8 GB after
consuming a *single* batch), ~15.4 GB for the full remote `add()`.

## Fix

`to_scannable` now sizes the scanner from an estimate of bytes-per-row
derived from the schema:

- **Narrow datasets keep pyarrow's defaults** (empty scanner kwargs) —
no throughput regression. The bound only engages above ~410 bytes/row.
- **Wide rows** get a smaller `batch_size` (~16 MiB/batch) and reduced
read-ahead (`batch_readahead=2`, `fragment_readahead=1`) so peak
in-flight memory stays near a ~1 GiB budget. Read-ahead (not just batch
size) has to drop, because pyarrow pins whole row-group buffers.

On the 10 GB dataset this drops peak client RSS to ~1.4 GB, and it stays
flat as the dataset grows. The `Dataset`/`LanceDataset` scannables
remain rescannable (retry-safe).

## Also: expose `write_parallelism` on `add()`

`AddDataBuilder::write_parallelism` already existed in Rust but was not
exposed in Python. This PR forwards it through the async, sync, and
remote `add()` methods, so users can cap the number of parallel write
partitions (each buffers data in flight) to trade throughput for memory
on large uploads.

## Tests

- `test_scannable.py`: bytes-per-row estimation; narrow → defaults; wide
→ bounded; `Dataset` reader streams bounded batches and stays
rescannable.
- `test_table.py`: `write_parallelism` on sync and async `add()`, and
that `write_parallelism=0` is rejected.

Fixes ENT-1883

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 07:41:22 -07:00
Dan Tasse f05140f21c refactor: move lancedb skill to a codex/claude plugin (#3681)
Moves the skill from .agents/skills/lancedb to
plugins/lancedb/skills/lancedb, as recommended by codex and claude.

Install path now is:
### Codex/ChatGPT app
Codex: Plugins -> "Create" arrow -> Add plugin marketplace
search for lancedb plugin and install
### Codex CLI
```
codex plugin marketplace add lancedb/lancedb
codex plugin install lancedb@lancedb # name@marketplace
```
### Claude app
Settings -> Plugins -> Add -> Add marketplace
search for lancedb plugin and install
### Claude CLI
```
claude plugin marketplace add lancedb/lancedb
claude plugin install lancedb@lancedb
```
Here's how it looks on ChatGPT/Codex app:
(the main icon has light and dark modes; the smaller one on the skill
doesn't so I made it gray 🤷 )
<img width="764" height="560" alt="Screenshot 2026-07-16 at 2 49 24 PM"
src="https://github.com/user-attachments/assets/b82cda16-3392-4740-ac47-b2f187cb2655"
/>
2026-07-16 15:51:15 -04:00
Kobi Hikri dfce767f4c ci: pin ad-m/github-push-action to a full commit SHA in the release job (#3677)
Hi, and thank you for LanceDB.

Small CI supply-chain hardening. In `make-release-commit.yml`, the
release job checks out with `LANCEDB_RELEASE_TOKEN` (a push-capable PAT)
and its final step pushes the version tag using a third-party action
pinned to a **mutable branch**:

```yaml
- name: Push new version tag
  uses: ad-m/github-push-action@master
  with:
    github_token: ${{ secrets.LANCEDB_RELEASE_TOKEN }}
```

`@master` can move after review; whatever it points at then runs with
that release token in scope. This PR pins it to the commit behind the
current release (`v1.3.0` → `881a6320…`), keeping the version visible as
a comment. Behavior today is unchanged.

For transparency: I used AI assistance to spot and draft this; I
verified the workflow and resolved the SHA myself.
2026-07-16 12:16:10 -07:00
Tobias 7b6ee0d655 feat(wheels): publish lancedb-compat for pre-Haswell x86_64 hosts (#3327)
Tracks #3324. On x86_64 CPUs without AVX2 (Sandy Bridge / Ivy Bridge /
Westmere on Intel; Bulldozer / Piledriver / Steamroller on AMD), `import
lancedb` SIGILLs because the wheel bakes AVX2 + FMA into every compiled
function. Per [westonpace's
review](https://github.com/lancedb/lancedb/issues/3324#issuecomment-4328944354),
the default `lancedb` wheel stays fast; pre-Haswell users get a
separately-published `lancedb-compat` wheel.

## Summary

- Adds a `lancedb-compat` matrix entry to `pypi-publish.yml` that builds
with `RUSTFLAGS="-C target-cpu=x86-64-v2"` (Nehalem-class baseline).
Same Python API (`import lancedb` works) — files install to the same
namespace, so the two wheels conflict at install time and users pick
one. Same pattern as `psycopg2` / `psycopg2-binary` and `tensorflow` /
`tensorflow-cpu`.
- Generalizes `build_linux_wheel` and `upload_wheel` composites with
optional `package-name` and `rustflags` inputs (defaults preserve the
existing 4 `lancedb` matrix entries verbatim).
- Documents the choice in `python/README.md`: `pip install
lancedb-compat` for pre-Haswell hosts.

The default `.cargo/config.toml` baseline is unchanged.

## Sequencing

1. ~~lance-format/lance#6630 merges → runtime SIMD dispatch lands in
lance.~~ **Done — merged.**
2. lancedb's lance dep is bumped to a release that includes it (separate
PR / normal cadence).
3. This PR's `lancedb-compat` wheel build path starts producing a wheel
that runs on pre-Haswell hardware. **Maintainer setup**: register
`lancedb-compat` on PyPI and configure trusted publishing.

## Verified end-to-end on Sandy Bridge Xeon E5-2609

Verification was done locally against a fork-pinned lance dep that
includes the runtime dispatch implementation, using the same
`RUSTFLAGS="-C target-cpu=x86-64-v2"` flags this PR uses in CI:

```
$ RUSTFLAGS="-C target-cpu=x86-64-v2" maturin build --release
$ pip install ./target/wheels/lancedb-*.whl
$ python verify.py
PASS: import + simd dispatch + table create + vector search all work.
```

Pre-fix on the same CPU (default `pip install lancedb`): `Illegal
instruction (core dumped)`. Full reproducer (deps + clone + build +
verification):
https://gist.github.com/tobocop2/2e341358b55c143527416edfdb1e37df.
Fork-internal verification PR with the dep bump and full logs:
[`tobocop2/lancedb#2`](https://github.com/tobocop2/lancedb/pull/2).

## Benchmarks — no regressions on modern CPUs from the lance-side change

These are the numbers I ran for the lance PR, confirming the runtime
dispatch doesn't slow down the default (`target-cpu=haswell`) wheel that
existing users install. Criterion, one machine, one session, base → PR,
no `RUSTFLAGS` override. Full methodology, null experiments, and logs:
[lance-format/lance#6630 benchmark
comment](https://github.com/lance-format/lance/pull/6630#issuecomment-4933063394)
and the [logs
gist](https://gist.github.com/tobocop2/3c6d0f449cbd736aa2501f89a7fe56a2).

| benchmark | EPYC 7B13 (`avx2`, `fma`, no `avx512f`) | Xeon Cascade
Lake (`avx512f`) |
|---|---|---|
| `Cosine(f32, scalar)` *(control)* | +0.04% | +0.09% |
| `Cosine(f64, scalar)` | −0.34% | −1.94% |
| `Cosine(u8, SIMD)` | +2.30% | +3.63% |
| `Dot(f16, SIMD)` | −0.58% | +0.61% |
| `Dot(f32, SIMD)` | +0.34% | **−6.08%** |
| `Dot(f32, arrow_arity)` | +0.02% | −0.00% |
| `L2(f32, scalar)` | −0.10% | −0.02% |
| `L2(f32, simd)` (dim 1024) | +2.63% | −0.53% |
| **`L2(simd,f32x8)` (dim 8)** | **−45.9%** | **−25.1%** |
| `L2(u8, SIMD)` | +0.42% | −3.11% |
| `NormL2(f32, SIMD)` | −1.02% | −4.17% |
| `NormL2(f64, SIMD)` | +3.51% | −0.58% |

Nothing regresses beyond the noise floor. Dim 8 — the PQ sub-vector
width — improves 25–46%.

---

To be transparent: this isn't my domain of expertise and the lance-side
implementation is AI-generated. I verified it works end-to-end on the
failing hardware. Happy to roll in feedback.
2026-07-16 10:54:42 -07:00
Jack Ye ca39258342 fix(python): route local sync namespace operations through rust (#3606)
Routes local sync child-namespace operations through the Rust-backed
connection instead of the Python namespace-client fallback.

Also keeps lazy namespace-client construction for table-to-Lance
conversion and preserves public namespace error mappings.

Validated locally with ruff format/check and targeted namespace pytest.
2026-07-16 10:53:49 -07:00
157 changed files with 8628 additions and 1170 deletions
+20
View File
@@ -0,0 +1,20 @@
{
"name": "lancedb",
"interface": {
"displayName": "LanceDB"
},
"plugins": [
{
"name": "lancedb",
"source": {
"source": "local",
"path": "./plugins/lancedb"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
}
]
}
+4
View File
@@ -5,3 +5,7 @@ This directory contains repo-scoped code agent skills for the LanceDB project.
Each skill is a folder that contains a required `SKILL.md` and optional bundled resources.
Codex discovers skills from `.agents/skills` in the current working directory and parent directories.
The `lancedb` skill lives in the `plugins/lancedb` plugin (see `plugins/lancedb/skills/lancedb`)
so it can be installed via the plugin marketplaces (`.claude-plugin/marketplace.json` and
`.agents/plugins/marketplace.json`); the `lancedb` entry here is a symlink into that plugin.
+1
View File
@@ -0,0 +1 @@
../../plugins/lancedb/skills/lancedb
+8 -1
View File
@@ -1,5 +1,5 @@
[tool.bumpversion]
current_version = "0.32.0-beta.2"
current_version = "0.37.1-beta.0"
parse = """(?x)
(?P<major>0|[1-9]\\d*)\\.
(?P<minor>0|[1-9]\\d*)\\.
@@ -75,6 +75,13 @@ filename = "nodejs/Cargo.toml"
replace = "\nversion = \"{new_version}\""
search = "\nversion = \"{current_version}\""
# The Python package takes its version from here (pyproject.toml declares
# `dynamic = ["version"]`, so maturin reads it out of the crate manifest).
[[tool.bumpversion.files]]
filename = "python/Cargo.toml"
replace = "\nversion = \"{new_version}\""
search = "\nversion = \"{current_version}\""
# Java documentation
[[tool.bumpversion.files]]
filename = "docs/src/java/java.md"
+19
View File
@@ -0,0 +1,19 @@
{
"name": "lancedb",
"owner": {
"name": "LanceDB"
},
"description": "LanceDB plugins for Claude Code.",
"plugins": [
{
"name": "lancedb",
"source": "./plugins/lancedb",
"description": "Write, review, debug, and document LanceDB pipelines in Python and TypeScript that work across local LanceDB OSS tables and remote LanceDB Enterprise/Cloud tables.",
"version": "0.1.0",
"author": {
"name": "LanceDB"
},
"category": "development"
}
]
}
@@ -27,19 +27,31 @@ runs:
# Extract failed job names
FAILED_JOBS=$(echo "$JOB_RESULTS" | jq -r 'to_entries | map(select(.value.result == "failure")) | map(.key) | join(", ")')
# Create issue with workflow name, failed jobs, and run URL
gh issue create \
--title "$WORKFLOW_NAME Failed ($FAILED_JOBS)" \
--body "The workflow **$WORKFLOW_NAME** failed during execution.
TITLE="$WORKFLOW_NAME Failed ($FAILED_JOBS)"
# This action now also runs on nightly schedules, so a breakage that
# persists for a few days would otherwise file one issue per night.
# Comment on the open report instead when one already exists.
EXISTING=$(gh issue list --state open --label ci --limit 100 --json number,title \
| jq -r --arg title "$TITLE" 'map(select(.title == $title)) | .[0].number // empty')
if [ -n "$EXISTING" ]; then
gh issue comment "$EXISTING" --body "Failed again: $RUN_URL"
echo "Commented on existing issue #$EXISTING"
else
gh issue create \
--title "$TITLE" \
--body "The workflow **$WORKFLOW_NAME** failed during execution.
**Failed jobs:** $FAILED_JOBS
**Run URL:** $RUN_URL
Please investigate the failed jobs and address any issues." \
--label "ci"
--label "ci"
echo "Issue created successfully"
echo "Issue created successfully"
fi
else
echo "No job failures detected, skipping issue creation"
fi
+22 -2
View File
@@ -18,6 +18,14 @@ inputs:
description: "The manylinux version to build for"
required: false
default: "2_17"
package-name:
description: "Override [project] name in python/pyproject.toml (e.g. 'lancedb-compat'). Default keeps 'lancedb'."
required: false
default: "lancedb"
rustflags:
description: "RUSTFLAGS for the build container, as a single whitespace-free token (e.g. '-Ctarget-cpu=x86-64-v2'). Empty leaves RUSTFLAGS unset, keeping the defaults from .cargo/config.toml."
required: false
default: ""
runs:
using: "composite"
steps:
@@ -27,6 +35,18 @@ runs:
ARM_BUILD: ${{ inputs.arm-build }}
run: |
echo "ARM BUILD: $ARM_BUILD"
- name: Patch package name for variant build
if: ${{ inputs.package-name != 'lancedb' }}
shell: bash
env:
PACKAGE_NAME: ${{ inputs.package-name }}
run: |
# Swap the [project] name so this build produces e.g. lancedb-compat
# wheels. The package still installs files under the lancedb/
# namespace -- import lancedb still works after pip install.
sed -i.bak 's/^name = "lancedb"$/name = "'"$PACKAGE_NAME"'"/' python/pyproject.toml
rm -f python/pyproject.toml.bak
grep '^name = ' python/pyproject.toml
- name: Build x86_64 Manylinux wheel
if: ${{ inputs.arm-build == 'false' }}
uses: PyO3/maturin-action@v1
@@ -34,7 +54,7 @@ runs:
maturin-version: "1.12.4"
command: build
working-directory: python
docker-options: "-e PIP_EXTRA_INDEX_URL='https://pypi.fury.io/lance-format/ https://pypi.fury.io/lancedb/' -e PROTOC=/usr/local/bin/protoc"
docker-options: "-e PIP_EXTRA_INDEX_URL='https://pypi.fury.io/lance-format/ https://pypi.fury.io/lancedb/' -e PROTOC=/usr/local/bin/protoc ${{ inputs.rustflags != '' && format('-e RUSTFLAGS={0}', inputs.rustflags) || '' }}"
target: x86_64-unknown-linux-gnu
manylinux: ${{ inputs.manylinux }}
args: ${{ inputs.args }}
@@ -51,7 +71,7 @@ runs:
maturin-version: "1.12.4"
command: build
working-directory: python
docker-options: "-e PIP_EXTRA_INDEX_URL='https://pypi.fury.io/lance-format/ https://pypi.fury.io/lancedb/' -e PROTOC=/usr/local/bin/protoc"
docker-options: "-e PIP_EXTRA_INDEX_URL='https://pypi.fury.io/lance-format/ https://pypi.fury.io/lancedb/' -e PROTOC=/usr/local/bin/protoc ${{ inputs.rustflags != '' && format('-e RUSTFLAGS={0}', inputs.rustflags) || '' }}"
target: aarch64-unknown-linux-gnu
manylinux: ${{ inputs.manylinux }}
args: ${{ inputs.args }}
-1
View File
@@ -6,7 +6,6 @@ on:
# We don't publish pre-releases for Rust. Crates.io is just a source
# distribution, so we don't need to publish pre-releases.
- "v*-beta*"
- "*-v*" # for example, python-vX.Y.Z
env:
# This env var is used by Swatinem/rust-cache@v2 for the cache
+85
View File
@@ -0,0 +1,85 @@
name: GitHub Release
# All SDKs share one version, so a single `vX.Y.Z` tag produces a single GitHub
# release covering all of them. The per-package publish workflows (PyPI, NPM,
# Cargo, Maven) trigger off the same tag independently.
on:
push:
tags:
- "v*"
permissions:
contents: read
jobs:
gh-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
lfs: true
- name: Extract version
id: extract_version
env:
GITHUB_REF: ${{ github.ref }}
run: |
set -e
echo "Extracting tag and version from $GITHUB_REF"
if [[ $GITHUB_REF =~ refs/tags/v(.*) ]]; then
VERSION=${BASH_REMATCH[1]}
TAG=v$VERSION
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
else
echo "Failed to extract version from $GITHUB_REF"
exit 1
fi
echo "Extracted version $VERSION from $GITHUB_REF"
if [[ $VERSION =~ beta ]]; then
echo "This is a beta release"
echo "prerelease=true" >> $GITHUB_OUTPUT
# Get last release (that is not this one)
FROM_TAG=$(git tag --sort='version:refname' \
| grep ^v \
| grep -vF "$TAG" \
| python ci/semver_sort.py v \
| tail -n 1)
else
echo "This is a stable release"
echo "prerelease=false" >> $GITHUB_OUTPUT
# Get last stable tag (ignore betas)
FROM_TAG=$(git tag --sort='version:refname' \
| grep ^v \
| grep -vF "$TAG" \
| grep -v beta \
| python ci/semver_sort.py v \
| tail -n 1)
fi
echo "Found from tag $FROM_TAG"
echo "from_tag=$FROM_TAG" >> $GITHUB_OUTPUT
- name: Create Release Notes
id: release_notes
uses: mikepenz/release-changelog-builder-action@v4
with:
configuration: .github/release_notes.json
toTag: ${{ steps.extract_version.outputs.tag }}
fromTag: ${{ steps.extract_version.outputs.from_tag }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GH release
uses: softprops/action-gh-release@v2
with:
# Marking betas as pre-releases keeps them from taking the "Latest"
# badge on the releases page.
prerelease: ${{ steps.extract_version.outputs.prerelease }}
make_latest: ${{ steps.extract_version.outputs.prerelease == 'false' }}
tag_name: ${{ steps.extract_version.outputs.tag }}
token: ${{ secrets.GITHUB_TOKEN }}
generate_release_notes: false
name: LanceDB v${{ steps.extract_version.outputs.version }}
body: ${{ steps.release_notes.outputs.changelog }}
+8 -30
View File
@@ -1,13 +1,14 @@
name: Create release commit
# This workflow increments versions, tags the version, and pushes it.
# This workflow increments the version, tags it, and pushes it. All SDKs share
# a single version, so one tag releases all of them.
# When a tag is pushed, another workflow is triggered that creates a GH release
# and uploads the binaries. This workflow is only for creating the tag.
# This script will enforce that a minor version is incremented if there are any
# breaking changes since the last minor increment. However, it isn't able to
# differentiate between breaking changes in Node versus Python. If you wish to
# bypass this check, you can manually increment the version and push the tag.
# breaking changes since the last minor increment. A breaking change in any SDK
# bumps the minor version for all of them. If you wish to bypass this check, you
# can manually increment the version and push the tag.
on:
workflow_dispatch:
inputs:
@@ -24,16 +25,6 @@ on:
options:
- preview
- stable
python:
description: 'Make a Python release'
required: true
default: true
type: boolean
other:
description: 'Make a Node/Rust/Java release'
required: true
default: true
type: boolean
bump-minor:
description: 'Bump minor version'
required: true
@@ -65,29 +56,16 @@ jobs:
run: |
git config user.name 'Lance Release'
git config user.email 'lance-dev@lancedb.com'
- name: Bump Python version
if: ${{ inputs.python }}
working-directory: python
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Need to get the commit before bumping the version, so we can
# determine if there are breaking changes in the next step as well.
echo "COMMIT_BEFORE_BUMP=$(git rev-parse HEAD)" >> $GITHUB_ENV
pip install bump-my-version PyGithub packaging
bash ../ci/bump_version.sh ${{ inputs.type }} ${{ inputs.bump-minor }} python-v
- name: Bump Node/Rust version
if: ${{ inputs.other }}
- name: Bump version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pip install bump-my-version PyGithub packaging
bash ci/bump_version.sh ${{ inputs.type }} ${{ inputs.bump-minor }} v $COMMIT_BEFORE_BUMP
bash ci/bump_version.sh ${{ inputs.type }} ${{ inputs.bump-minor }}
bash ci/update_lockfiles.sh --amend
- name: Push new version tag
if: ${{ !inputs.dry_run }}
uses: ad-m/github-push-action@master
uses: ad-m/github-push-action@881a6320fdb16eb5318c5054f31c218aec2b324c # v1.3.0
with:
# Need to use PAT here too to trigger next workflow. See comment above.
github_token: ${{ secrets.LANCEDB_RELEASE_TOKEN }}
+15
View File
@@ -61,6 +61,11 @@ jobs:
sudo apt update
sudo apt install -y protobuf-compiler libssl-dev
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Format Rust
run: cargo fmt --all -- --check
- name: Lint Rust
@@ -103,6 +108,11 @@ jobs:
cache: 'pnpm'
cache-dependency-path: nodejs/pnpm-lock.yaml
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install dependencies
run: |
sudo apt update
@@ -182,6 +192,11 @@ jobs:
cache-dependency-path: nodejs/pnpm-lock.yaml
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install dependencies
run: |
brew install protobuf
+95 -89
View File
@@ -10,10 +10,16 @@ permissions:
on:
push:
branches:
- main
tags:
- "v*"
# The cross-compiled targets (musl especially) break from toolchain and
# dependency changes that nothing else in CI catches, and discovering that
# mid-release is expensive. A nightly run keeps that signal while dropping
# the full 8-target release matrix from all ~90 pushes to main each month.
# `report-failure` files an issue when a nightly breaks.
schedule:
- cron: "0 8 * * *"
workflow_dispatch:
pull_request:
# This should trigger a dry run (we skip the final publish step)
paths:
@@ -26,73 +32,6 @@ concurrency:
cancel-in-progress: true
jobs:
gh-release:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
lfs: true
- name: Extract version
id: extract_version
env:
GITHUB_REF: ${{ github.ref }}
run: |
set -e
echo "Extracting tag and version from $GITHUB_REF"
if [[ $GITHUB_REF =~ refs/tags/v(.*) ]]; then
VERSION=${BASH_REMATCH[1]}
TAG=v$VERSION
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
else
echo "Failed to extract version from $GITHUB_REF"
exit 1
fi
echo "Extracted version $VERSION from $GITHUB_REF"
if [[ $VERSION =~ beta ]]; then
echo "This is a beta release"
# Get last release (that is not this one)
FROM_TAG=$(git tag --sort='version:refname' \
| grep ^v \
| grep -vF "$TAG" \
| python ci/semver_sort.py v \
| tail -n 1)
else
echo "This is a stable release"
# Get last stable tag (ignore betas)
FROM_TAG=$(git tag --sort='version:refname' \
| grep ^v \
| grep -vF "$TAG" \
| grep -v beta \
| python ci/semver_sort.py v \
| tail -n 1)
fi
echo "Found from tag $FROM_TAG"
echo "from_tag=$FROM_TAG" >> $GITHUB_OUTPUT
- name: Create Release Notes
id: release_notes
uses: mikepenz/release-changelog-builder-action@v4
with:
configuration: .github/release_notes.json
toTag: ${{ steps.extract_version.outputs.tag }}
fromTag: ${{ steps.extract_version.outputs.from_tag }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GH release
uses: softprops/action-gh-release@v2
with:
prerelease: ${{ contains('beta', github.ref) }}
tag_name: ${{ steps.extract_version.outputs.tag }}
token: ${{ secrets.GITHUB_TOKEN }}
generate_release_notes: false
name: Node/Rust LanceDB v${{ steps.extract_version.outputs.version }}
body: ${{ steps.release_notes.outputs.changelog }}
build-lancedb:
strategy:
fail-fast: false
@@ -101,9 +40,18 @@ jobs:
- target: aarch64-apple-darwin
host: macos-latest
features: fp16kernels
pre_build: brew install protobuf
pre_build: |-
brew install protobuf
# Fat LTO (the workspace default in .cargo/config.toml) is
# single-threaded and is the peak-memory step of the build. On
# this runner it accounted for ~111 of the job's ~113 minutes,
# making it the critical path of the entire publish pipeline.
# ThinLTO parallelizes it across the runner's cores, for a few
# percent of runtime performance.
export CARGO_PROFILE_RELEASE_LTO=thin
export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16
- target: x86_64-pc-windows-msvc
host: windows-2025-8x-x64
host: windows-2025
features: ","
pre_build: |-
choco install --no-progress protoc ninja nasm
@@ -111,19 +59,19 @@ jobs:
# There is an issue where choco doesn't add nasm to the path
export PATH="$PATH:/c/Program Files/NASM"
nasm -v
# Fat LTO of the cdylib is single-threaded and the peak-memory
# step of the build, and had started hitting rustc-LLVM OOM on the
# Windows runners. ThinLTO parallelizes it across the runner's
# cores and keeps peak memory well under the limit.
# See the ThinLTO note on aarch64-apple-darwin above. Keeping
# peak memory down is also what lets this run on the standard
# 4-core runner: the 8-core larger runner was only needed to
# stop fat LTO from OOMing rustc-LLVM.
export CARGO_PROFILE_RELEASE_LTO=thin
export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16
- target: aarch64-pc-windows-msvc
host: windows-2025-8x-x64
host: windows-2025
features: ","
pre_build: |-
choco install --no-progress protoc
rustup target add aarch64-pc-windows-msvc
# See ThinLTO note on the x86_64-pc-windows-msvc target above.
# See the ThinLTO note on aarch64-apple-darwin above.
export CARGO_PROFILE_RELEASE_LTO=thin
export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16
- target: x86_64-unknown-linux-gnu
@@ -198,16 +146,49 @@ jobs:
with:
toolchain: stable
targets: ${{ matrix.settings.target }}
- name: Cache cargo
uses: actions/cache@v5
# These builds were entirely uncached: the old key was static, so
# `actions/cache` (which only writes on a miss) could never refresh it,
# and the multi-GB whole-`target/` copy it tried to store never fit the
# repo's cache budget, so no entry was ever saved. rust-cache prunes
# `target/` to dependency artifacts and keys on Cargo.lock plus the rustc
# version, which both fixes the key and keeps entries a sane size.
#
# This caches dependency *compilation* only. The LTO link of the cdylib
# re-runs regardless, since the local crate changes every time, so the
# win is larger on the non-LTO jobs than here.
- name: Cache cargo (native builds)
uses: Swatinem/rust-cache@v2
if: ${{ !matrix.settings.docker }}
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
.cargo-cache
target/
key: nodejs-${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }}
# The release profile and per-target dirs differ from what the test
# workflows cache, so these need to be separate entries.
key: release-${{ matrix.settings.target }}
# Only the nightly run on main writes, so tag and PR runs restore a
# warm entry without every dependabot PR writing its own (which would
# be unreadable elsewhere anyway, since GitHub scopes caches to the
# creating ref). The nightly cadence also keeps entries inside
# GitHub's 7-day eviction window, which a tag-only trigger would not.
save-if: ${{ github.ref == 'refs/heads/main' }}
# Docker builds can use rust-cache too. `target/` already lives on the
# host because the whole workspace is bind-mounted into the container, and
# rust-cache's prune and save run host-side, so they can manage it -- which
# is what keeps the entry to dependency artifacts rather than a multi-GB
# copy of everything.
#
# Two differences from the native builds. The container's CARGO_HOME is
# bind-mounted from `.cargo-cache` rather than the host's ~/.cargo, so that
# has to be cached explicitly. And the key is derived from the *host* rustc
# version, which is not the compiler that produced these artifacts; that is
# safe because cargo fingerprints the real compiler and rebuilds on a
# mismatch, it just means a base-image toolchain bump costs one cold build
# instead of invalidating the key.
- name: Cache cargo (docker builds)
uses: Swatinem/rust-cache@v2
if: ${{ matrix.settings.docker }}
with:
key: docker-${{ matrix.settings.target }}
cache-directories: .cargo-cache
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install Zig
@@ -225,9 +206,13 @@ jobs:
if: ${{ matrix.settings.docker }}
with:
image: ${{ matrix.settings.docker }}
# All three mounts must live under `.cargo-cache`, which is what the
# cache step above saves. Previously the registry mounts pointed at
# `.cargo/...`, a path nothing cached, so the container re-downloaded
# the whole crate registry on every run.
options: "--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db \
-v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache \
-v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index \
-v ${{ github.workspace }}/.cargo-cache/registry/cache:/usr/local/cargo/registry/cache \
-v ${{ github.workspace }}/.cargo-cache/registry/index:/usr/local/cargo/registry/index \
-v ${{ github.workspace }}:/build -w /build/nodejs"
run: |
set -e
@@ -239,6 +224,16 @@ jobs:
--js ../lancedb/native.js \
--strip \
--output-dir dist/
# The container runs as root (`--user 0:0`), so everything it wrote to the
# mounted cache dirs is root-owned. rust-cache's post step runs as the
# runner user and has to both read these and delete from them while
# pruning, so hand them back before it runs.
- name: Take ownership of docker build output
if: ${{ matrix.settings.docker }}
run: |
sudo chown -R "$(id -u):$(id -g)" \
"${{ github.workspace }}/.cargo-cache" \
"${{ github.workspace }}/target"
- name: Build
run: |
${{ matrix.settings.pre_build }}
@@ -252,6 +247,15 @@ jobs:
--output-dir dist/
if: ${{ !matrix.settings.docker }}
shell: bash
# The standard Windows runners have ~14 GB free, and a release `target/`
# for this workspace is a large fraction of that. Report the remaining
# headroom so a build that only just fits is visible before a dependency
# bump turns it into a failed release. `always()` so the numbers are
# still there when the build is what ran out of space.
- name: Report disk headroom
if: always()
run: df -h
shell: bash
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
@@ -402,7 +406,9 @@ jobs:
name: Report Workflow Failure
runs-on: ubuntu-latest
needs: [build-lancedb, test-lancedb, publish]
if: always() && failure() && startsWith(github.ref, 'refs/tags/v')
# Nightly runs are the only thing watching the cross-compiled targets now,
# so they have to report failures too or the signal is silently lost.
if: always() && failure() && (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'schedule')
permissions:
contents: read
issues: write
+42 -76
View File
@@ -3,7 +3,7 @@ name: PyPI Publish
on:
push:
tags:
- 'python-v*'
- 'v*'
pull_request:
# This should trigger a dry run (we skip the final publish step)
paths:
@@ -20,9 +20,15 @@ env:
permissions:
contents: read
# Without this, a force-push to a PR leaves the previous run going -- including
# a ~74 minute Windows job and a billed arm64 wheel build.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
linux:
name: Python ${{ matrix.config.platform }} manylinux${{ matrix.config.manylinux }}
name: Python ${{ matrix.config.package_name }} ${{ matrix.config.platform }} manylinux${{ matrix.config.manylinux }}
timeout-minutes: 60
strategy:
matrix:
@@ -31,11 +37,28 @@ jobs:
manylinux: "2_28"
extra_args: "--features fp16kernels"
runner: ubuntu-22.04
package_name: "lancedb"
rustflags: ""
# For successful fat LTO builds, we need a large runner to avoid OOM errors.
- platform: aarch64
manylinux: "2_28"
extra_args: "--features fp16kernels"
runner: ubuntu-2404-8x-arm64
package_name: "lancedb"
rustflags: ""
# `lancedb-compat`: pre-Haswell-friendly variant for x86_64 hosts
# without AVX2 (Sandy Bridge / Ivy Bridge / Westmere on Intel,
# Bulldozer / Piledriver / Steamroller on AMD). Compiled at the
# `x86-64-v2` baseline; runtime SIMD dispatch in lance-linalg
# picks the appropriate tier (scalar / AVX / AVX+FMA / AVX2+FMA
# / AVX-512) at load time. Same import as `lancedb` -- conflicts
# at install time, so users pick one.
- platform: x86_64
manylinux: "2_28"
extra_args: ""
runner: ubuntu-22.04
package_name: "lancedb-compat"
rustflags: "-Ctarget-cpu=x86-64-v2"
runs-on: ${{ matrix.config.runner }}
steps:
- uses: actions/checkout@v6
@@ -52,11 +75,13 @@ jobs:
args: "--release --strip ${{ matrix.config.extra_args }}"
arm-build: ${{ matrix.config.platform == 'aarch64' }}
manylinux: ${{ matrix.config.manylinux }}
package-name: ${{ matrix.config.package_name }}
rustflags: ${{ matrix.config.rustflags }}
- uses: actions/upload-artifact@v7
if: startsWith(github.ref, 'refs/tags/python-v')
if: startsWith(github.ref, 'refs/tags/v')
with:
name: wheels-linux-${{ matrix.config.platform }}-${{ matrix.config.manylinux }}
path: target/wheels/lancedb-*.whl
name: wheels-linux-${{ matrix.config.package_name }}-${{ matrix.config.platform }}-${{ matrix.config.manylinux }}
path: target/wheels/*.whl
if-no-files-found: error
mac:
timeout-minutes: 90
@@ -82,7 +107,7 @@ jobs:
python-minor-version: 10
args: "--release --strip --target ${{ matrix.config.target }} --features fp16kernels"
- uses: actions/upload-artifact@v7
if: startsWith(github.ref, 'refs/tags/python-v')
if: startsWith(github.ref, 'refs/tags/v')
with:
name: wheels-mac-${{ matrix.config.target }}
path: target/wheels/lancedb-*.whl
@@ -103,19 +128,26 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: "3.13"
# NOTE: caching cargo here would be a no-op. This workflow only runs on
# tags and PRs, and GitHub only lets a run restore caches from its own ref
# or the default branch -- so with no run on main there is nothing that
# can populate an entry the release build would be allowed to read. Fixing
# this needs a main/nightly trigger (which would also catch wheel-build
# breakage before a release); the ~74 minutes here is otherwise dominated
# by the fat-LTO link, which no cache avoids.
- uses: ./.github/workflows/build_windows_wheel
with:
python-minor-version: 10
args: "--release --strip"
- uses: actions/upload-artifact@v7
if: startsWith(github.ref, 'refs/tags/python-v')
if: startsWith(github.ref, 'refs/tags/v')
with:
name: wheels-windows
path: target/wheels/lancedb-*.whl
if-no-files-found: error
publish:
name: Publish wheels
if: startsWith(github.ref, 'refs/tags/python-v')
if: startsWith(github.ref, 'refs/tags/v')
needs: [linux, mac, windows]
runs-on: ubuntu-latest
permissions:
@@ -145,7 +177,7 @@ jobs:
FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
run: |
shopt -s nullglob
WHEELS=(target/wheels/lancedb-*.whl)
WHEELS=(target/wheels/*.whl)
if [[ ${#WHEELS[@]} -eq 0 ]]; then
echo "No wheels found in target/wheels/" >&2
exit 1
@@ -164,72 +196,6 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: target/wheels/
gh-release:
if: startsWith(github.ref, 'refs/tags/python-v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
lfs: true
- name: Extract version
id: extract_version
env:
GITHUB_REF: ${{ github.ref }}
run: |
set -e
echo "Extracting tag and version from $GITHUB_REF"
if [[ $GITHUB_REF =~ refs/tags/python-v(.*) ]]; then
VERSION=${BASH_REMATCH[1]}
TAG=python-v$VERSION
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
else
echo "Failed to extract version from $GITHUB_REF"
exit 1
fi
echo "Extracted version $VERSION from $GITHUB_REF"
if [[ $VERSION =~ beta ]]; then
echo "This is a beta release"
# Get last release (that is not this one)
FROM_TAG=$(git tag --sort='version:refname' \
| grep ^python-v \
| grep -vF "$TAG" \
| python ci/semver_sort.py python-v \
| tail -n 1)
else
echo "This is a stable release"
# Get last stable tag (ignore betas)
FROM_TAG=$(git tag --sort='version:refname' \
| grep ^python-v \
| grep -vF "$TAG" \
| grep -v beta \
| python ci/semver_sort.py python-v \
| tail -n 1)
fi
echo "Found from tag $FROM_TAG"
echo "from_tag=$FROM_TAG" >> $GITHUB_OUTPUT
- name: Create Python Release Notes
id: python_release_notes
uses: mikepenz/release-changelog-builder-action@v4
with:
configuration: .github/release_notes.json
toTag: ${{ steps.extract_version.outputs.tag }}
fromTag: ${{ steps.extract_version.outputs.from_tag }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Python GH release
uses: softprops/action-gh-release@v2
with:
prerelease: ${{ contains('beta', github.ref) }}
tag_name: ${{ steps.extract_version.outputs.tag }}
token: ${{ secrets.GITHUB_TOKEN }}
generate_release_notes: false
name: Python LanceDB v${{ steps.extract_version.outputs.version }}
body: ${{ steps.python_release_notes.outputs.changelog }}
report-failure:
name: Report Workflow Failure
runs-on: ubuntu-latest
@@ -237,7 +203,7 @@ jobs:
permissions:
contents: read
issues: write
if: always() && failure() && startsWith(github.ref, 'refs/tags/python-v')
if: always() && failure() && startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/create-failure-issue
+33
View File
@@ -108,6 +108,15 @@ jobs:
run: |
sudo apt update
sudo apt install -y protobuf-compiler
# `pip install -e .` builds the extension with maturin, which is most of
# this job's ~33 minutes. It had no Rust cache, so every dependency was
# recompiled from scratch on every run.
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install
run: |
pip install --extra-index-url https://pypi.fury.io/lance-format/ --extra-index-url https://pypi.fury.io/lancedb/ -e .[tests,dev,embeddings]
@@ -168,6 +177,14 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: "3.13"
# maturin runs cargo natively on macOS (docker is Linux-only), so the host
# target dir is cacheable. This job had no Rust cache.
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: ./.github/workflows/build_mac_wheel
with:
args: --profile ci
@@ -197,6 +214,14 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: "3.13"
# maturin runs cargo natively on Windows (docker is Linux-only), so the
# host target dir is cacheable. This job had no Rust cache at all and so
# rebuilt every dependency from scratch on every run.
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. The repo sits at
# GitHub's cache cap, so per-PR saves just evict main's entries.
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: ./.github/workflows/build_windows_wheel
with:
args: --profile ci
@@ -224,6 +249,14 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: "3.10"
# As with Doctest, `pip install -e .` compiles the extension and this job
# had no Rust cache, which is most of its ~37 minutes.
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install lancedb
run: |
pip install "pydantic<2"
+47 -9
View File
@@ -48,6 +48,11 @@ jobs:
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install dependencies
run: |
sudo apt update
@@ -89,6 +94,11 @@ jobs:
run: rm -f Cargo.lock
- uses: rui314/setup-mold@v1
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install dependencies
run: |
sudo apt update
@@ -98,7 +108,7 @@ jobs:
cargo build --profile ci --benches --all-features --tests
linux:
timeout-minutes: 30
timeout-minutes: 60
# To build all features, we need more disk space than is available
# on the free OSS github runner. This is mostly due to the the
# sentence-transformers feature.
@@ -118,6 +128,11 @@ jobs:
fetch-depth: 0
lfs: true
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install dependencies
run: |
sudo apt update
@@ -158,7 +173,7 @@ jobs:
run: CARGO_ARGS="--profile ci" make -C ./lancedb remote-tests
macos:
timeout-minutes: 30
timeout-minutes: 60
strategy:
matrix:
mac-runner: ["macos-14", "macos-15"]
@@ -175,6 +190,11 @@ jobs:
- name: CPU features
run: sysctl -a | grep cpu
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install dependencies
run: brew install protobuf
- name: Run tests
@@ -187,12 +207,19 @@ jobs:
cargo test --profile ci --features $ALL_FEATURES --locked
windows:
runs-on: windows-2022
strategy:
fail-fast: false
matrix:
target:
- x86_64-pc-windows-msvc
- aarch64-pc-windows-msvc
include:
- target: x86_64-pc-windows-msvc
runner: windows-2022
# windows-11-arm is a standard runner, so it is free on public repos.
# Running natively lets the aarch64 tests actually execute -- this
# job used to cross-compile them and then skip the test step, paying
# full codegen and link cost for a compile check.
- target: aarch64-pc-windows-msvc
runner: windows-11-arm
runs-on: ${{ matrix.runner }}
defaults:
run:
working-directory: rust/lancedb
@@ -201,6 +228,11 @@ jobs:
- name: Set target
run: rustup target add ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install Protoc v21.12
run: choco install --no-progress protoc
- name: Build
@@ -208,11 +240,12 @@ jobs:
$env:VCPKG_ROOT = $env:VCPKG_INSTALLATION_ROOT
cargo build --profile ci --features aws,remote --tests --locked --target ${{ matrix.target }}
- name: Run tests
# Can only run tests when target matches host
if: ${{ matrix.target == 'x86_64-pc-windows-msvc' }}
run: |
$env:VCPKG_ROOT = $env:VCPKG_INSTALLATION_ROOT
cargo test --profile ci --features aws,remote --locked
# `--target` has to match the build step above. Without it cargo uses
# target/ci/ rather than target/<triple>/ci/ and rebuilds the entire
# dependency graph a second time.
cargo test --profile ci --features aws,remote --locked --target ${{ matrix.target }}
msrv:
# Check the minimum supported Rust version
@@ -238,6 +271,11 @@ jobs:
with:
toolchain: ${{ matrix.msrv }}
- uses: Swatinem/rust-cache@v2
with:
# Restore everywhere, but only save from main. Per-PR saves are
# unreadable outside their own branch anyway, since GitHub scopes
# caches to the creating ref.
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Downgrade dependencies
# These packages have newer requirements for MSRV
run: |
+29
View File
@@ -92,6 +92,8 @@ Python bindings changes:
* 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:
@@ -103,6 +105,33 @@ TypeScript bindings changes:
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:
```shell
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.
Generated
+211 -187
View File
File diff suppressed because it is too large Load Diff
+14 -15
View File
@@ -13,20 +13,20 @@ categories = ["database-implementations"]
rust-version = "1.91.0"
[workspace.dependencies]
lance = { "version" = "=9.0.0-rc.1", default-features = false, "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-core = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-datagen = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-file = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-io = { "version" = "=9.0.0-rc.1", default-features = false, "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-index = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-linalg = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-namespace = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-namespace-impls = { "version" = "=9.0.0-rc.1", default-features = false, "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-table = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-testing = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-datafusion = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-encoding = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance-arrow = { "version" = "=9.0.0-rc.1", "tag" = "v9.0.0-rc.1", "git" = "https://github.com/lance-format/lance.git" }
lance = { "version" = "=10.0.0-beta.5", default-features = false, "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-core = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-datagen = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-file = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-io = { "version" = "=10.0.0-beta.5", default-features = false, "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-index = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-linalg = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-namespace = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-namespace-impls = { "version" = "=10.0.0-beta.5", default-features = false, "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-table = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-testing = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-datafusion = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-encoding = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
lance-arrow = { "version" = "=10.0.0-beta.5", "tag" = "v10.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" }
ahash = "0.8"
# Note that this one does not include pyarrow
arrow = { version = "58.0.0", optional = false }
@@ -64,7 +64,6 @@ snafu = "0.8"
url = "2"
num-traits = "0.2"
regex = "1.10"
lazy_static = "1"
semver = "1.0.25"
chrono = "0.4"
+3 -3
View File
@@ -2,9 +2,9 @@ set -e
RELEASE_TYPE=${1:-"stable"}
BUMP_MINOR=${2:-false}
TAG_PREFIX=${3:-"v"} # Such as "python-v"
HEAD_SHA=${4:-$(git rev-parse HEAD)}
HEAD_SHA=$(git rev-parse HEAD)
readonly TAG_PREFIX="v"
readonly SELF_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
PREV_TAG=$(git tag --sort='version:refname' | grep ^$TAG_PREFIX | python $SELF_DIR/semver_sort.py $TAG_PREFIX | tail -n 1)
@@ -12,7 +12,7 @@ echo "Found previous tag $PREV_TAG"
# Initially, we don't want to tag if we are doing stable, because we will bump
# again later. See comment at end for why.
if [[ "$RELEASE_TYPE" == 'stable' ]]; then
if [[ "$RELEASE_TYPE" == 'stable' ]]; then
BUMP_ARGS="--no-tag"
fi
+5
View File
@@ -51,6 +51,11 @@ plugins:
paths: [../python/python]
options:
docstring_style: numpy
docstring_options:
# Attributes documented in a `Parameters` section, and pydantic
# dataclasses whose `__init__` griffe cannot see statically, both
# trip this check. It reports nothing actionable here.
warn_unknown_params: false
heading_level: 3
show_signature_annotations: true
show_root_heading: true
+11 -1
View File
@@ -453,6 +453,16 @@ paths:
The metric type to use for the index. l2, Cosine, Dot are supported.
index_type:
type: string
custom_stop_words:
type: [array, "null"]
items:
type: string
description: |
The custom stop-word list for an FTS index. A non-null
array replaces the language's built-in stop-word list and is only
applied when remove_stop_words is enabled. Null uses the built-in
language list, while an empty array explicitly replaces it with no
stop words.
responses:
"200":
description: Index successfully created
@@ -510,4 +520,4 @@ paths:
"401":
$ref: "#/components/responses/unauthorized"
"404":
$ref: "#/components/responses/not_found"
$ref: "#/components/responses/not_found"
+165 -30
View File
@@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`:
<dependency>
<groupId>com.lancedb</groupId>
<artifactId>lancedb-core</artifactId>
<version>0.32.0-beta.2</version>
<version>0.37.1-beta.0</version>
</dependency>
```
@@ -249,6 +249,57 @@ try (BufferAllocator allocator = new RootAllocator();
}
```
### Creating an Empty Table
To create an empty table, send an Arrow IPC stream that contains the table schema and no record batches.
The schema in the IPC stream becomes the table schema, and rows can be inserted later.
```java
import org.lance.namespace.model.CreateTableRequest;
import org.lance.namespace.model.CreateTableResponse;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.ipc.ArrowStreamWriter;
import org.apache.arrow.vector.types.FloatingPointPrecision;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
import org.apache.arrow.vector.types.pojo.Schema;
import java.io.ByteArrayOutputStream;
import java.nio.channels.Channels;
import java.util.Arrays;
Schema schema = new Schema(Arrays.asList(
new Field("id", FieldType.nullable(new ArrowType.Int(32, true)), null),
new Field("name", FieldType.nullable(new ArrowType.Utf8()), null),
new Field("embedding",
FieldType.nullable(new ArrowType.FixedSizeList(128)),
Arrays.asList(new Field("item",
FieldType.nullable(new ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE)),
null)))
));
byte[] emptyTableData;
try (BufferAllocator allocator = new RootAllocator();
VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator)) {
root.setRowCount(0);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ArrowStreamWriter writer = new ArrowStreamWriter(root, null, Channels.newChannel(out))) {
writer.start();
writer.end();
}
emptyTableData = out.toByteArray();
}
CreateTableRequest request = new CreateTableRequest();
request.setId(Arrays.asList("my_namespace", "empty_table"));
CreateTableResponse response = namespaceClient.createTable(request, emptyTableData);
```
### Insert
```java
@@ -431,9 +482,88 @@ query.setVector(vector);
byte[] result = namespaceClient.queryTable(query);
```
### Reading Query Results
## Indexing
Query results are returned in Apache Arrow IPC file format. Here's how to read them:
The Java SDK exposes the REST namespace index operations through the same `LanceNamespace` client.
Index creation runs asynchronously, so use `listTableIndices` or `describeTableIndexStats` to check progress.
### Creating a Vector Index
```java
import org.lance.namespace.model.CreateTableIndexRequest;
import org.lance.namespace.model.CreateTableIndexResponse;
CreateTableIndexRequest request = new CreateTableIndexRequest();
request.setId(Arrays.asList("my_namespace", "my_table"));
request.setColumn("embedding");
request.setIndexType("IVF_PQ");
request.setDistanceType("cosine");
request.setName("embedding_idx");
CreateTableIndexResponse response = namespaceClient.createTableIndex(request);
System.out.println("Index transaction: " + response.getTransactionId());
```
### Creating a Scalar Index
```java
import org.lance.namespace.model.CreateTableIndexRequest;
import org.lance.namespace.model.CreateTableScalarIndexResponse;
CreateTableIndexRequest request = new CreateTableIndexRequest();
request.setId(Arrays.asList("my_namespace", "my_table"));
request.setColumn("category");
request.setIndexType("BTREE");
request.setName("category_idx");
CreateTableScalarIndexResponse response = namespaceClient.createTableScalarIndex(request);
System.out.println("Index transaction: " + response.getTransactionId());
```
### Creating a Full Text Search Index
```java
import org.lance.namespace.model.CreateTableIndexRequest;
import org.lance.namespace.model.CreateTableScalarIndexResponse;
CreateTableIndexRequest request = new CreateTableIndexRequest();
request.setId(Arrays.asList("my_namespace", "my_table"));
request.setColumn("text_column");
request.setIndexType("FTS");
request.setName("text_idx");
request.setBaseTokenizer("simple");
request.setLowerCase(true);
request.setWithPosition(true);
CreateTableScalarIndexResponse response = namespaceClient.createTableScalarIndex(request);
System.out.println("Index transaction: " + response.getTransactionId());
```
### Listing Indexes
```java
import org.lance.namespace.model.IndexContent;
import org.lance.namespace.model.ListTableIndicesRequest;
import org.lance.namespace.model.ListTableIndicesResponse;
ListTableIndicesRequest request = new ListTableIndicesRequest();
request.setId(Arrays.asList("my_namespace", "my_table"));
ListTableIndicesResponse response = namespaceClient.listTableIndices(request);
for (IndexContent index : response.getIndexes()) {
System.out.println(index.getIndexName() + ": " + index.getStatus());
}
```
!!! note
The current Java namespace API exposes index type, index name, distance type, and full text search tokenizer options.
IVF training parameters such as `num_partitions` are not exposed by `CreateTableIndexRequest` yet.
To make those configurable from Java, the namespace API must add those fields first.
## Reading Query Results
Query results are returned as bytes in Apache Arrow IPC file format. Put the byte-channel
adapter behind a small helper so query code can work with `ArrowFileReader` directly:
```java
import org.apache.arrow.vector.ipc.ArrowFileReader;
@@ -441,45 +571,50 @@ import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
// Helper class to read Arrow data from byte array
class ByteArraySeekableByteChannel implements SeekableByteChannel {
private final byte[] data;
private long position = 0;
private boolean isOpen = true;
public ByteArraySeekableByteChannel(byte[] data) {
this.data = data;
final class ArrowIpc {
static ArrowFileReader openFileReader(byte[] data, BufferAllocator allocator) throws IOException {
return new ArrowFileReader(new ByteArraySeekableByteChannel(data), allocator);
}
@Override
public int read(ByteBuffer dst) {
int remaining = dst.remaining();
int available = (int) (data.length - position);
if (available <= 0) return -1;
int toRead = Math.min(remaining, available);
dst.put(data, (int) position, toRead);
position += toRead;
return toRead;
}
private static final class ByteArraySeekableByteChannel implements SeekableByteChannel {
private final byte[] data;
private long position = 0;
private boolean isOpen = true;
@Override public long position() { return position; }
@Override public SeekableByteChannel position(long newPosition) { position = newPosition; return this; }
@Override public long size() { return data.length; }
@Override public boolean isOpen() { return isOpen; }
@Override public void close() { isOpen = false; }
@Override public int write(ByteBuffer src) { throw new UnsupportedOperationException(); }
@Override public SeekableByteChannel truncate(long size) { throw new UnsupportedOperationException(); }
private ByteArraySeekableByteChannel(byte[] data) {
this.data = data;
}
@Override
public int read(ByteBuffer dst) {
int remaining = dst.remaining();
int available = (int) (data.length - position);
if (available <= 0) return -1;
int toRead = Math.min(remaining, available);
dst.put(data, (int) position, toRead);
position += toRead;
return toRead;
}
@Override public long position() { return position; }
@Override public SeekableByteChannel position(long newPosition) { position = newPosition; return this; }
@Override public long size() { return data.length; }
@Override public boolean isOpen() { return isOpen; }
@Override public void close() { isOpen = false; }
@Override public int write(ByteBuffer src) { throw new UnsupportedOperationException(); }
@Override public SeekableByteChannel truncate(long size) { throw new UnsupportedOperationException(); }
}
}
// Read query results
byte[] queryResult = namespaceClient.queryTable(query);
try (BufferAllocator allocator = new RootAllocator();
ArrowFileReader reader = new ArrowFileReader(
new ByteArraySeekableByteChannel(queryResult), allocator)) {
ArrowFileReader reader = ArrowIpc.openFileReader(queryResult, allocator)) {
for (int i = 0; i < reader.getRecordBlocks().size(); i++) {
reader.loadRecordBatch(reader.getRecordBlocks().get(i));
+1 -1
View File
@@ -1,7 +1,7 @@
# Contributing to LanceDB Typescript
This document outlines the process for contributing to LanceDB Typescript.
For general contribution guidelines, see [CONTRIBUTING.md](../CONTRIBUTING.md).
For general contribution guidelines, see [CONTRIBUTING.md](https://github.com/lancedb/lancedb/blob/main/CONTRIBUTING.md).
## Project layout
+43
View File
@@ -83,6 +83,24 @@ Delete a branch.
***
### diff()
```ts
diff(fromBranch): Promise<BranchDiff>
```
Compare a branch against main without modifying either branch.
#### Parameters
* **fromBranch**: `string`
#### Returns
`Promise`&lt;[`BranchDiff`](../interfaces/BranchDiff.md)&gt;
***
### list()
```ts
@@ -94,3 +112,28 @@ List all branches, mapping name to branch metadata.
#### Returns
`Promise`&lt;`Record`&lt;`string`, [`BranchContents`](BranchContents.md)&gt;&gt;
***
### merge()
```ts
merge(fromBranch, dryRun): Promise<MergeBranchResult>
```
Merge a branch into main.
Set `dryRun` to `true` to preview the merge. A rejected merge resolves
with `status: "rejected"` instead of throwing.
#### Parameters
* **fromBranch**: `string`
Branch to merge from.
* **dryRun**: `boolean` = `false`
When true, only preview the merge. Defaults to false.
#### Returns
`Promise`&lt;[`MergeBranchResult`](../interfaces/MergeBranchResult.md)&gt;
+8 -9
View File
@@ -76,24 +76,23 @@ the query optimizer chooses a suboptimal path.
***
### useLsmWrite()
### useLsm()
```ts
useLsmWrite(useLsmWrite): MergeInsertBuilder
useLsm(enable): MergeInsertBuilder
```
Controls whether the merge uses the MemWAL LSM write path.
Control MemWAL routing for this merge.
By default (unset), a `mergeInsert` on a table with an LSM write spec is
routed through Lance's MemWAL shard writer, and a table without one uses
the standard path. Pass `false` to force the standard path even when a
spec is set. Pass `true` to require a spec — `mergeInsert` rejects if none
is installed.
routed through Lance's MemWAL shard writer, and a table without one uses the
standard path.
#### Parameters
* **useLsmWrite**: `boolean`
Whether to use the LSM write path.
* **enable**: `boolean`
`true` forces MemWAL routing and errors if the table has no
LSM write spec. `false` forces the standard write path even when a spec is set.
#### Returns
+36
View File
@@ -497,6 +497,42 @@ ArrowTable.
***
### useLsm()
```ts
useLsm(enable): this
```
Control MemWAL read routing for this query.
By default (unset), when the table carries a MemWAL write spec (see
[Table#setLsmWriteSpec](Table.md#setlsmwritespec)), reads are routed through the LSM scanner so
they also return data written via the `mergeInsert` LSM path that has not yet
been compacted into the base table (the active/frozen in-memory memtables and
the flushed generations), deduplicated by primary key; a table without a spec
reads the base table.
#### Parameters
* **enable**: `boolean`
`true` forces the LSM scanner and errors if the table has no
MemWAL write spec. `false` bypasses the MemWAL and reads the base table only,
even when a spec is present.
Note: the LSM scanner does not support every query shape (e.g. reranking,
hybrid search, `orderBy`). On a MemWAL table those shapes error unless
`useLsm(false)` is set, because a base-only read would silently exclude
un-compacted MemWAL data.
#### Returns
`this`
#### Inherited from
`StandardQueryBase.useLsm`
***
### where()
```ts
+23
View File
@@ -273,6 +273,29 @@ ArrowTable.
***
### useLsm()
```ts
useLsm(enable): this
```
Control MemWAL read routing for this take query.
`false` bypasses the MemWAL and reads the base table only — the escape hatch,
since take-by-row-id/offset is not supported on the LSM scanner and, on a
MemWAL table, auto-routes to it and errors otherwise.
#### Parameters
* **enable**: `boolean`
`false` reads the base table only.
#### Returns
`this`
***
### withRowId()
```ts
+36
View File
@@ -746,6 +746,42 @@ ArrowTable.
***
### useLsm()
```ts
useLsm(enable): this
```
Control MemWAL read routing for this query.
By default (unset), when the table carries a MemWAL write spec (see
[Table#setLsmWriteSpec](Table.md#setlsmwritespec)), reads are routed through the LSM scanner so
they also return data written via the `mergeInsert` LSM path that has not yet
been compacted into the base table (the active/frozen in-memory memtables and
the flushed generations), deduplicated by primary key; a table without a spec
reads the base table.
#### Parameters
* **enable**: `boolean`
`true` forces the LSM scanner and errors if the table has no
MemWAL write spec. `false` bypasses the MemWAL and reads the base table only,
even when a spec is present.
Note: the LSM scanner does not support every query shape (e.g. reranking,
hybrid search, `orderBy`). On a MemWAL table those shapes error unless
`useLsm(false)` is set, because a base-only read would silently exclude
un-compacted MemWAL data.
#### Returns
`this`
#### Inherited from
`StandardQueryBase.useLsm`
***
### where()
```ts
+8
View File
@@ -52,6 +52,11 @@
- [AddDataOptions](interfaces/AddDataOptions.md)
- [AddResult](interfaces/AddResult.md)
- [AlterColumnsResult](interfaces/AlterColumnsResult.md)
- [BranchColumnChange](interfaces/BranchColumnChange.md)
- [BranchColumnSummary](interfaces/BranchColumnSummary.md)
- [BranchDiff](interfaces/BranchDiff.md)
- [BranchIndexSummary](interfaces/BranchIndexSummary.md)
- [BranchRowCountSummary](interfaces/BranchRowCountSummary.md)
- [ClientConfig](interfaces/ClientConfig.md)
- [ColumnAlteration](interfaces/ColumnAlteration.md)
- [ColumnOrdering](interfaces/ColumnOrdering.md)
@@ -86,6 +91,9 @@
- [ListNamespacesOptions](interfaces/ListNamespacesOptions.md)
- [ListNamespacesResponse](interfaces/ListNamespacesResponse.md)
- [LsmWriteSpec](interfaces/LsmWriteSpec.md)
- [MergeBlocker](interfaces/MergeBlocker.md)
- [MergeBranchResult](interfaces/MergeBranchResult.md)
- [MergePreview](interfaces/MergePreview.md)
- [MergeResult](interfaces/MergeResult.md)
- [NativeOAuthConfig](interfaces/NativeOAuthConfig.md)
- [OAuthConfig](interfaces/OAuthConfig.md)
@@ -0,0 +1,33 @@
[**@lancedb/lancedb**](../README.md) • **Docs**
***
[@lancedb/lancedb](../globals.md) / BranchColumnChange
# Interface: BranchColumnChange
A column whose definition differs between main and the branch.
## Properties
### branch
```ts
branch: BranchColumnSummary;
```
***
### main
```ts
main: BranchColumnSummary;
```
***
### name
```ts
name: string;
```
@@ -0,0 +1,33 @@
[**@lancedb/lancedb**](../README.md) • **Docs**
***
[@lancedb/lancedb](../globals.md) / BranchColumnSummary
# Interface: BranchColumnSummary
Summary of a column in a branch diff.
## Properties
### dataType
```ts
dataType: string;
```
***
### name
```ts
name: string;
```
***
### nullable
```ts
nullable: boolean;
```
+129
View File
@@ -0,0 +1,129 @@
[**@lancedb/lancedb**](../README.md) • **Docs**
***
[@lancedb/lancedb](../globals.md) / BranchDiff
# Interface: BranchDiff
Read-only comparison of a branch against main.
## Properties
### addedColumns
```ts
addedColumns: BranchColumnSummary[];
```
***
### addedIndexes
```ts
addedIndexes: BranchIndexSummary[];
```
***
### baseMoved
```ts
baseMoved: boolean;
```
***
### branchVersion
```ts
branchVersion: number;
```
***
### changedColumns
```ts
changedColumns: BranchColumnChange[];
```
***
### fromBranch
```ts
fromBranch: string;
```
***
### mainVersion
```ts
mainVersion: number;
```
***
### mergeBlockers
```ts
mergeBlockers: MergeBlocker[];
```
***
### mergeable
```ts
mergeable: boolean;
```
***
### parentVersion
```ts
parentVersion: number;
```
***
### removedColumns
```ts
removedColumns: BranchColumnSummary[];
```
***
### removedIndexes
```ts
removedIndexes: BranchIndexSummary[];
```
***
### rowCountBranch
```ts
rowCountBranch: number;
```
***
### rowCountMain
```ts
rowCountMain: number;
```
***
### rowSummary
```ts
rowSummary: BranchRowCountSummary;
```
@@ -0,0 +1,41 @@
[**@lancedb/lancedb**](../README.md) • **Docs**
***
[@lancedb/lancedb](../globals.md) / BranchIndexSummary
# Interface: BranchIndexSummary
Summary of an index in a branch diff.
## Properties
### columns
```ts
columns: string[];
```
***
### indexName
```ts
indexName: string;
```
***
### indexType?
```ts
optional indexType: string;
```
***
### status
```ts
status: string;
```
@@ -0,0 +1,57 @@
[**@lancedb/lancedb**](../README.md) • **Docs**
***
[@lancedb/lancedb](../globals.md) / BranchRowCountSummary
# Interface: BranchRowCountSummary
Row-level comparison between main and the branch.
## Properties
### deltaAvailable
```ts
deltaAvailable: boolean;
```
***
### inputsChanged
```ts
inputsChanged: number;
```
***
### newOnBase
```ts
newOnBase: number;
```
***
### newOnBranch
```ts
newOnBranch: number;
```
***
### staleRecompute
```ts
staleRecompute: number;
```
***
### unchanged
```ts
unchanged: number;
```
+28
View File
@@ -43,6 +43,34 @@ The following tokenizers are available:
***
### blockSize?
```ts
optional blockSize: 128 | 256;
```
Number of documents per compressed posting block.
The default is 128. Supported values are 128 and 256. A value of 256 uses
the experimental FTS V3 format and may introduce breaking changes.
***
### customStopWords?
```ts
optional customStopWords: string[];
```
Custom stop words that replace the built-in list for `language`.
This option only affects tokenization when `removeStopWords` is true.
`undefined` keeps the built-in language list. An empty array explicitly
replaces it with no stop words.
***
### language?
```ts
+25
View File
@@ -0,0 +1,25 @@
[**@lancedb/lancedb**](../README.md) • **Docs**
***
[@lancedb/lancedb](../globals.md) / MergeBlocker
# Interface: MergeBlocker
A reason why a branch cannot currently be merged.
## Properties
### code
```ts
code: string;
```
***
### message
```ts
message: string;
```
@@ -0,0 +1,46 @@
[**@lancedb/lancedb**](../README.md) • **Docs**
***
[@lancedb/lancedb](../globals.md) / MergeBranchResult
# Interface: MergeBranchResult
Result of previewing or attempting a branch merge.
## Properties
### diff
```ts
diff: BranchDiff;
```
***
### mainVersionAfter?
```ts
optional mainVersionAfter: number;
```
***
### preview
```ts
preview: MergePreview;
```
***
### status
```ts
status:
| "unknown"
| "rejected"
| "ready"
| "notImplemented"
| "merged";
```
+17
View File
@@ -0,0 +1,17 @@
[**@lancedb/lancedb**](../README.md) • **Docs**
***
[@lancedb/lancedb](../globals.md) / MergePreview
# Interface: MergePreview
Changes that would be, or were, promoted by a branch merge.
## Properties
### promotedColumns
```ts
promotedColumns: string[];
```
+15
View File
@@ -30,6 +30,21 @@ The tokenizer to use. The default is "simple".
***
### customStopWords?
```ts
optional customStopWords: string[];
```
Custom stop words that replace the built-in list for `language`.
This option only affects tokenization when `removeStopWords` is true.
`undefined` keeps the built-in language list. An empty array explicitly
replaces it with no stop words.
***
### language?
```ts
+141 -52
View File
@@ -26,6 +26,18 @@ is also an [asynchronous API client](#connections-asynchronous).
::: lancedb.db.DBConnection
::: lancedb.Session
## Namespaces (Synchronous)
A namespace-backed connection resolves tables through a
[Lance namespace](https://lancedb.github.io/lance-namespace/) service instead of
listing a storage directory.
::: lancedb.connect_namespace
::: lancedb.namespace.LanceNamespaceDBConnection
## Tables (Synchronous)
::: lancedb.table.Table
@@ -34,8 +46,12 @@ is also an [asynchronous API client](#connections-asynchronous).
::: lancedb.table.FragmentSummaryStats
::: lancedb.table.TableStatistics
::: lancedb.table.Tags
::: lancedb.table.Branches
## Expressions
Type-safe expression builder for filters and projections. Use these instead
@@ -62,29 +78,46 @@ of raw SQL strings with [where][lancedb.query.LanceQueryBuilder.where] and
::: lancedb.query.LanceHybridQueryBuilder
::: lancedb.query.LanceEmptyQueryBuilder
::: lancedb.query.LanceTakeQueryBuilder
## Full text queries
Structured full text queries can be passed to
[Table.search][lancedb.table.Table.search] or
[AsyncTable.search][lancedb.table.AsyncTable.search] in place of a query string,
and combined with [BooleanQuery][lancedb.query.BooleanQuery].
::: lancedb.query.FullTextQuery
::: lancedb.query.MatchQuery
::: lancedb.query.PhraseQuery
::: lancedb.query.BoostQuery
::: lancedb.query.MultiMatchQuery
::: lancedb.query.BooleanQuery
::: lancedb.query.FullTextOperator
::: lancedb.query.Occur
## Embeddings
::: lancedb.embeddings.registry.EmbeddingFunctionRegistry
::: lancedb.embeddings.base.EmbeddingFunctionConfig
::: lancedb.embeddings.base.EmbeddingFunction
::: lancedb.embeddings.base.TextEmbeddingFunction
::: lancedb.embeddings.sentence_transformers.SentenceTransformerEmbeddings
::: lancedb.embeddings.openai.OpenAIEmbeddings
::: lancedb.embeddings.open_clip.OpenClipEmbeddings
::: lancedb.embeddings
options:
show_root_heading: false
show_root_toc_entry: false
## Remote configuration
::: lancedb.remote.ClientConfig
::: lancedb.remote.TimeoutConfig
::: lancedb.remote.RetryConfig
::: lancedb.remote
options:
show_root_heading: false
show_root_toc_entry: false
## Context
@@ -94,11 +127,50 @@ of raw SQL strings with [where][lancedb.query.LanceQueryBuilder.where] and
## Full text search
Use [lancedb.table.Table.create_fts_index][] for the synchronous API or
[lancedb.table.AsyncTable.create_index][] with [lancedb.index.FTS][] for the
asynchronous API.
Pass `custom_stop_words` to [lancedb.index.FTS][]:
::: lancedb.index.FTS
```python
from lancedb.index import FTS
table.create_index(
"text",
config=FTS(remove_stop_words=True, custom_stop_words=["acme", "internal"]),
)
```
The list replaces the built-in stop words and is used only when
`remove_stop_words=True`:
- `custom_stop_words=None` uses the built-in list for `language`.
- `custom_stop_words=[]` removes no words.
- Values are passed through without trimming, lowercasing, or other rewriting.
The same option is available on `lancedb.tokenize(...)` and the deprecated
[lancedb.table.Table.create_fts_index][] compatibility helper:
```python
import lancedb
tokens = list(lancedb.tokenize("acme makes searchable data",
custom_stop_words=["acme"]))
```
::: lancedb.tokenize
::: lancedb.FtsToken
## Blobs
Blob columns store large binary values out of line so they can be read lazily
instead of being materialized with the rest of the row.
::: lancedb.blob
::: lancedb.BlobType
::: lancedb._blob.BlobFile
options:
show_root_full_path: false
## Utilities
@@ -106,6 +178,14 @@ asynchronous API.
::: lancedb.merge.LanceMergeInsertBuilder
::: lancedb.otel.instrument_lancedb_metrics
## Exceptions
::: lancedb.exceptions.MissingValueError
::: lancedb.exceptions.MissingColumnError
## Integrations
## Pydantic
@@ -114,19 +194,30 @@ asynchronous API.
::: lancedb.pydantic.vector
::: lancedb.pydantic.Vector
::: lancedb.pydantic.MultiVector
::: lancedb.pydantic.LanceModel
## PyTorch
::: lancedb.streaming.StreamingDataset
::: lancedb.permutation.permutation_builder
::: lancedb.permutation.PermutationBuilder
::: lancedb.permutation.Permutation
::: lancedb.permutation.Transforms
## Reranking
::: lancedb.rerankers.linear_combination.LinearCombinationReranker
::: lancedb.rerankers.cohere.CohereReranker
::: lancedb.rerankers.colbert.ColbertReranker
::: lancedb.rerankers.cross_encoder.CrossEncoderReranker
::: lancedb.rerankers.openai.OpenaiReranker
::: lancedb.rerankers
options:
show_root_heading: false
show_root_toc_entry: false
## Connections (Asynchronous)
@@ -137,6 +228,12 @@ can be used to create, list, or open tables.
::: lancedb.db.AsyncConnection
## Namespaces (Asynchronous)
::: lancedb.connect_namespace_async
::: lancedb.namespace.AsyncLanceNamespaceDBConnection
## Tables (Asynchronous)
Table hold your actual data as a collection of records / rows.
@@ -145,32 +242,20 @@ Table hold your actual data as a collection of records / rows.
::: lancedb.table.AsyncTags
::: lancedb.table.AsyncBranches
## Indices (Asynchronous)
Indices can be created on a table to speed up queries. This section
lists the indices that LanceDb supports.
::: lancedb.index.BTree
::: lancedb.index.Bitmap
::: lancedb.index.LabelList
::: lancedb.index.FTS
::: lancedb.index.IvfPq
::: lancedb.index.HnswPq
::: lancedb.index.HnswSq
::: lancedb.index.IvfFlat
::: lancedb.index.IvfSq
::: lancedb.index.IvfRq
::: lancedb.index.HnswFlat
::: lancedb.index
options:
show_root_heading: false
show_root_toc_entry: false
# `lang_mapping` is defined in the module rather than imported, so it is
# picked up despite not being in `__all__`. It is an internal lookup table.
filters: ["!^_", "!^lang_mapping$"]
::: lancedb.table.IndexStatistics
@@ -198,3 +283,7 @@ rows nearest to a query vector and can be created with the
::: lancedb.query.AsyncHybridQuery
options:
inherited_members: true
::: lancedb.query.AsyncTakeQuery
options:
inherited_members: true
+1 -1
View File
@@ -8,7 +8,7 @@
<parent>
<groupId>com.lancedb</groupId>
<artifactId>lancedb-parent</artifactId>
<version>0.32.0-beta.2</version>
<version>0.37.1-beta.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
+2 -2
View File
@@ -6,7 +6,7 @@
<groupId>com.lancedb</groupId>
<artifactId>lancedb-parent</artifactId>
<version>0.32.0-beta.2</version>
<version>0.37.1-beta.0</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>LanceDB Java SDK Parent POM</description>
@@ -28,7 +28,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<arrow.version>15.0.0</arrow.version>
<lance-core.version>9.0.0-rc.1</lance-core.version>
<lance-core.version>10.0.0-beta.5</lance-core.version>
<spotless.skip>false</spotless.skip>
<spotless.version>2.30.0</spotless.version>
<spotless.java.googlejavaformat.version>1.7</spotless.java.googlejavaformat.version>
+1 -1
View File
@@ -1,7 +1,7 @@
# Contributing to LanceDB Typescript
This document outlines the process for contributing to LanceDB Typescript.
For general contribution guidelines, see [CONTRIBUTING.md](../CONTRIBUTING.md).
For general contribution guidelines, see [CONTRIBUTING.md](https://github.com/lancedb/lancedb/blob/main/CONTRIBUTING.md).
## Project layout
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "lancedb-nodejs"
edition.workspace = true
version = "0.32.0-beta.2"
version = "0.37.1-beta.0"
publish = false
license.workspace = true
description.workspace = true
+84
View File
@@ -52,6 +52,7 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
Float64,
Struct,
List,
Map_,
Int16,
Int32,
Int64,
@@ -69,6 +70,30 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
type Schema = ApacheArrow["Schema"];
type Table = ApacheArrow["Table"];
function expectValidMapField(
// biome-ignore lint/suspicious/noExplicitAny: Arrow Field types vary across supported versions
field: any,
): void {
expect(DataType.isMap(field.type)).toBe(true);
expect(field.type.keysSorted).toBe(true);
expect(field.type.children).toHaveLength(1);
const entries = field.type.children[0];
expect(entries.name).toBe("entries");
expect(entries.nullable).toBe(false);
expect(DataType.isStruct(entries.type)).toBe(true);
expect(entries.type.children).toHaveLength(2);
const [key, value] = entries.type.children;
expect([key.name, value.name]).toEqual(["key", "value"]);
expect(key.nullable).toBe(false);
expect(DataType.isUtf8(key.type)).toBe(true);
expect(value.nullable).toBe(true);
expect(DataType.isInt(value.type)).toBe(true);
expect(value.type.bitWidth).toBe(32);
expect(value.type.isSigned).toBe(true);
}
// Helper method to verify various ways to create a table
async function checkTableCreation(
tableCreationMethod: (
@@ -938,6 +963,65 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
false,
);
});
it("will make an empty table with a Map field", async function () {
const schema = new Schema([
new Field(
"attributes",
new Map_(
new Field(
"entries",
new Struct([
new Field("key", new Utf8(), false),
new Field("value", new Int32(), true),
]),
false,
),
true,
),
),
]);
const table = makeEmptyTable(schema);
expectValidMapField(table.schema.fields[0]);
const buffer = await fromTableToBuffer(table);
const roundTripped = tableFromIPC(buffer);
expectValidMapField(roundTripped.schema.fields[0]);
});
it("preserves string schema metadata", function () {
const metadata = new Map([["source", "fixture"]]);
const schema = new Schema(
[new Field("value", new Int32(), true)],
metadata,
);
expect(makeEmptyTable(schema).schema.metadata.get("source")).toBe(
"fixture",
);
});
it.each([
["non-string keys", new Map<unknown, unknown>([[42, "fixture"]])],
["non-string values", new Map<unknown, unknown>([["source", 42]])],
[
"non-string keys and values",
new Map<unknown, unknown>([[42, false]]),
],
])("rejects schema metadata with %s", function (_, metadataLike) {
const metadata = metadataLike as unknown as Map<string, string>;
const schema = new Schema(
[new Field("value", new Int32(), true)],
metadata,
);
expect(() => makeEmptyTable(schema)).toThrow(
"Expected metadata, if present, to be a Map<string, string> but it had non-string keys or values",
);
});
});
describe("when using two versions of arrow", function () {
+161
View File
@@ -15,6 +15,7 @@ import {
OAuthHeaderProvider,
StaticHeaderProvider,
} from "../lancedb/header";
import { Index } from "../lancedb/indices";
// Test-only header providers
class CustomProvider extends HeaderProvider {
@@ -225,6 +226,166 @@ describe("remote connection", () => {
);
});
it("sends FTS options to remote tables", async () => {
let createIndexBody: Record<string, unknown> | undefined;
await withMockDatabase(
(req, res) => {
const path = req.url ?? "";
if (path.endsWith("/describe/")) {
res.writeHead(200, { "Content-Type": "application/json" }).end(
JSON.stringify({
name: "t",
version: 1,
schema: {
fields: [
{ name: "text", type: { type: "string" }, nullable: false },
],
},
}),
);
return;
}
if (path.endsWith("/create_index/")) {
let raw = "";
req.on("data", (chunk) => {
raw += chunk;
});
req.on("end", () => {
createIndexBody = JSON.parse(raw);
res.writeHead(200).end();
});
return;
}
res.writeHead(404).end();
},
async (db) => {
const table = await db.openTable("t");
await table.createIndex("text", {
config: Index.fts({
blockSize: 256,
removeStopWords: true,
customStopWords: ["the"],
}),
});
},
);
expect(createIndexBody?.["column"]).toBe("text");
expect(createIndexBody?.["index_type"]).toBe("FTS");
expect(createIndexBody?.["block_size"]).toBe(256);
expect(createIndexBody?.["custom_stop_words"]).toEqual(["the"]);
});
it("diffs and merges remote branches", async () => {
const sampleDiff = {
fromBranch: "exp",
parentVersion: 1,
mainVersion: 2,
branchVersion: 3,
baseMoved: false,
rowCountMain: 3,
rowCountBranch: 3,
rowSummary: {
unchanged: 3,
newOnBase: 0,
newOnBranch: 0,
staleRecompute: 0,
inputsChanged: 0,
deltaAvailable: false,
},
addedColumns: [{ name: "tag", dataType: "utf8", nullable: true }],
removedColumns: [],
changedColumns: [],
addedIndexes: [],
removedIndexes: [],
mergeable: true,
mergeBlockers: [],
};
const mergeBodies: Record<string, unknown>[] = [];
await withMockDatabase(
(req, res) => {
const path = req.url ?? "";
if (path.endsWith("/describe/")) {
res.writeHead(200, { "Content-Type": "application/json" }).end(
JSON.stringify({
name: "t",
version: 2,
schema: { fields: [] },
}),
);
return;
}
let raw = "";
req.on("data", (chunk) => {
raw += chunk;
});
req.on("end", () => {
const body = raw ? JSON.parse(raw) : {};
if (path.endsWith("/branches/diff/")) {
// biome-ignore lint/style/useNamingConvention: snake_case mandated by the server wire format
expect(body).toEqual({ from_branch: "exp" });
res
.writeHead(200, { "Content-Type": "application/json" })
.end(JSON.stringify(sampleDiff));
return;
}
if (path.endsWith("/branches/merge/")) {
mergeBodies.push(body);
const dryRun = body["dry_run"] === true;
const response = {
status: dryRun ? "ready" : "rejected",
diff: dryRun
? sampleDiff
: {
...sampleDiff,
mergeable: false,
mergeBlockers: [
{ code: "baseMoved", message: "main has advanced" },
],
},
preview: { promotedColumns: dryRun ? ["tag"] : [] },
};
res
.writeHead(dryRun ? 200 : 409, {
"Content-Type": "application/json",
})
.end(JSON.stringify(response));
return;
}
res.writeHead(404).end();
});
},
async (db) => {
const table = await db.openTable("t");
const branches = await table.branches();
await expect(branches.diff("exp")).resolves.toEqual(sampleDiff);
const rejected = await branches.merge("exp");
expect(rejected.status).toBe("rejected");
expect(rejected.diff.mergeBlockers).toEqual([
{ code: "baseMoved", message: "main has advanced" },
]);
const preview = await branches.merge("exp", true);
expect(preview.status).toBe("ready");
expect(preview.preview.promotedColumns).toEqual(["tag"]);
},
);
expect(mergeBodies).toEqual([
// biome-ignore lint/style/useNamingConvention: snake_case mandated by the server wire format
{ from_branch: "exp", dry_run: false },
// biome-ignore lint/style/useNamingConvention: snake_case mandated by the server wire format
{ from_branch: "exp", dry_run: true },
]);
});
describe("TlsConfig", () => {
it("should create TlsConfig with all fields", () => {
const tlsConfig: TlsConfig = {
+12 -1
View File
@@ -2,7 +2,7 @@
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
import * as arrow from "../lancedb/arrow";
import { sanitizeField, sanitizeType } from "../lancedb/sanitize";
import { sanitizeField, sanitizeMap, sanitizeType } from "../lancedb/sanitize";
describe("sanitize", function () {
describe("sanitizeType function", function () {
@@ -181,4 +181,15 @@ describe("sanitize", function () {
);
});
});
describe("sanitizeMap function", function () {
it.each([
["no children", []],
["two children", [{}, {}]],
])("should reject a Map type with %s", function (_, children) {
expect(() => sanitizeMap({ children, keysSorted: false })).toThrow(
"Expected a Map type to have exactly one child",
);
});
});
});
+80 -2
View File
@@ -527,6 +527,14 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
);
});
it("should expose useLsm on takeRowIds as the base-only escape hatch", async () => {
await table.add([{ id: 1 }, { id: 2 }, { id: 3 }]);
// useLsm(false) is reachable on TakeQuery (the escape hatch for MemWAL tables,
// where take-by-row-id auto-routes to the LSM scanner and is rejected).
const res = await table.takeRowIds([0, 2]).useLsm(false).toArray();
expect(res.map((r) => r.id)).toEqual([1, 3]);
});
it("should throw for negative number in takeRowIds", () => {
expect(() => table.takeRowIds([-1])).toThrow("Row id cannot be negative");
expect(() => table.takeRowIds([0, -5, 2])).toThrow(
@@ -2527,6 +2535,35 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
expect(results3.length).toBe(1);
});
test("full text search with custom posting block size", async () => {
const db = await connect(tmpDir.name);
const data = [
{ text: "hello world", vector: [0.1, 0.2, 0.3] },
{ text: "goodbye world", vector: [0.4, 0.5, 0.6] },
];
const table = await db.createTable("test", data);
await table.createIndex("text", {
config: Index.fts({ blockSize: 256 }),
});
const index = (await table.listIndices()).find(
(index) => index.indexType === "FTS",
);
expect(index?.indexVersion).toBe(3);
expect(
(index?.indexDetails as Record<string, unknown>)["block_size"],
).toBe(256);
const results = await table.search("hello").toArray();
expect(results[0].text).toBe(data[0].text);
});
test("rejects invalid full text posting block size", () => {
expect(() => Index.fts({ blockSize: 129 as 128 | 256 })).toThrow(
"128 or 256",
);
});
test("full text search without lowercase", async () => {
const db = await connect(tmpDir.name);
const data = [
@@ -2732,6 +2769,15 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
},
);
test("tokenize supports custom stop words", async () => {
const tokens = await tokenize("the lance data", {
stem: false,
removeStopWords: true,
customStopWords: ["lance"],
});
expect(tokens.map((token) => token.text)).toEqual(["the", "data"]);
});
describe("when calling explainPlan", () => {
let tmpDir: tmp.DirResult;
let table: Table;
@@ -3170,14 +3216,14 @@ describe("LSM merge insert", () => {
await table.closeLsmWriters();
});
it("falls back to the standard path with useLsmWrite(false)", async () => {
it("falls back to the standard path with useLsm(false)", async () => {
const conn = await connect(tmpDir.name);
const table = await bucketTable(conn);
const res = await table
.mergeInsert("id")
.whenNotMatchedInsertAll()
.useLsmWrite(false)
.useLsm(false)
.execute([
{ id: "b", value: 9 },
{ id: "e", value: 5 },
@@ -3211,4 +3257,36 @@ describe("LSM merge insert", () => {
.execute([{ id: "g", value: 7 }]),
).rejects.toThrow();
});
it("auto-routes reads through the MemWAL scanner", async () => {
const conn = await connect(tmpDir.name);
const table = await bucketTable(conn); // base ids "a", "b"
await table
.mergeInsert("id")
.whenMatchedUpdateAll()
.whenNotMatchedInsertAll()
.execute([{ id: "c", value: 3 }]);
// Default read auto-routes and includes the active memtable row.
const lsm = await table.query().toArray();
expect(lsm.map((r) => r.id).sort()).toEqual(["a", "b", "c"]);
// useLsm(false) bypasses the MemWAL and reads the base table only.
const baseOnly = await table.query().useLsm(false).toArray();
expect(baseOnly.map((r) => r.id).sort()).toEqual(["a", "b"]);
});
it("reads the base table when no LSM spec is installed", async () => {
const conn = await connect(tmpDir.name);
const table = await conn.createEmptyTable(
"plain",
new arrow.Schema([new arrow.Field("id", new arrow.Utf8(), false)]),
);
// No spec: default read and useLsm(false) both succeed against the base table.
await expect(table.query().toArray()).resolves.toBeDefined();
await expect(table.query().useLsm(false).toArray()).resolves.toBeDefined();
// useLsm(true) demands MemWAL routing; without a spec it errors.
await expect(table.query().useLsm(true).toArray()).rejects.toThrow();
});
});
+7 -1
View File
@@ -29,8 +29,14 @@ test("full text search", async () => {
const tbl = await db.createTable("myVectors", data, { mode: "overwrite" });
await tbl.createIndex("doc", {
config: lancedb.Index.fts(),
config: lancedb.Index.fts({
stem: false,
removeStopWords: true,
customStopWords: ["banana"],
}),
});
const tokens = await tbl.tokenize("apple banana", { column: "doc" });
expect(tokens.map((token) => token.text)).toEqual(["apple"]);
// --8<-- [start:full_text_search]
const result = await tbl
+19
View File
@@ -124,6 +124,14 @@ export {
export {
Table,
Branches,
BranchColumnSummary,
BranchColumnChange,
BranchIndexSummary,
BranchRowCountSummary,
MergeBlocker,
BranchDiff,
MergePreview,
MergeBranchResult,
AddDataOptions,
UpdateOptions,
OptimizeOptions,
@@ -186,6 +194,16 @@ export interface TokenizeOptions {
/** Whether to remove stop words. */
removeStopWords?: boolean;
/**
* Custom stop words that replace the built-in list for `language`.
*
* This option only affects tokenization when `removeStopWords` is true.
*
* `undefined` keeps the built-in language list. An empty array explicitly
* replaces it with no stop words.
*/
customStopWords?: string[];
/** Whether to fold ASCII characters. */
asciiFolding?: boolean;
@@ -217,6 +235,7 @@ export async function tokenize(
options?.lowercase,
options?.stem,
options?.removeStopWords,
options?.customStopWords,
options?.asciiFolding,
options?.ngramMinLength,
options?.ngramMaxLength,
+20
View File
@@ -553,6 +553,16 @@ export interface FtsOptions {
*/
removeStopWords?: boolean;
/**
* Custom stop words that replace the built-in list for `language`.
*
* This option only affects tokenization when `removeStopWords` is true.
*
* `undefined` keeps the built-in language list. An empty array explicitly
* replaces it with no stop words.
*/
customStopWords?: string[];
/**
* whether to remove punctuation
*/
@@ -572,6 +582,14 @@ export interface FtsOptions {
* whether to only index the prefix of the token for ngram tokenizer
*/
prefixOnly?: boolean;
/**
* Number of documents per compressed posting block.
*
* The default is 128. Supported values are 128 and 256. A value of 256 uses
* the experimental FTS V3 format and may introduce breaking changes.
*/
blockSize?: 128 | 256;
}
export class Index {
@@ -747,10 +765,12 @@ export class Index {
options?.lowercase,
options?.stem,
options?.removeStopWords,
options?.customStopWords,
options?.asciiFolding,
options?.ngramMinLength,
options?.ngramMaxLength,
options?.prefixOnly,
options?.blockSize,
),
);
}
+7 -11
View File
@@ -88,21 +88,17 @@ export class MergeInsertBuilder {
);
}
/**
* Controls whether the merge uses the MemWAL LSM write path.
* Control MemWAL routing for this merge.
*
* By default (unset), a `mergeInsert` on a table with an LSM write spec is
* routed through Lance's MemWAL shard writer, and a table without one uses
* the standard path. Pass `false` to force the standard path even when a
* spec is set. Pass `true` to require a spec `mergeInsert` rejects if none
* is installed.
* routed through Lance's MemWAL shard writer, and a table without one uses the
* standard path.
*
* @param useLsmWrite - Whether to use the LSM write path.
* @param enable - `true` forces MemWAL routing and errors if the table has no
* LSM write spec. `false` forces the standard write path even when a spec is set.
*/
useLsmWrite(useLsmWrite: boolean): MergeInsertBuilder {
return new MergeInsertBuilder(
this.#native.useLsmWrite(useLsmWrite),
this.#schema,
);
useLsm(enable: boolean): MergeInsertBuilder {
return new MergeInsertBuilder(this.#native.useLsm(enable), this.#schema);
}
/**
* Controls how an LSM merge checks that its input targets a single shard.
+38
View File
@@ -460,6 +460,30 @@ export class StandardQueryBase<
this.doCall((inner: NativeQueryType) => inner.fastSearch());
return this;
}
/**
* Control MemWAL read routing for this query.
*
* By default (unset), when the table carries a MemWAL write spec (see
* {@link Table#setLsmWriteSpec}), reads are routed through the LSM scanner so
* they also return data written via the `mergeInsert` LSM path that has not yet
* been compacted into the base table (the active/frozen in-memory memtables and
* the flushed generations), deduplicated by primary key; a table without a spec
* reads the base table.
*
* @param enable - `true` forces the LSM scanner and errors if the table has no
* MemWAL write spec. `false` bypasses the MemWAL and reads the base table only,
* even when a spec is present.
*
* Note: the LSM scanner does not support every query shape (e.g. reranking,
* hybrid search, `orderBy`). On a MemWAL table those shapes error unless
* `useLsm(false)` is set, because a base-only read would silently exclude
* un-compacted MemWAL data.
*/
useLsm(enable: boolean): this {
this.doCall((inner: NativeQueryType) => inner.useLsm(enable));
return this;
}
}
/**
@@ -748,6 +772,20 @@ export class TakeQuery extends QueryBase<NativeTakeQuery> {
constructor(inner: NativeTakeQuery) {
super(inner);
}
/**
* Control MemWAL read routing for this take query.
*
* `false` bypasses the MemWAL and reads the base table only the escape hatch,
* since take-by-row-id/offset is not supported on the LSM scanner and, on a
* MemWAL table, auto-routes to it and errors otherwise.
*
* @param enable - `false` reads the base table only.
*/
useLsm(enable: boolean): this {
this.doCall((inner: NativeTakeQuery) => inner.useLsm(enable));
return this;
}
}
/** A builder for LanceDB queries.
+5 -6
View File
@@ -84,7 +84,7 @@ export function sanitizeMetadata(
throw Error("Expected metadata, if present, to be a Map<string, string>");
}
for (const item of metadataLike) {
if (!(typeof item[0] === "string" || !(typeof item[1] === "string"))) {
if (typeof item[0] !== "string" || typeof item[1] !== "string") {
throw Error(
"Expected metadata, if present, to be a Map<string, string> but it had non-string keys or values",
);
@@ -288,12 +288,11 @@ export function sanitizeMap(typeLike: object) {
if (!("keysSorted" in typeLike) || typeof typeLike.keysSorted !== "boolean") {
throw Error("Expected a Map type to have a `keysSorted` property");
}
if (typeLike.children.length !== 1) {
throw Error("Expected a Map type to have exactly one child");
}
return new Map_(
// biome-ignore lint/suspicious/noExplicitAny: skip
typeLike.children.map((field) => sanitizeField(field)) as any,
typeLike.keysSorted,
);
return new Map_(sanitizeField(typeLike.children[0]), typeLike.keysSorted);
}
export function sanitizeDuration(typeLike: object) {
+94
View File
@@ -1329,6 +1329,76 @@ export interface FieldMetadataUpdate {
replace?: boolean;
}
/** Summary of a column in a branch diff. */
export interface BranchColumnSummary {
name: string;
dataType: string;
nullable: boolean;
}
/** A column whose definition differs between main and the branch. */
export interface BranchColumnChange {
name: string;
main: BranchColumnSummary;
branch: BranchColumnSummary;
}
/** Summary of an index in a branch diff. */
export interface BranchIndexSummary {
indexName: string;
columns: string[];
indexType?: string;
status: string;
}
/** Row-level comparison between main and the branch. */
export interface BranchRowCountSummary {
unchanged: number;
newOnBase: number;
newOnBranch: number;
staleRecompute: number;
inputsChanged: number;
deltaAvailable: boolean;
}
/** A reason why a branch cannot currently be merged. */
export interface MergeBlocker {
code: string;
message: string;
}
/** Read-only comparison of a branch against main. */
export interface BranchDiff {
fromBranch: string;
parentVersion: number;
mainVersion: number;
branchVersion: number;
baseMoved: boolean;
rowCountMain: number;
rowCountBranch: number;
rowSummary: BranchRowCountSummary;
addedColumns: BranchColumnSummary[];
removedColumns: BranchColumnSummary[];
changedColumns: BranchColumnChange[];
addedIndexes: BranchIndexSummary[];
removedIndexes: BranchIndexSummary[];
mergeable: boolean;
mergeBlockers: MergeBlocker[];
}
/** Changes that would be, or were, promoted by a branch merge. */
export interface MergePreview {
promotedColumns: string[];
}
/** Result of previewing or attempting a branch merge. */
export interface MergeBranchResult {
status: "ready" | "rejected" | "notImplemented" | "merged" | "unknown";
diff: BranchDiff;
preview: MergePreview;
mainVersionAfter?: number;
}
/**
* Branch manager for a {@link Table}.
*
@@ -1381,4 +1451,28 @@ export class Branches {
async delete(name: string): Promise<void> {
return await this.#inner.delete(name);
}
/** Compare a branch against main without modifying either branch. */
async diff(fromBranch: string): Promise<BranchDiff> {
return (await this.#inner.diff(fromBranch)) as unknown as BranchDiff;
}
/**
* Merge a branch into main.
*
* Set `dryRun` to `true` to preview the merge. A rejected merge resolves
* with `status: "rejected"` instead of throwing.
*
* @param fromBranch Branch to merge from.
* @param dryRun When true, only preview the merge. Defaults to false.
*/
async merge(
fromBranch: string,
dryRun: boolean = false,
): Promise<MergeBranchResult> {
return (await this.#inner.merge(
fromBranch,
dryRun,
)) as unknown as MergeBranchResult;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lancedb/lancedb-darwin-arm64",
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"os": ["darwin"],
"cpu": ["arm64"],
"main": "lancedb.darwin-arm64.node",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lancedb/lancedb-linux-arm64-gnu",
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"os": ["linux"],
"cpu": ["arm64"],
"main": "lancedb.linux-arm64-gnu.node",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lancedb/lancedb-linux-arm64-musl",
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"os": ["linux"],
"cpu": ["arm64"],
"main": "lancedb.linux-arm64-musl.node",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lancedb/lancedb-linux-x64-gnu",
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"os": ["linux"],
"cpu": ["x64"],
"main": "lancedb.linux-x64-gnu.node",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lancedb/lancedb-linux-x64-musl",
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"os": ["linux"],
"cpu": ["x64"],
"main": "lancedb.linux-x64-musl.node",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lancedb/lancedb-win32-arm64-msvc",
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"os": [
"win32"
],
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lancedb/lancedb-win32-x64-msvc",
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"os": ["win32"],
"cpu": ["x64"],
"main": "lancedb.win32-x64-msvc.node",
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@lancedb/lancedb",
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@lancedb/lancedb",
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"cpu": [
"x64",
"arm64"
+1 -1
View File
@@ -11,7 +11,7 @@
"ann"
],
"private": false,
"version": "0.32.0-beta.2",
"version": "0.37.1-beta.0",
"main": "dist/index.js",
"exports": {
".": "./dist/index.js",
+14 -4
View File
@@ -43,6 +43,7 @@ pub fn tokenize(
lower_case: Option<bool>,
stem: Option<bool>,
remove_stop_words: Option<bool>,
custom_stop_words: Option<Vec<String>>,
ascii_folding: Option<bool>,
ngram_min_length: Option<u32>,
ngram_max_length: Option<u32>,
@@ -72,6 +73,7 @@ pub fn tokenize(
if let Some(remove_stop_words) = remove_stop_words {
opts = opts.remove_stop_words(remove_stop_words);
}
opts = opts.custom_stop_words(custom_stop_words);
if let Some(ascii_folding) = ascii_folding {
opts = opts.ascii_folding(ascii_folding);
}
@@ -222,11 +224,13 @@ impl Index {
lower_case: Option<bool>,
stem: Option<bool>,
remove_stop_words: Option<bool>,
custom_stop_words: Option<Vec<String>>,
ascii_folding: Option<bool>,
ngram_min_length: Option<u32>,
ngram_max_length: Option<u32>,
prefix_only: Option<bool>,
) -> Self {
block_size: Option<u32>,
) -> napi::Result<Self> {
let mut opts = FtsIndexBuilder::default();
if let Some(with_position) = with_position {
opts = opts.with_position(with_position);
@@ -249,6 +253,7 @@ impl Index {
if let Some(remove_stop_words) = remove_stop_words {
opts = opts.remove_stop_words(remove_stop_words);
}
opts = opts.custom_stop_words(custom_stop_words);
if let Some(ascii_folding) = ascii_folding {
opts = opts.ascii_folding(ascii_folding);
}
@@ -261,10 +266,15 @@ impl Index {
if let Some(prefix_only) = prefix_only {
opts = opts.ngram_prefix_only(prefix_only);
}
Self {
inner: Mutex::new(Some(LanceDbIndex::FTS(opts))),
if let Some(block_size) = block_size {
opts = opts
.block_size(block_size as usize)
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
}
Ok(Self {
inner: Mutex::new(Some(LanceDbIndex::FTS(opts))),
})
}
#[napi(factory)]
+2 -2
View File
@@ -51,9 +51,9 @@ impl NativeMergeInsertBuilder {
}
#[napi]
pub fn use_lsm_write(&self, use_lsm_write: bool) -> Self {
pub fn use_lsm(&self, enable: bool) -> Self {
let mut this = self.clone();
this.inner.use_lsm_write(use_lsm_write);
this.inner.use_lsm(enable);
this
}
+15
View File
@@ -168,6 +168,11 @@ impl Query {
self.inner = self.inner.clone().with_row_id();
}
#[napi]
pub fn use_lsm(&mut self, enable: bool) {
self.inner = self.inner.clone().use_lsm(enable);
}
#[napi]
pub fn order_by(&mut self, ordering: Option<Vec<ColumnOrdering>>) -> napi::Result<()> {
let ordering = ordering.map(|ordering| {
@@ -374,6 +379,11 @@ impl VectorQuery {
self.inner = self.inner.clone().with_row_id();
}
#[napi]
pub fn use_lsm(&mut self, enable: bool) {
self.inner = self.inner.clone().use_lsm(enable);
}
#[napi]
pub fn rerank(
&mut self,
@@ -479,6 +489,11 @@ impl TakeQuery {
self.inner = self.inner.clone().with_row_id();
}
#[napi]
pub fn use_lsm(&mut self, enable: bool) {
self.inner = self.inner.clone().use_lsm(enable);
}
#[napi(catch_unwind)]
pub async fn output_schema(&self) -> napi::Result<Buffer> {
let schema = self.inner.output_schema().await.default_error()?;
+4
View File
@@ -232,6 +232,10 @@ impl From<ClientConfig> for lancedb::remote::ClientConfig {
tls_config: config.tls_config.map(Into::into),
header_provider: None, // the header provider is set separately later
user_id: config.user_id,
// Resolved from LANCE_CLIENT_MAX_BYTES_PER_REQUEST or the default.
max_bytes_per_request: None,
// Resolved from LANCE_CLIENT_MAX_REQUEST_DURATION or the read timeout.
max_request_duration: None,
}
}
}
+24
View File
@@ -1355,4 +1355,28 @@ impl Branches {
pub async fn delete(&self, name: String) -> napi::Result<()> {
self.inner.delete_branch(&name).await.default_error()
}
#[napi(ts_return_type = "Promise<Record<string, unknown>>")]
pub async fn diff(&self, from_branch: String) -> napi::Result<serde_json::Value> {
let diff = self.inner.diff_branch(&from_branch).await.default_error()?;
serde_json::to_value(diff).map_err(|err| {
napi::Error::from_reason(format!("failed to serialize branch diff: {err}"))
})
}
#[napi(ts_return_type = "Promise<Record<string, unknown>>")]
pub async fn merge(
&self,
from_branch: String,
dry_run: Option<bool>,
) -> napi::Result<serde_json::Value> {
let result = self
.inner
.merge_branch(&from_branch, dry_run.unwrap_or(false))
.await
.default_error()?;
serde_json::to_value(result).map_err(|err| {
napi::Error::from_reason(format!("failed to serialize branch merge result: {err}"))
})
}
}
@@ -0,0 +1,21 @@
{
"name": "lancedb",
"description": "Write, review, debug, and document LanceDB pipelines in Python and TypeScript that work across local LanceDB OSS tables and remote LanceDB Enterprise/Cloud tables, with idiomatic query/search patterns and performance defaults for ingestion, indexing, filtering, and diagnostics.",
"version": "0.1.0",
"author": {
"name": "LanceDB"
},
"homepage": "https://www.lancedb.com",
"keywords": [
"lancedb",
"vector-search",
"full-text-search",
"hybrid-search",
"python",
"typescript",
"pipelines",
"ingestion",
"indexing",
"performance"
]
}
+33
View File
@@ -0,0 +1,33 @@
{
"name": "lancedb",
"version": "0.1.0",
"description": "Codex plugin for building LanceDB pipelines in Python and TypeScript.",
"author": {
"name": "LanceDB"
},
"keywords": [
"lancedb",
"vector-search",
"full-text-search",
"hybrid-search",
"python",
"typescript",
"pipelines"
],
"skills": "./skills/",
"interface": {
"displayName": "LanceDB",
"shortDescription": "Build LanceDB pipelines in Python and TypeScript.",
"longDescription": "Write, review, debug, and document LanceDB pipelines in Python and TypeScript that work across local LanceDB OSS tables and remote LanceDB Enterprise/Cloud tables, with idiomatic query/search patterns and performance defaults for ingestion, indexing, filtering, and diagnostics.",
"developerName": "LanceDB",
"websiteURL": "https://www.lancedb.com",
"category": "Developer Tools",
"capabilities": [
"Developer Tools"
],
"defaultPrompt": "Create a LanceDB table, embed sample text, and run a vector search.",
"composerIcon": "./assets/logo.png",
"logo": "./assets/logo.png",
"logoDark": "./assets/logo-dark.png"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

@@ -1,6 +1,6 @@
---
name: lancedb
description: Use when writing, reviewing, debugging, or documenting LanceDB pipelines in Python or TypeScript, especially code that should work across local LanceDB OSS tables and remote LanceDB Enterprise/Cloud tables. Helps avoid non-portable full-table materialization, choose idiomatic query/search patterns, and apply LanceDB performance defaults for ingestion, indexing, filtering, and diagnostics.
description: Use when writing, reviewing, debugging, or documenting LanceDB pipelines in Python or TypeScript, especially code that should work across local LanceDB OSS tables and remote LanceDB Enterprise/Cloud tables. Helps avoid non-portable full-table materialization, choose idiomatic query/search patterns, apply LanceDB performance defaults for ingestion, indexing, filtering, and diagnostics, and resolve connections to the remote server for Enterprise-only operations such as jobs.
---
# Building LanceDB Pipelines
@@ -19,7 +19,7 @@ Do NOT assume local-only table helpers exist on remote tables. If the user asks
## Workflow
1. Identify the SDK: Python, TypeScript, or both.
2. Identify the table mode: local/embedded OSS, remote Enterprise/Cloud, or portable across both. If the user says "LanceDB Enterprise", choose the remote table path.
2. Identify the table mode: local/embedded OSS, remote Enterprise/Cloud, or portable across both. If the user says "LanceDB Enterprise", choose the remote table path. If the task involves jobs in any way (listing, inspecting, creating, or canceling jobs), it is always the remote path and requires a remote server connection — see "Connecting to the LanceDB remote server" below before doing anything else.
3. Read the matching language branch before writing or changing code:
- Python patterns: `references/python/patterns.md`
- Python API quick reference: `references/python/api_reference.md`
@@ -29,7 +29,9 @@ Do NOT assume local-only table helpers exist on remote tables. If the user asks
- TypeScript performance guidance: `references/typescript/performance.md`
- Column metadata authoring (both SDKs): `references/column_metadata.md`
- Branch operations (both SDKs): `references/branch_ops.md`
4. Start with `patterns.md` for the selected SDK. Read `api_reference.md` when choosing method names or return collectors. Read `performance.md` when the task involves ingestion, indexing, filtering, query tuning, diagnostics, or large datasets. Read `column_metadata.md` when the task is documenting, tagging, classifying, or grouping table columns (field descriptions, `lancedb:tag:*` tags, logical column families). Read `branch_ops.md` when the task involves branch lifecycle (list/create/delete), writing to a non-main branch, or verifying a change stayed off main.
- Remote server connection resolution (jobs, raw REST): `references/remote_connect.md`
- Job operations REST API (list/describe/cancel/query_events): `references/remote_jobs.md`
4. Start with `patterns.md` for the selected SDK. Read `api_reference.md` when choosing method names or return collectors. Read `performance.md` when the task involves ingestion, indexing, filtering, query tuning, diagnostics, or large datasets. Read `column_metadata.md` when the task is documenting, tagging, classifying, or grouping table columns (field descriptions, `lancedb:tag:*` tags, logical column families). Read `branch_ops.md` when the task involves branch lifecycle (list/create/delete), writing to a non-main branch, or verifying a change stayed off main. Read `remote_connect.md` when the task involves jobs or direct REST access to an Enterprise deployment, and `remote_jobs.md` for the job REST methods themselves (list, describe, cancel, query_events).
5. For Python schemas, favor Pydantic models and validate records before writing. Use PyArrow schemas when Arrow-native, streaming, or highly dynamic data makes them materially better suited.
6. Prefer `search()` or `query()` builders with explicit `select()` and `limit()` for reads.
7. Avoid table-level full materialization in remote or portable code. This is the main local-vs-remote read pitfall.
@@ -70,6 +72,10 @@ Rules for portable Enterprise ingestion:
This is Enterprise/Cloud-specific. Local/OSS tables have no separate data plane, so `mode="overwrite"` and immediate same-name reuse are fine there.
## Connecting to the LanceDB remote server
LanceDB Enterprise/Cloud deployments are served by a server implementing the lance-namespace OpenAPI spec (<https://github.com/lance-format/lance-namespace/blob/main/docs/src/spec.yaml>). Every remote (`db://...`) connection talks to such a server, and some operations exist only there. In particular, **all operations around jobs (listing, inspecting, creating, or canceling jobs) run server-side** — there is no local/OSS equivalent. Before any job work, or any direct REST call to an Enterprise deployment, read `references/remote_connect.md` to resolve the base URL, credentials, and database header and to validate the connection. Then use the four job REST methods documented in `references/remote_jobs.md` (list, describe, cancel, query_events).
## Script
Run the scanner when reviewing or modifying an existing codebase:
@@ -0,0 +1,6 @@
interface:
display_name: "LanceDB"
short_description: "Build LanceDB pipelines in Python and TypeScript"
default_prompt: "Use $lancedb to create a table, embed sample text, and run a vector search."
icon_small: "./assets/icon.png"
icon_large: "./assets/icon.png"
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

@@ -2,7 +2,7 @@
Manage branches on a LanceDB table: list what exists, create new ones, delete stale ones, and direct read/write operations at a specific branch without touching main. Use for branch lifecycle tasks, experimental/isolated table versions, targeting an operation at a non-main branch, or confirming a mutation did not affect main.
Works on local/OSS and remote Enterprise/Cloud tables.
Works on local/OSS and remote Enterprise/Cloud tables, except merging a branch into main, which is Enterprise-only.
## The branch model (important)
@@ -101,6 +101,69 @@ assert b"lancedb:description" not in (table.schema.field("category").metadata or
Two handles on the same branch see each other's writes (e.g. `table.branches.create("exp")` and `db.open_table(name, branch="exp")`); main stays isolated.
## Merging a branch into main (Enterprise only)
Merge is available through the SDKs (`table.branches.merge(...)`) on **Enterprise tables only** — it is not supported on Cloud or local/OSS tables, which raise `NotSupported`.
`merge` takes the branch to merge **from** and a `dry_run` flag. Both the SDK method and the underlying REST endpoint **actually merge by default** (`dry_run=False`); pass `dry_run=True` to only preview. A rejected merge is **not an exception** — it returns a result with `status="rejected"` rather than raising, so inspect the return value. Use `branches.diff(from_branch)` to inspect a branch's pending diff without attempting a merge.
```python
exp = "experiment-reindex"
# preview only — returns status="ready" if it would merge cleanly
preview = table.branches.merge(exp, dry_run=True)
# actually merge (default)
result = table.branches.merge(exp)
if result["status"] == "merged":
print("landed at", result["mainVersionAfter"])
elif result["status"] == "rejected":
print(result["diff"]["mergeBlockers"]) # why it was refused
# inspect a branch's pending diff without merging
diff = table.branches.diff(exp)
```
Async: `await table.branches.merge(exp)`, `await table.branches.diff(exp)`.
```typescript
const branches = await table.branches();
const exp = "experiment-reindex";
// preview only (second arg is dryRun)
const preview = await branches.merge(exp, true);
// actually merge (default)
const result = await branches.merge(exp);
if (result.status === "merged") {
console.log("landed at", result.mainVersionAfter);
} else if (result.status === "rejected") {
console.log(result.diff.mergeBlockers);
}
const diff = await branches.diff(exp);
```
The result is the wire JSON, containing `status` (`ready` on a passing dry run, `merged` on success, `rejected` when refused — also `notImplemented`/`unknown`), the branch `diff` (including `mergeBlockers` explaining any rejection), a `preview` of the columns that would be promoted, and — after a real merge — `mainVersionAfter`.
### Merge preconditions
Merge only **promotes newly added columns** onto main; it does not replay arbitrary commits. Practically, a branch is mergeable only if it has **exactly one commit since it was created, and that commit added a column**. The merge is rejected (`status: "rejected"`, with `mergeBlockers` set) if:
- the branch was forked from another branch rather than directly from main
- main has advanced since the branch was forked
- the branch's rows changed since the fork (row counts must match main exactly)
- the branch removed columns or changed a column's type/nullability
- the branch added no columns (index-only changes are not merged)
### Adding a column in a single commit
Because the branch must contain just one column-adding commit, add the column with its values in one operation rather than add-then-backfill:
1. **SQL transformation**`add_columns` with a SQL expression computed from existing columns, so the column lands populated in one commit.
2. **Precompute the values** — compute the column's values externally, then add the fully-populated column in a single operation (e.g. via `merge_insert`/`add_columns` with the data ready).
3. **Lance-format-level data evolution (pylance)** — use Lance's data evolution with backfill, documented at <https://lance.org/guide/data_evolution/#with-data-backfill>.
## Quick reference
| Goal | Python | TypeScript |
@@ -113,5 +176,7 @@ Two handles on the same branch see each other's writes (e.g. `table.branches.cre
| Delete branch | `table.branches.delete(name)` | `await branches.delete(name)` |
| Which branch is this handle on? | `table.current_branch()` (`None` = main) | `table.currentBranch()` (`null` = main) |
| Target main | use the original (non-branch) handle | use the original (non-branch) handle |
| Merge branch into main (Enterprise only) | `table.branches.merge(from_branch, dry_run=False)` | `await branches.merge(fromBranch, dryRun)` |
| Preview a branch's pending diff (Enterprise only) | `table.branches.diff(from_branch)` | `await branches.diff(fromBranch)` |
Branch names must be non-empty; empty names raise a validation error.
@@ -4,12 +4,19 @@ Quick method reference for Python LanceDB code. Cross-check source for non-trivi
## Connect
If you're connecting to a remote database, use this:
```python
import lancedb
db = lancedb.connect("./camelot-db") # local/OSS
db = lancedb.connect("db://my-db", api_key=api_key, region=region) # remote
db = lancedb.connect("db://my-db", api_key=api_key, host_override=host_override) # remote
```
(values may be found in LANCEDB_API_KEY and LANCEDB_HOST_OVERRIDE, either in env vars or a .env file)
If you're connecting to a local table using OSS LanceDB, use this:
```python
db = lancedb.connect("./camelot-db") # local/OSS
```
If you're not sure which, or if you can't find the api_key or host_override params, ask the user.
**Place the local database directory next to the script/entrypoint that opens it** (i.e. resolve the path relative to the script, `Path(__file__).parent / "camelot-db"`), not buried under a shared `data/` folder. The Lance dataset is the database, not a data file — keeping it beside its code makes ownership obvious and paths stable regardless of the working directory the script is launched from.
@@ -0,0 +1,45 @@
# Connecting to a LanceDB remote server
LanceDB Enterprise/Cloud deployments are served by a server implementing the
lance-namespace OpenAPI spec
(<https://github.com/lance-format/lance-namespace/blob/main/docs/src/spec.yaml>).
Every remote (`db://...`) connection talks to such a server, and some operations
exist only there. In particular, all operations around jobs (listing, inspecting,
creating, or canceling jobs) run server-side — there is no local/OSS equivalent, so
resolve a server connection before attempting any job work. The job REST methods
themselves are documented in `references/remote_jobs.md`.
Every request needs two things:
1. **Base URL** — the server endpoint
2. **Credentials** — an API key (`x-api-key` header over REST), and usually a database name (`x-lancedb-database` header)
## Resolution steps
1. If the user already gave a URL and API key (or said which environment they're working against), use that.
2. Otherwise, look for credentials already available in the environment:
- Env vars like `LANCEDB_URI` / `LANCEDB_HOST` / `LANCEDB_API_KEY`
- A server endpoint already running or port-forwarded locally (the REST default port is 2333, i.e. `http://localhost:2333`)
3. If you didn't find both pieces, ask the user directly: **"What's your LanceDB endpoint's URL, and what's your API key?"** Also ask which database to use if it isn't obvious. Don't guess or probe further — the user knows their deployment.
## Validating the connection
Make a cheap authenticated request and check the status before starting real work:
```bash
curl -s -w "\n%{http_code}" "{base_url}/v1/table/?limit=1" \
-H "x-api-key: <key>" \
-H "x-lancedb-database: <database>"
```
- `200` — connection, key, and database header all good
- `401` — API key missing or wrong
- `400` mentioning a database header — this deployment expects `x-lancedb-database`
## Non-REST equivalents
The same credentials work through the SDKs and CLI:
- Python SDK: `lancedb.connect("db://<database>", api_key="<key>", host_override="<base_url>")`
- TypeScript SDK: `await lancedb.connect("db://<database>", { apiKey: "<key>", hostOverride: "<base_url>" })`
- `lancedb` CLI: a `[profiles.<name>]` entry in `~/.lancedb/config.toml` with `http_server_url`, `api_key`, `database`
@@ -0,0 +1,151 @@
# Job operations over the LanceDB remote server REST API
Jobs are server-side background operations on LanceDB Enterprise/Cloud — index builds,
column backfills, materialized view refreshes, and similar async work. Endpoints that
trigger async work (e.g. the column backfill or materialized view refresh endpoints)
return a `job_id`; these four methods are how you track and manage those jobs.
Resolve the connection first — see `references/remote_connect.md`. All four methods
are **POST** requests under `{base_url}/v1/jobs/` with JSON bodies, and take the usual
`x-api-key` / `x-lancedb-database` headers. If every job call returns `501`, job APIs
are disabled on that deployment (the server has no job registry configured) — report
that rather than retrying.
## 1. List jobs — `POST /v1/jobs/list`
The body is optional; an empty body lists everything. All fields are filters:
```json
{
"limit": 100,
"table_name": "my_table",
"job_type": "...",
"job_subtype": "...",
"state": "...",
"page_token": "..."
}
```
```bash
curl -s -X POST "{base_url}/v1/jobs/list" \
-H "x-api-key: <key>" -H "x-lancedb-database: <database>" \
-H "content-type: application/json" \
-d '{"table_name": "my_table"}'
```
Response:
```json
{
"jobs": [
{
"job_id": "...",
"table": "my_table",
"job_type": "...",
"job_subtype": "...",
"state": "done",
"created_at_millis": 1720000000000
}
],
"page_token": "..."
}
```
A `page_token` in the response means there are more results — pass it back in the next
request to continue. Note list rows use a lowercase `state` string, while describe uses
an uppercase `job_state`.
## 2. Describe a job — `POST /v1/jobs/describe`
Body: `{"job_id": "<id>"}`. Returns full detail for one job:
```json
{
"job_id": "...",
"job_type": "...",
"job_subtype": "...",
"job_state": "IN_PROGRESS",
"creation_ms": 1720000000000,
"spec": {},
"status": {}
}
```
`job_state` is one of `IN_PROGRESS`, `CANCELLED`, `FAILED`, `DONE`. `spec` and `status`
are job-type-specific JSON objects (the job's input specification and its current
progress/status). Returns `404` for an unknown job id.
## 3. Cancel a job — `POST /v1/jobs/cancel`
Body: `{"job_id": "<id>"}`; response echoes `{"job_id": "<id>"}`. Cancellation is a
service-level operation requiring the same administrative authorization as the
`/admin` routes — a database-scoped API key that can list and describe jobs may still
get a permission error here. Other errors: `404` unknown job, `409` state conflict
(e.g. already in a terminal state), `429` too much write contention (safe to retry).
## 4. Query job event history — `POST /v1/jobs/query_events`
Returns the event history (state transitions, progress updates) for one or more jobs.
Body: `{"job_id": "<id>"}` for one job, or `{"job_ids": ["<id>", ...]}` for a batch.
Optional fields: `limit` (max event rows), `limit_per_job` (per job in a batch query),
and `filter` — a SQL-like expression over the columns `state`, `updated_by`,
`owner_component`, and `claim_entity`. (`full_text_search` is reserved and currently
rejected as not implemented.)
The response is **not JSON** — it is an Arrow IPC stream
(`content-type: application/vnd.apache.arrow.stream`). Decode it, e.g. in Python:
```python
import pyarrow.ipc
import requests
resp = requests.post(
f"{base_url}/v1/jobs/query_events",
headers={"x-api-key": key, "x-lancedb-database": database},
json={"job_id": job_id},
)
resp.raise_for_status()
events = pyarrow.ipc.open_stream(resp.content).read_all()
```
## Feature engineering (Geneva) jobs
Feature engineering jobs — UDF column backfills and materialized view refreshes run
through Geneva — are tracked **separately** from the `/v1/jobs` registry above. Their
records live in a `geneva_jobs` table inside the database itself (in the `__system`
namespace), and you access them through a Python `geneva` connection rather than the
REST endpoints above:
```python
import geneva
from geneva.jobs import JobStateManager
# Same credentials as lancedb.connect / the REST API
conn = geneva.connect("db://<database>", api_key="<key>", host_override="<base_url>")
jsm = JobStateManager(conn)
# List jobs. NOTE: status defaults to "RUNNING"; pass status=None for all jobs.
# Statuses: PENDING | RUNNING | DONE | FAILED | CANCELLED
jobs = jsm.list_jobs(table_name="my_table", status=None)
# Fetch one job by id (returns a list of JobRecord)
records = jsm.get("<job_id>")
```
Each `JobRecord` has `table_name`, `column_name`, `job_id`, `job_type`, `status`,
`launched_at`, `completed_at`, `config`, `launched_by`, `manifest_id`, `cluster_name`,
`metrics` (progress counters), `events` (human-readable history), and `updated_at`.
For filters `list_jobs` doesn't support (e.g. time ranges), query the underlying table
directly: `jsm.get_table(True).search().where("launched_at >= TIMESTAMP '...'")`
pass `True` to check out the latest version, since other processes update job state.
Stale-status caveat: nothing reaps dead Geneva jobs, so a job can sit in
`RUNNING`/`PENDING` forever if its worker died. Treat a job as effectively `FAILED`
when it has been running longer than ~36 hours, or its `updated_at` is more than ~2
hours old (this matches the heuristic the Geneva console UI applies on read).
## Workflow tips
- To wait for async work (a backfill, an index build), poll `describe` until
`job_state` leaves `IN_PROGRESS`; on `FAILED`, pull `status` and `query_events` for
the failure detail.
-49
View File
@@ -1,49 +0,0 @@
[tool.bumpversion]
current_version = "0.35.0-beta.2"
parse = """(?x)
(?P<major>0|[1-9]\\d*)\\.
(?P<minor>0|[1-9]\\d*)\\.
(?P<patch>0|[1-9]\\d*)
(?:-(?P<pre_l>[a-zA-Z-]+)\\.(?P<pre_n>0|[1-9]\\d*))?
"""
serialize = [
"{major}.{minor}.{patch}-{pre_l}.{pre_n}",
"{major}.{minor}.{patch}",
]
search = "{current_version}"
replace = "{new_version}"
regex = false
ignore_missing_version = false
ignore_missing_files = false
tag = true
sign_tags = false
tag_name = "python-v{new_version}"
tag_message = "Bump version: {current_version} → {new_version}"
allow_dirty = true
commit = true
message = "Bump version: {current_version} → {new_version}"
commit_args = ""
# bump-my-version >=1.4.0 rejects pre_commit_hooks containing shell syntax unless opted in.
allow_shell_hooks = true
# Update Cargo.lock after version bump
pre_commit_hooks = [
"""
cd python && cargo update -p lancedb-python
if git diff --quiet ../Cargo.lock; then
echo "Cargo.lock unchanged"
else
git add ../Cargo.lock
echo "Updated and staged Cargo.lock"
fi
""",
]
[tool.bumpversion.parts.pre_l]
values = ["beta", "final"]
optional_value = "final"
[[tool.bumpversion.files]]
filename = "Cargo.toml"
search = "\nversion = \"{current_version}\""
replace = "\nversion = \"{new_version}\""
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "lancedb-python"
version = "0.35.0-beta.2"
version = "0.37.1-beta.0"
publish = false
edition.workspace = true
description = "Python bindings for LanceDB"
+21
View File
@@ -8,6 +8,27 @@ A Python library for [LanceDB](https://github.com/lancedb/lancedb).
pip install lancedb
```
### Pre-Haswell x86_64 hosts: `lancedb-compat`
The default `lancedb` wheel targets `x86-64-haswell` (AVX2 + FMA + F16C) for full performance on modern hardware. Pre-Haswell hosts — Intel Sandy Bridge / Ivy Bridge / Westmere; AMD Bulldozer / Piledriver / Steamroller — don't have AVX2 and crash with `Illegal instruction` at `import lancedb`.
For those hosts, install the `lancedb-compat` package instead:
```bash
pip install lancedb-compat
```
Same Python API (`import lancedb` works as usual). The compat wheel is compiled at the `x86-64-v2` baseline (Nehalem-class) and uses runtime SIMD dispatch in the embedded lance crate to pick the right kernel tier (scalar / AVX / AVX+FMA / AVX2+FMA / AVX-512) at load time, so it still goes fast on modern hardware while running cleanly on the pre-Haswell silicon. Use `lance.simd_info()` from Python to verify which tier was selected.
`lancedb` and `lancedb-compat` install to the same `lancedb/` namespace and conflict at install time. Pick one. To switch, `pip uninstall lancedb` first, then `pip install lancedb-compat` (or vice-versa).
If you need a custom baseline (or `lancedb-compat` isn't yet published for your platform), build from source with the override:
```bash
RUSTFLAGS="-C target-cpu=x86-64-v2" maturin build --release
pip install ./target/wheels/lancedb-*.whl
```
### Preview Releases
Stable releases are created about every 2 weeks. For the latest features and bug fixes, you can install the preview release. These releases receive the same level of testing as stable releases, but are not guaranteed to be available for more than 6 months after they are released. Once your application is stable, we recommend switching to stable releases.
+5 -2
View File
@@ -258,6 +258,7 @@ def tokenize(
lower_case: bool = True,
stem: bool = True,
remove_stop_words: bool = True,
custom_stop_words: Optional[List[str]] = None,
ascii_folding: bool = True,
ngram_min_length: int = 3,
ngram_max_length: int = 3,
@@ -265,9 +266,10 @@ def tokenize(
) -> Iterable[FtsToken]:
"""Tokenize a full-text search query using an explicit tokenizer.
This does not require a table or FTS index. The tokenizer options match
:class:`lancedb.index.FTS`.
This does not require an FTS index. The tokenizer options match
:class:`lancedb.index.FTS`. ``custom_stop_words`` accepts a list of strings.
"""
return _tokenize(
query,
base_tokenizer=base_tokenizer,
@@ -276,6 +278,7 @@ def tokenize(
lower_case=lower_case,
stem=stem,
remove_stop_words=remove_stop_words,
custom_stop_words=custom_stop_words,
ascii_folding=ascii_folding,
ngram_min_length=ngram_min_length,
ngram_max_length=ngram_max_length,
+17
View File
@@ -59,6 +59,7 @@ def tokenize(
lower_case: bool = True,
stem: bool = True,
remove_stop_words: bool = True,
custom_stop_words: Optional[List[str]] = None,
ascii_folding: bool = True,
ngram_min_length: int = 3,
ngram_max_length: int = 3,
@@ -219,6 +220,7 @@ class Table:
data: pa.RecordBatchReader,
mode: Literal["append", "overwrite"],
progress: Optional[Any] = None,
write_parallelism: Optional[int] = None,
) -> AddResult: ...
async def update(
self, updates: Dict[str, str], where: Optional[str]
@@ -296,6 +298,11 @@ class Table:
async def fetch_blobs(
self, column: str, row_ids: list[int]
) -> pa.LargeBinaryArray: ...
async def fetch_blob_ranges(
self,
column: str,
requests: List[Tuple[int, int, int]],
) -> pa.LargeBinaryArray: ...
async def fetch_blob_files(
self, column: str, row_ids: list[int]
) -> list[Optional[BlobFile]]: ...
@@ -318,6 +325,10 @@ class Branches:
) -> Table: ...
async def checkout(self, name: str, version: Optional[int] = None) -> Table: ...
async def delete(self, name: str) -> None: ...
async def diff(self, from_branch: str) -> Dict[str, Any]: ...
async def merge(
self, from_branch: str, dry_run: bool = False
) -> Dict[str, Any]: ...
class IndexConfig:
name: str
@@ -386,6 +397,7 @@ class Query:
def fast_search(self): ...
def with_row_id(self): ...
def postfilter(self): ...
def use_lsm(self, enable: bool): ...
def nearest_to(self, query_vec: pa.Array) -> VectorQuery: ...
def nearest_to_text(self, query: dict) -> FTSQuery: ...
def order_by(self, ordering: Optional[List[ColumnOrdering]]): ...
@@ -402,6 +414,7 @@ class Query:
class TakeQuery:
def select(self, columns: List[str]): ...
def with_row_id(self): ...
def use_lsm(self, enable: bool): ...
async def output_schema(self) -> pa.Schema: ...
async def execute(self) -> RecordBatchStream: ...
async def explain_plan(self, verbose: Optional[bool]) -> str: ...
@@ -420,6 +433,7 @@ class FTSQuery:
def fast_search(self): ...
def with_row_id(self): ...
def postfilter(self): ...
def use_lsm(self, enable: bool): ...
def get_query(self) -> str: ...
def add_query_vector(self, query_vec: pa.Array) -> None: ...
def nearest_to(self, query_vec: pa.Array) -> HybridQuery: ...
@@ -447,6 +461,7 @@ class VectorQuery:
def column(self, column: str): ...
def distance_type(self, distance_type: str): ...
def postfilter(self): ...
def use_lsm(self, enable: bool): ...
def refine_factor(self, refine_factor: int): ...
def nprobes(self, nprobes: int): ...
def minimum_nprobes(self, minimum_nprobes: int): ...
@@ -470,6 +485,7 @@ class HybridQuery:
def fast_search(self): ...
def with_row_id(self): ...
def postfilter(self): ...
def use_lsm(self, enable: bool): ...
def distance_type(self, distance_type: str): ...
def refine_factor(self, refine_factor: int): ...
def nprobes(self, nprobes: int): ...
@@ -494,6 +510,7 @@ class PyQueryRequest:
select: Optional[Union[str, List[str]]]
fast_search: Optional[bool]
with_row_id: Optional[bool]
use_lsm: Optional[bool]
column: Optional[str]
query_vector: Optional[List[pa.Array]]
minimum_nprobes: Optional[int]
+40 -64
View File
@@ -41,6 +41,7 @@ from lance_namespace import (
ListTablesResponse,
connect as namespace_connect,
)
from lance_namespace.errors import NamespaceNotEmptyError, TableNotFoundError
from . import __version__
from ._lancedb import connect as lancedb_connect # type: ignore
@@ -358,7 +359,7 @@ class DBConnection(EnforceOverrides):
Data is converted to Arrow before being written to disk. For maximum
control over how data is saved, either provide the PyArrow schema to
convert to or else provide a [PyArrow Table](pyarrow.Table) directly.
convert to or else provide a [PyArrow Table][pyarrow.Table] directly.
>>> import pyarrow as pa
>>> custom_schema = pa.schema([
@@ -746,10 +747,12 @@ class LanceDBConnection(DBConnection):
"""
if namespace_path is None:
namespace_path = []
return self._namespace_conn().list_namespaces(
namespace_path=namespace_path,
page_token=page_token,
limit=limit,
return LOOP.run(
self._conn.list_namespaces(
namespace_path=namespace_path,
page_token=page_token,
limit=limit,
)
)
@override
@@ -759,10 +762,12 @@ class LanceDBConnection(DBConnection):
mode: Optional[str] = None,
properties: Optional[Dict[str, str]] = None,
) -> CreateNamespaceResponse:
return self._namespace_conn().create_namespace(
namespace_path=namespace_path,
mode=mode,
properties=properties,
return LOOP.run(
self._conn.create_namespace(
namespace_path=namespace_path,
mode=mode,
properties=properties,
)
)
@override
@@ -772,19 +777,24 @@ class LanceDBConnection(DBConnection):
mode: Optional[str] = None,
behavior: Optional[str] = None,
) -> DropNamespaceResponse:
return self._namespace_conn().drop_namespace(
namespace_path=namespace_path,
mode=mode,
behavior=behavior,
)
try:
return LOOP.run(
self._conn.drop_namespace(
namespace_path=namespace_path,
mode=mode,
behavior=behavior,
)
)
except RuntimeError as e:
if "Namespace not empty" in str(e):
raise NamespaceNotEmptyError(str(e)) from e
raise
@override
def describe_namespace(
self, namespace_path: List[str]
) -> DescribeNamespaceResponse:
return self._namespace_conn().describe_namespace(
namespace_path=namespace_path,
)
return LOOP.run(self._conn.describe_namespace(namespace_path=namespace_path))
@override
def list_tables(
@@ -813,12 +823,6 @@ class LanceDBConnection(DBConnection):
"""
if namespace_path is None:
namespace_path = []
if namespace_path:
return self._namespace_conn().list_tables(
namespace_path=namespace_path,
page_token=page_token,
limit=limit,
)
return LOOP.run(
self._conn.list_tables(
namespace_path=namespace_path, page_token=page_token, limit=limit
@@ -916,22 +920,6 @@ class LanceDBConnection(DBConnection):
raise ValueError("mode must be either 'create' or 'overwrite'")
validate_table_name(name)
if namespace_path:
return self._namespace_conn().create_table(
name,
data=data,
schema=schema,
mode=mode,
exist_ok=exist_ok,
on_bad_vectors=on_bad_vectors,
fill_value=fill_value,
embedding_functions=embedding_functions,
namespace_path=namespace_path,
storage_options=storage_options,
data_storage_version=data_storage_version,
enable_v2_manifest_paths=enable_v2_manifest_paths,
)
tbl = LanceTable.create(
self,
name,
@@ -944,22 +932,11 @@ class LanceDBConnection(DBConnection):
embedding_functions=embedding_functions,
namespace_path=namespace_path,
storage_options=storage_options,
data_storage_version=data_storage_version,
enable_v2_manifest_paths=enable_v2_manifest_paths,
)
return tbl
def _namespace_conn(self) -> DBConnection:
"""Return a LanceNamespaceDBConnection backed by this connection's
directory namespace. Used to delegate child-namespace operations."""
from lancedb.namespace import LanceNamespaceDBConnection
return LanceNamespaceDBConnection(
self.namespace_client(),
read_consistency_interval=self.read_consistency_interval,
storage_options=self.storage_options,
namespace_client_impl=None,
namespace_client_properties=None,
)
@override
def open_table(
self,
@@ -1006,14 +983,7 @@ class LanceDBConnection(DBConnection):
stacklevel=2,
)
if namespace_path:
tbl = self._namespace_conn().open_table(
name,
namespace_path=namespace_path,
storage_options=storage_options,
index_cache_size=index_cache_size,
)
else:
try:
tbl = LanceTable.open(
self,
name,
@@ -1021,6 +991,15 @@ class LanceDBConnection(DBConnection):
storage_options=storage_options,
index_cache_size=index_cache_size,
)
except (RuntimeError, ValueError) as e:
if namespace_path and (
"Table not found" in str(e) or "was not found" in str(e)
):
table_id = namespace_path + [name]
raise TableNotFoundError(
f"Table not found: {'$'.join(table_id)}"
) from e
raise
if branch is not None:
tbl = tbl.branches.checkout(branch, version)
@@ -1104,9 +1083,6 @@ class LanceDBConnection(DBConnection):
"""
if namespace_path is None:
namespace_path = []
if namespace_path:
self._namespace_conn().drop_table(name, namespace_path=namespace_path)
return
LOOP.run(
self._conn.drop_table(
name, namespace_path=namespace_path, ignore_missing=ignore_missing
@@ -1553,7 +1529,7 @@ class AsyncConnection(object):
Data is converted to Arrow before being written to disk. For maximum
control over how data is saved, either provide the PyArrow schema to
convert to or else provide a [PyArrow Table](pyarrow.Table) directly.
convert to or else provide a [PyArrow Table][pyarrow.Table] directly.
>>> import pyarrow as pa
>>> custom_schema = pa.schema([
@@ -21,3 +21,32 @@ from .watsonx import WatsonxEmbeddings
from .voyageai import VoyageAIEmbeddingFunction
from .colpali import ColPaliEmbeddings
from .siglip import SigLipEmbeddings
# The API reference renders this package with a single mkdocstrings directive,
# which only picks up names listed here. New embedding functions must be added
# to both the imports above and this list, or they will silently go undocumented.
__all__ = [
"EmbeddingFunction",
"EmbeddingFunctionConfig",
"TextEmbeddingFunction",
"EmbeddingFunctionRegistry",
"get_registry",
"register",
"SentenceTransformerEmbeddings",
"OpenAIEmbeddings",
"OpenClipEmbeddings",
"BedRockText",
"CohereEmbeddingFunction",
"GeminiText",
"GteEmbeddings",
"InstructorEmbeddingFunction",
"JinaEmbeddings",
"OllamaEmbeddings",
"TransformersEmbeddingFunction",
"ColbertEmbeddings",
"VoyageAIEmbeddingFunction",
"WatsonxEmbeddings",
"ColPaliEmbeddings",
"ImageBindEmbeddings",
"SigLipEmbeddings",
]
+5 -5
View File
@@ -21,20 +21,20 @@ class BedRockText(TextEmbeddingFunction):
"""
Parameters
----------
name: str, default "amazon.titan-embed-text-v1"
name : str, default "amazon.titan-embed-text-v1"
The model ID of the bedrock model to use. Supported models for are:
- amazon.titan-embed-text-v1
- cohere.embed-english-v3
- cohere.embed-multilingual-v3
region: str, default "us-east-1"
region : str, default "us-east-1"
Optional name of the AWS Region in which the service should be called.
profile_name: str, default None
profile_name : str, default None
Optional name of the AWS profile to use for calling the Bedrock service.
If not specified, the default profile will be used.
assumed_role: str, default None
assumed_role : str, default None
Optional ARN of an AWS IAM role to assume for calling the Bedrock service.
If not specified, the current active credentials will be used.
role_session_name: str, default "lancedb-embeddings"
role_session_name : str, default "lancedb-embeddings"
Optional name of the AWS IAM role session to use for calling the Bedrock
service. If not specified, "lancedb-embeddings" name will be used.
+5 -3
View File
@@ -22,7 +22,7 @@ class CohereEmbeddingFunction(TextEmbeddingFunction):
Parameters
----------
name: str, default "embed-multilingual-v2.0"
name : str, default "embed-multilingual-v2.0"
The name of the model to use. List of acceptable models:
* embed-english-v3.0
@@ -33,12 +33,14 @@ class CohereEmbeddingFunction(TextEmbeddingFunction):
* embed-english-light-v2.0
* embed-multilingual-v2.0
source_input_type: str, default "search_document"
source_input_type : str, default "search_document"
The input type for the source column in the database
query_input_type: str, default "search_query"
query_input_type : str, default "search_query"
The input type for the query column in the database
Notes
-----
Cohere supports following input types:
| Input Type | Description |
+2 -2
View File
@@ -44,7 +44,7 @@ class ColPaliEmbeddings(EmbeddingFunction):
The token pooling strategy to use, by default "hierarchical".
- "hierarchical": Progressively pools tokens to reduce sequence length.
- "lambda": A simpler pooling that uses a custom `pooling_func`.
pooling_func: typing.Callable, optional
pooling_func : typing.Callable, optional
A function to use for pooling when `pooling_strategy` is "lambda".
pool_factor : int
Factor to reduce sequence length if token pooling is enabled (default 2).
@@ -52,7 +52,7 @@ class ColPaliEmbeddings(EmbeddingFunction):
Quantization configuration for the model. (default None, bitsandbytes needed)
batch_size : int
Batch size for processing inputs (default 2).
offload_folder: str, optional
offload_folder : str, optional
Folder to offload model weights if using CPU offloading (default None). This is
useful for large models that do not fit in memory.
"""
@@ -48,16 +48,16 @@ class GeminiText(TextEmbeddingFunction):
Parameters
----------
name: str, default "gemini-embedding-001"
name : str, default "gemini-embedding-001"
The name of the model to use. Supported models include:
- "gemini-embedding-001" (768 dimensions)
Note: The legacy "models/embedding-001" format is also supported but
"gemini-embedding-001" is recommended.
query_task_type: str, default "retrieval_query"
query_task_type : str, default "retrieval_query"
Sets the task type for the queries.
source_task_type: str, default "retrieval_document"
source_task_type : str, default "retrieval_document"
Sets the task type for ingestion.
Examples
+4 -4
View File
@@ -26,13 +26,13 @@ class GteEmbeddings(TextEmbeddingFunction):
Parameters
----------
name: str, default "thenlper/gte-large"
name : str, default "thenlper/gte-large"
The name of the model to use.
device: str, default "cpu"
device : str, default "cpu"
Sets the device type for the model.
normalize: str, default "True"
normalize : str, default "True"
Controls normalize param in encode function for the transformer.
mlx: bool, default False
mlx : bool, default False
Controls which model to use. False for gte-large,True for the mlx version.
Examples
@@ -35,23 +35,23 @@ class InstructorEmbeddingFunction(TextEmbeddingFunction):
Parameters
----------
name: str
name : str
The name of the model to use. Available models are listed at
https://github.com/xlang-ai/instructor-embedding#model-list;
The default model is hkunlp/instructor-base
batch_size: int, default 32
batch_size : int, default 32
The batch size to use when generating embeddings
device: str, default "cpu"
device : str, default "cpu"
The device to use when generating embeddings
show_progress_bar: bool, default True
show_progress_bar : bool, default True
Whether to show a progress bar when generating embeddings
normalize_embeddings: bool, default True
normalize_embeddings : bool, default True
Whether to normalize the embeddings
quantize: bool, default False
quantize : bool, default False
Whether to quantize the model
source_instruction: str, default "represent the document for retrieval"
source_instruction : str, default "represent the document for retrieval"
The instruction for the source column
query_instruction: str, default "represent the document for retrieving the most
query_instruction : str, default "represent the document for retrieving the most
similar documents"
The instruction for the query

Some files were not shown because too many files have changed in this diff Show More