Skip to content

feat: support deferred embedding via update/RecordPatch (#88)#93

Merged
beinan merged 1 commit into
lance-format:mainfrom
dcfocus:feat/issue-88-deferred-embedding
Jun 15, 2026
Merged

feat: support deferred embedding via update/RecordPatch (#88)#93
beinan merged 1 commit into
lance-format:mainfrom
dcfocus:feat/issue-88-deferred-embedding

Conversation

@dcfocus

@dcfocus dcfocus commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Lets callers append raw records first and enrich them with embeddings later — the common pattern for bulk ingestion where source chunks must be persisted immediately but embeddings are computed asynchronously (large documents, rate-limited or remote embedding providers).

Adds an embedding field to the record patch path so a vector can be attached or replaced by id or external_id after the original write. A record without an embedding is durably stored but excluded from vector search until enriched (search skips null-embedding records).

Closes #88.

What changed

  • coreRecordPatch.embedding, applied in update_visible_record.
  • apiRecordPatchDto.embedding; copied through in both the core and server patch_from_dto.
  • pythonembedding parameter on the PyO3 update and Context.update().
  • Rust client — inherits the field automatically via the UpdateRecordRequest JSON body.
  • README — documents the raw-first → enrich-later pattern.

Tests

  • Rust core round-trip (enrich by id and by external_id).
  • Python e2e: raw-first → enrich, including add_many bulk ingestion.

🤖 Generated with Claude Code

)

Append raw records first and enrich them with embeddings later. Adds an
`embedding` field to the record patch path so a vector can be attached or
replaced by id or external_id after the original write.

- core: RecordPatch.embedding, applied in update_visible_record
- api: RecordPatchDto.embedding; copied in core + server patch_from_dto
- python: embedding param on PyO3 update and Context.update()
- Rust client inherits the field via the UpdateRecordRequest JSON body

A record without an embedding is durably stored but excluded from vector
search until enriched, since search skips null-embedding records.

Tests: Rust core round-trip (by id + external_id) and Python e2e
(raw-first -> enrich, incl. add_many bulk). README documents the pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@beinan beinan merged commit ecc91f2 into lance-format:main Jun 15, 2026
9 checks passed
beinan pushed a commit that referenced this pull request Jun 15, 2026
…) (#92)

## Summary

Brings the REST `search` and `list` endpoints to parity with the core
and Python APIs by adding `filters`, `include_expired`, and
`include_retired`, reusing the existing `RecordFilters` JSON semantics
and `LifecycleQueryOptions` — mirroring the plumbing already used by
REST `retrieve`. No new query language, no SQL, and no change to default
visibility for expired/retired/superseded records.

Closes #89.

> ### ⚠️ Stacked on #93 — review/merge that first
> This branch is stacked on top of #93 (deferred embedding, #88). Until
#93 lands on `main`, the diff below shows **both** commits (`b336bdc` is
#93's work; `1aae34a` is this PR's #89 work). Once #93 merges I'll
rebase this branch onto `main` so the diff is #89-only. Please review
the second commit for #89.

## What changed (#89)

- **`lance-context-api`** — `SearchRequest` gains `filters` /
`include_expired` / `include_retired` (legacy `{query, limit}` payloads
still deserialize). The `ContextStoreApi` trait now takes
`search(&SearchRequest)` and `list(limit, offset, filters,
include_expired, include_retired)` (breaking change to the trait;
acceptable pre-1.0).
- **`lance-context-core`** — trait `search`/`list` route through the
existing `search_filtered_with_options` / `list_filtered_with_options`.
- **`lance-context-server`** — `search` applies filters + lifecycle;
`list_records` accepts `filters` (URL JSON string) plus
`include_expired` / `include_retired` query params, matching the
existing `related_records` style.
- **`lance-context` (unified) + `lance-context-client`** — dispatch and
`RemoteContextStore` updated; the low-level client `list_records` sends
the new query params. Adds a `serde_json` dependency to the
`lance-context` crate (the trait now exposes `serde_json::Value`).

## Tests

- 8 new server route tests — `search` and `list`, each covering:
metadata filter, built-in field filter, `include_expired`,
`include_retired` (superseded), and `include_relationships` toggling.
- A `list` invalid-filters-JSON rejection test.
- 3 `lance-context-api` serde tests, including backward compatibility
for the pre-#89 `SearchRequest` payload.

## Verification

- `cargo fmt --all -- --check`
- `cargo clippy --workspace --all-targets -- -D warnings`
- `cargo test`: core (44), server (14, incl. 8 new), api (3) — all
green.
- Python untouched (already had filter/lifecycle parity).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support deferred embedding workflows for bulk ingestion

2 participants