mirror of
https://github.com/lancedb/lancedb.git
synced 2026-10-30 15:26:52 +00:00
Closes #1849 This was lancedb/docs#371. After lancedb/docs#355 moved the open-source pages, their navigation and the snippet tests into this repository's `docs/web` and `docs/web-tests`, that PR could no longer merge there, so I've moved the same change here. ## Why #1849 asks for an example of LanceDB in a VS Code extension, because people kept running into problems there (#927, #1713). An earlier attempt, #3682, added a README link. @prrao87 asked for something else instead: a quick guide on the docs site, under **Integrations › AI Platforms & Frameworks**, showing how to use LanceDB in an extension and what results look like. The author of #3682 agreed and closed it. This PR is that guide. ## What it adds - **`docs/web/integrations/ai/vscode.mdx`**: a new page, also added to `docs/web/docs.json`. It builds a small extension with two commands. One indexes the workspace's Markdown into a LanceDB table using the built-in `huggingface` embedding function, so no API key is needed. The other searches it by meaning. The page covers the parts that are specific to VS Code: - storing the table under `context.storageUri` instead of a relative path, which is the cause of #927; - `@types/node` ≥ 22 and `skipLibCheck`, which covers #1713 and the DOM-type errors from `apache-arrow`/onnxruntime typings; - pinning `apache-arrow` inside LanceDB's peer range; - marking `@lancedb/lancedb` and `@huggingface/transformers` as esbuild externals, and removing `node_modules/**` from the template's `.vscodeignore`; - storing each file's URI so a hit opens in the right folder of a multi-root workspace; - building one VSIX per platform with `vsce package --target`, with a table mapping each target to LanceDB's native package, and a note that the sample's local model (via `onnxruntime-node`) doesn't run on Alpine; - keeping the Transformers.js model cache in `globalStorageUri`, which only works if it's loaded with `import()`; - a troubleshooting table listing each error message with its cause and fix. - **`docs/web-tests/ts/vscode_search.ts`** and **`vscode_search.test.ts`**: the LanceDB code the page shows (connect, chunk, index, search), plus jest tests for it. - **`docs/web-tests/ts/vscode_extension.ts`**: the extension's `src/extension.ts`. Jest can't run it because it needs the VS Code extension host, so it was tested in a real extension instead (see below). This follows the precedent of `integrations.ts`. - **`docs/web/snippets/vscode_search.mdx`** and **`vscode_extension.mdx`**: generated with the command in CONTRIBUTING.md. ## Changes from lancedb/docs#371 - **New paths only.** The page, tests and snippets are the same content, at their paths in this repository. - **One link.** `/tables/update` doesn't exist under `docs/web`, so the `mergeInsert` link now points to `/ingestion/merge-insert`. - **Heading anchors.** Every heading now has an explicit `{#anchor}`, as CONTRIBUTING.md asks. ## How I tested it - **Snippet tests.** In this repository at `c2c8269f`, `vscode_search.test.ts` passes (2 tests) with the pinned `@lancedb/lancedb` 0.31.0. On Windows the `npm test` script fails before it starts, because it runs `node` on the `.bin/jest` shell shim. I ran `node --experimental-vm-modules node_modules/jest/bin/jest.js --testEnvironment jest-environment-node-single-context vscode_search` instead. - **Lint.** `biome ci` is clean for the three new files, and they carry the SPDX header that `license-header-check.yml` looks for. - **Snippets.** I regenerated them with CONTRIBUTING.md's command, `mdx_snippets_gen.py -s docs/web-tests/py -s docs/web-tests/ts -s docs/web-tests/rs -o docs/web/snippets`, run with `python` since I don't have `uv`. It reproduces the two new snippet files exactly, once the Windows CRLF endings are normalised. As CONTRIBUTING.md warns, it also rewrites `build_with_ai_agents.mdx`, `integrations.mdx`, `search.mdx` and `storage.mdx`. I left those out. - **Links.** `mint broken-links` from `docs/web` reports no entries on the new page, and its three internal links (`/embedding/quickstart`, `/ingestion/merge-insert`, `/search/full-text-search`) resolve to pages here. - **Real extension.** I built a sample extension from exactly the code on the page, scaffolded with `yo code` (TypeScript + esbuild) and using `@lancedb/lancedb` 0.39.0. I ran it with `@vscode/test-cli` in VS Code 1.140 on Windows x64. The integration test runs both commands against a six-file workspace and checks the top hits. The "Sample results" block on the page is copied from that run, and after the review fixes below it reproduces line for line in VS Code 1.141. - **Multi-root workspace.** A second test configuration opens two folders that both contain `docs/auth.md`. It types a query and picks the top hit through the real picker, then checks that the right folder's file opens at the right line. It fails on the code before the review fix and passes after it. - **Packaged VSIX.** I packaged `vsce package --target win32-x64` (200 MB, or 148 MB with the onnxruntime trimming in the Tip). Then I unpacked it outside the project and ran the same tests against the unpacked copy, so they couldn't pick up the project's `node_modules`. They pass. - **Negative checks behind the troubleshooting rows:** - With `node_modules/**` left in `.vscodeignore`, activation fails with `Cannot find module '@lancedb/lancedb'` and the commands report `command '…' not found`. - Without the externals, esbuild fails with `No loader is configured for ".node" files`. - Installing LanceDB into a project that already has `apache-arrow` 21 fails with `ERESOLVE`. - `require("@huggingface/transformers")` returns a different `env` object from `import()`. - **Rendering.** For lancedb/docs#371, `mint dev` rendered the page, its 14 code blocks and the sidebar entry. I haven't re-rendered it from `docs/web`; only the paths, one link and the heading anchors changed. **Not tested:** - macOS. I couldn't reproduce the read-only working directory from #927; the page's fix (an absolute storage path) doesn't depend on the OS. - Linux and macOS VSIX builds. ## Notes for reviewers - I added `_distance` to `.select(...)`, because LanceDB warns that it will stop adding the column automatically. ## AI disclosure I used Claude Code to research the issue and its history, write the sample extension, the tests and this page, move the change here, and draft this description. I ran every check listed above myself, and the outputs quoted on the page come from those runs. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
118 lines
4.0 KiB
TypeScript
118 lines
4.0 KiB
TypeScript
// SPDX-License-Identifier: Apache-2.0
|
|
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
|
|
|
// The `src/extension.ts` of the sample extension in the VS Code integration guide.
|
|
// It needs the VS Code extension host, so jest doesn't run it: the LanceDB code it
|
|
// calls is tested in vscode_search.test.ts, and the extension itself was tested
|
|
// with @vscode/test-cli.
|
|
|
|
// --8<-- [start:vscode_extension]
|
|
import * as vscode from "vscode";
|
|
import {
|
|
type Chunk,
|
|
chunkMarkdown,
|
|
indexChunks,
|
|
openDatabase,
|
|
searchChunks,
|
|
} from "./search";
|
|
|
|
export async function activate(context: vscode.ExtensionContext) {
|
|
// One index per workspace. `storageUri` is undefined when no folder is open.
|
|
const storage = context.storageUri ?? context.globalStorageUri;
|
|
|
|
// Transformers.js downloads the embedding model on first use. Keep it in
|
|
// global storage so every workspace shares one copy and it survives
|
|
// extension updates. Use `import()`: LanceDB loads the ES module build, and
|
|
// `require()` would return a separate copy whose settings LanceDB never sees.
|
|
const { env } = await import("@huggingface/transformers");
|
|
env.cacheDir = vscode.Uri.joinPath(context.globalStorageUri, "models").fsPath;
|
|
|
|
context.subscriptions.push(
|
|
vscode.commands.registerCommand("lancedbSearch.index", async () => {
|
|
const files = await vscode.workspace.findFiles(
|
|
"**/*.md",
|
|
"**/node_modules/**",
|
|
);
|
|
const chunks: Chunk[] = [];
|
|
for (const file of files) {
|
|
const bytes = await vscode.workspace.fs.readFile(file);
|
|
// Store the file's URI, not a path relative to its folder: in a
|
|
// multi-root workspace `findFiles` searches every folder, and only the
|
|
// URI says which one a result came from.
|
|
chunks.push(
|
|
...chunkMarkdown(file.toString(), new TextDecoder().decode(bytes)),
|
|
);
|
|
}
|
|
if (chunks.length === 0) {
|
|
vscode.window.showWarningMessage("No Markdown files to index.");
|
|
return 0;
|
|
}
|
|
|
|
await vscode.window.withProgress(
|
|
{
|
|
location: vscode.ProgressLocation.Notification,
|
|
title: `Indexing ${files.length} Markdown files`,
|
|
},
|
|
async () => {
|
|
await vscode.workspace.fs.createDirectory(storage);
|
|
await indexChunks(await openDatabase(storage.fsPath), chunks);
|
|
},
|
|
);
|
|
vscode.window.showInformationMessage(
|
|
`Indexed ${chunks.length} paragraphs from ${files.length} files.`,
|
|
);
|
|
return chunks.length;
|
|
}),
|
|
|
|
// Called with a query (for example from a test or another extension), the
|
|
// command returns the matching rows instead of showing a picker.
|
|
vscode.commands.registerCommand(
|
|
"lancedbSearch.search",
|
|
async (query?: string) => {
|
|
const text =
|
|
query ??
|
|
(await vscode.window.showInputBox({
|
|
prompt: "Search Markdown by meaning",
|
|
}));
|
|
if (!text) {
|
|
return [];
|
|
}
|
|
const results = await searchChunks(
|
|
await openDatabase(storage.fsPath),
|
|
text,
|
|
);
|
|
if (query !== undefined) {
|
|
return results;
|
|
}
|
|
|
|
const picked = await vscode.window.showQuickPick(
|
|
results.map((row) => {
|
|
const uri = vscode.Uri.parse(row.path);
|
|
return {
|
|
label: `${vscode.workspace.asRelativePath(uri)}:${row.line + 1}`,
|
|
description: `distance ${row._distance.toFixed(3)}`,
|
|
detail: row.text,
|
|
uri,
|
|
line: row.line,
|
|
};
|
|
}),
|
|
{ placeHolder: text, matchOnDetail: true },
|
|
);
|
|
if (picked) {
|
|
const editor = await vscode.window.showTextDocument(picked.uri);
|
|
const start = new vscode.Position(picked.line, 0);
|
|
editor.selection = new vscode.Selection(start, start);
|
|
editor.revealRange(
|
|
new vscode.Range(start, start),
|
|
vscode.TextEditorRevealType.InCenter,
|
|
);
|
|
}
|
|
return results;
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
export function deactivate() {}
|
|
// --8<-- [end:vscode_extension]
|