opencode-rag-plugin 1.21.0 → 1.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/init-helpers.d.ts +5 -2
- package/dist/cli/commands/init-helpers.js +26 -2
- package/dist/core/interfaces.d.ts +5 -0
- package/dist/indexer/pipeline.js +16 -5
- package/dist/vectorstore/lancedb.d.ts +34 -0
- package/dist/vectorstore/lancedb.js +155 -8
- package/dist/vectorstore/memory.d.ts +2 -0
- package/package.json +1 -1
|
@@ -21,8 +21,11 @@ export declare function buildWorkspacePackageJson(existing: Record<string, unkno
|
|
|
21
21
|
/**
|
|
22
22
|
* Build the `.opencode/opencode.json` config object.
|
|
23
23
|
*
|
|
24
|
-
* Ensures the `$schema` key is present and removes
|
|
25
|
-
*
|
|
24
|
+
* Ensures the `$schema` key is present and removes stale `plugin` entries
|
|
25
|
+
* (which would trigger erroneous npm installs) plus stale MCP entries that
|
|
26
|
+
* are not valid local/remote server configs (e.g. a leftover
|
|
27
|
+
* `mcp.<name>: { enabled: true }` that OpenCode ignores with
|
|
28
|
+
* "Ignoring MCP config entry without type").
|
|
26
29
|
*
|
|
27
30
|
* @param existing - The existing opencode.json content (if any).
|
|
28
31
|
* @returns The normalized config object.
|
|
@@ -49,8 +49,11 @@ export function buildWorkspacePackageJson(existing, packageMetadata) {
|
|
|
49
49
|
/**
|
|
50
50
|
* Build the `.opencode/opencode.json` config object.
|
|
51
51
|
*
|
|
52
|
-
* Ensures the `$schema` key is present and removes
|
|
53
|
-
*
|
|
52
|
+
* Ensures the `$schema` key is present and removes stale `plugin` entries
|
|
53
|
+
* (which would trigger erroneous npm installs) plus stale MCP entries that
|
|
54
|
+
* are not valid local/remote server configs (e.g. a leftover
|
|
55
|
+
* `mcp.<name>: { enabled: true }` that OpenCode ignores with
|
|
56
|
+
* "Ignoring MCP config entry without type").
|
|
54
57
|
*
|
|
55
58
|
* @param existing - The existing opencode.json content (if any).
|
|
56
59
|
* @returns The normalized config object.
|
|
@@ -65,6 +68,27 @@ export function buildOpencodeConfig(existing) {
|
|
|
65
68
|
// init versions would trigger npm install (which fails due to native
|
|
66
69
|
// dependencies like sharp) and produce "Plugin export is not a function".
|
|
67
70
|
delete next.plugin;
|
|
71
|
+
// Keep only MCP entries that look like real server configs (have a
|
|
72
|
+
// "type" of "local" or "remote"). Drop shorthand entries such as
|
|
73
|
+
// `{ enabled: true }` that OpenCode ignores at startup.
|
|
74
|
+
if (next.mcp && typeof next.mcp === "object" && !Array.isArray(next.mcp)) {
|
|
75
|
+
const mcp = next.mcp;
|
|
76
|
+
const valid = {};
|
|
77
|
+
for (const [name, value] of Object.entries(mcp)) {
|
|
78
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
79
|
+
const entry = value;
|
|
80
|
+
if (entry.type === "local" || entry.type === "remote") {
|
|
81
|
+
valid[name] = value;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (Object.keys(valid).length === 0) {
|
|
86
|
+
delete next.mcp;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
next.mcp = valid;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
68
92
|
return next;
|
|
69
93
|
}
|
|
70
94
|
/**
|
|
@@ -209,9 +209,14 @@ export interface VectorStore {
|
|
|
209
209
|
* accumulation.
|
|
210
210
|
* @param options - `aggressive: true` prunes all but the current version
|
|
211
211
|
* (only safe when no other process reads the store, e.g. temp rebuilds).
|
|
212
|
+
* `skipIndex: true` skips ANN index creation/repair (private temp stores
|
|
213
|
+
* are never searched — the rebuild pipeline builds the index once at the
|
|
214
|
+
* end). `logger` receives diagnostics instead of the console.
|
|
212
215
|
*/
|
|
213
216
|
optimize?(options?: {
|
|
214
217
|
aggressive?: boolean;
|
|
218
|
+
skipIndex?: boolean;
|
|
219
|
+
logger?: (message: string) => void;
|
|
215
220
|
}): Promise<void>;
|
|
216
221
|
/**
|
|
217
222
|
* Verify that the store's data is actually readable.
|
package/dist/indexer/pipeline.js
CHANGED
|
@@ -528,6 +528,11 @@ async function runIndexPassInner(options, logger) {
|
|
|
528
528
|
prep.chunks = undefined;
|
|
529
529
|
prep.textToEmbed = undefined;
|
|
530
530
|
}
|
|
531
|
+
// Number of files whose chunks were actually bulk-written this window.
|
|
532
|
+
// The caller uses this to skip mid-run compaction on idle passes that
|
|
533
|
+
// wrote nothing (no fragments were added, so nothing needs compacting,
|
|
534
|
+
// and the store avoids unnecessary index-repair churn).
|
|
535
|
+
return storePayloads.length;
|
|
531
536
|
}
|
|
532
537
|
// ── Windowed pipeline ────────────────────────────────────────────────────
|
|
533
538
|
// The prepare→describe→embed→store chain used to materialize ALL chunks,
|
|
@@ -833,12 +838,18 @@ async function runIndexPassInner(options, logger) {
|
|
|
833
838
|
// entries and skip every file ("Store phase: storing N → 0 files stored").
|
|
834
839
|
prevStore = storeWindow(prepared, new Map(earlyWorkerResults));
|
|
835
840
|
// Periodically compact fragments and prune old versions so the store
|
|
836
|
-
// phase doesn't slow down as the index grows during long runs.
|
|
841
|
+
// phase doesn't slow down as the index grows during long runs. Skipped on
|
|
842
|
+
// windows that wrote nothing — an idle pass (no changed files) adds no
|
|
843
|
+
// fragments, so compaction would only churn the index-repair path.
|
|
837
844
|
if (optimizeInterval > 0 && windowCount % optimizeInterval === 0) {
|
|
838
|
-
await prevStore;
|
|
845
|
+
const storedInWindow = await prevStore;
|
|
839
846
|
prevStore = null;
|
|
840
|
-
|
|
841
|
-
|
|
847
|
+
if (storedInWindow > 0) {
|
|
848
|
+
logger.info("Optimizing vector store (mid-run compaction, pruning old versions)...");
|
|
849
|
+
await effectiveStore.optimize?.(tempStorePath
|
|
850
|
+
? { aggressive: true, skipIndex: true, logger: logger.warn }
|
|
851
|
+
: { logger: logger.warn });
|
|
852
|
+
}
|
|
842
853
|
}
|
|
843
854
|
if (abortedInWindow)
|
|
844
855
|
break;
|
|
@@ -915,7 +926,7 @@ async function runIndexPassInner(options, logger) {
|
|
|
915
926
|
const optimizeStart = Date.now();
|
|
916
927
|
try {
|
|
917
928
|
const optimizeTarget = tempStorePath ? options.store : effectiveStore;
|
|
918
|
-
await optimizeTarget.optimize?.(tempStorePath ? { aggressive: true } :
|
|
929
|
+
await optimizeTarget.optimize?.(tempStorePath ? { aggressive: true, logger: logger.warn } : { logger: logger.warn });
|
|
919
930
|
logger.info(`Vector store optimized in ${((Date.now() - optimizeStart) / 1000).toFixed(1)}s`);
|
|
920
931
|
}
|
|
921
932
|
catch (err) {
|
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
import type { VectorStore, Chunk, ChunkSummary, SearchResult, MetadataFilter } from "../core/interfaces.js";
|
|
2
|
+
/** Minimal warning sink for store diagnostics (defaults to console.warn). */
|
|
3
|
+
export type StoreWarn = (message: string) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Count index-version directories in a LanceDB table directory. Each
|
|
6
|
+
* `createIndex` writes a new `<uuid>/` directory under `_indices`; a healthy
|
|
7
|
+
* store holds one per built index, while a store whose index commits fail
|
|
8
|
+
* accumulates one per attempt (and eventually degrades / corrupts).
|
|
9
|
+
*
|
|
10
|
+
* Empty directories are the husks of pruned versions (Lance removes the files
|
|
11
|
+
* but leaves the directory behind) and are harmless — they must not count
|
|
12
|
+
* toward the corruption threshold.
|
|
13
|
+
*
|
|
14
|
+
* @param tablePath - Filesystem path of the table directory (e.g. `.../chunks.lance`).
|
|
15
|
+
* @param options - `withFiles: true` counts only directories that still contain index files.
|
|
16
|
+
* @returns The number of index-version directories, or 0 when unavailable.
|
|
17
|
+
*/
|
|
18
|
+
export declare function countIndexVersionDirs(tablePath: string, options?: {
|
|
19
|
+
withFiles?: boolean;
|
|
20
|
+
}): number;
|
|
21
|
+
/**
|
|
22
|
+
* Remove stale empty index-version directories (husks of versions whose files
|
|
23
|
+
* Lance already pruned). Empty directories are never referenced by the index,
|
|
24
|
+
* so removing them is safe; only husks older than `MIN_HUSK_AGE_MS` are swept
|
|
25
|
+
* so a concurrent createIndex from another process can never lose its
|
|
26
|
+
* freshly-created (and momentarily empty) version directory.
|
|
27
|
+
*
|
|
28
|
+
* @param tablePath - Filesystem path of the table directory (e.g. `.../chunks.lance`).
|
|
29
|
+
* @returns The number of removed directories.
|
|
30
|
+
*/
|
|
31
|
+
export declare function sweepEmptyIndexVersionDirs(tablePath: string): number;
|
|
2
32
|
/**
|
|
3
33
|
* L2-normalize a vector to unit length. Cosine models require unit vectors
|
|
4
34
|
* for the dot product to equal cosine similarity.
|
|
@@ -62,6 +92,8 @@ export declare class LanceDbStore implements VectorStore {
|
|
|
62
92
|
* back to brute-force. This repairs that stale index on first search.
|
|
63
93
|
*/
|
|
64
94
|
private indexRepairPromise;
|
|
95
|
+
/** Consecutive failed repair attempts — bounded so a broken store cannot retrain forever. */
|
|
96
|
+
private indexRepairFailures;
|
|
65
97
|
/**
|
|
66
98
|
* Execute an async function under an exclusive write lock.
|
|
67
99
|
*
|
|
@@ -245,6 +277,8 @@ export declare class LanceDbStore implements VectorStore {
|
|
|
245
277
|
*/
|
|
246
278
|
optimize(options?: {
|
|
247
279
|
aggressive?: boolean;
|
|
280
|
+
skipIndex?: boolean;
|
|
281
|
+
logger?: StoreWarn;
|
|
248
282
|
}): Promise<void>;
|
|
249
283
|
/**
|
|
250
284
|
* Return all unique file paths currently stored in the index.
|
|
@@ -3,11 +3,102 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import * as lancedb from "@lancedb/lancedb";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
|
+
import fsSync from "node:fs";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import { normalizeFilePath, manifestPathFor } from "../core/manifest.js";
|
|
8
9
|
import { normalizeFileExtensions, matchesFileExtension } from "../core/filters.js";
|
|
9
10
|
const TABLE_NAME = "chunks";
|
|
10
11
|
const QUERY_COLUMNS = ["id", "content", "description", "filePath", "startLine", "endLine", "language", "kind", "quirkType", "tags"];
|
|
12
|
+
/**
|
|
13
|
+
* Upper bound for the number of failed index-creation attempts per process.
|
|
14
|
+
* Beyond this the repair gives up for the process lifetime instead of
|
|
15
|
+
* retraining the IVF index on every optimize/search call (each attempt runs
|
|
16
|
+
* a full KMeans training pass and logs "partition N is empty, skipping").
|
|
17
|
+
*/
|
|
18
|
+
const MAX_INDEX_REPAIR_ATTEMPTS = 3;
|
|
19
|
+
/**
|
|
20
|
+
* Upper bound for stale index-version directories under `chunks.lance/_indices`.
|
|
21
|
+
* Healthy stores have one per built index; a store whose index registration
|
|
22
|
+
* keeps failing accumulates one directory per attempt and never converges.
|
|
23
|
+
* Above this threshold the repair refuses to create yet another version and
|
|
24
|
+
* instead tells the user to rebuild the store.
|
|
25
|
+
*/
|
|
26
|
+
const MAX_STALE_INDEX_VERSIONS = 40;
|
|
27
|
+
/**
|
|
28
|
+
* Count index-version directories in a LanceDB table directory. Each
|
|
29
|
+
* `createIndex` writes a new `<uuid>/` directory under `_indices`; a healthy
|
|
30
|
+
* store holds one per built index, while a store whose index commits fail
|
|
31
|
+
* accumulates one per attempt (and eventually degrades / corrupts).
|
|
32
|
+
*
|
|
33
|
+
* Empty directories are the husks of pruned versions (Lance removes the files
|
|
34
|
+
* but leaves the directory behind) and are harmless — they must not count
|
|
35
|
+
* toward the corruption threshold.
|
|
36
|
+
*
|
|
37
|
+
* @param tablePath - Filesystem path of the table directory (e.g. `.../chunks.lance`).
|
|
38
|
+
* @param options - `withFiles: true` counts only directories that still contain index files.
|
|
39
|
+
* @returns The number of index-version directories, or 0 when unavailable.
|
|
40
|
+
*/
|
|
41
|
+
export function countIndexVersionDirs(tablePath, options) {
|
|
42
|
+
try {
|
|
43
|
+
const entries = fsSync.readdirSync(path.join(tablePath, "_indices"), { withFileTypes: true });
|
|
44
|
+
const dirs = entries.filter((e) => e.isDirectory());
|
|
45
|
+
if (!options?.withFiles)
|
|
46
|
+
return dirs.length;
|
|
47
|
+
let count = 0;
|
|
48
|
+
for (const dir of dirs) {
|
|
49
|
+
try {
|
|
50
|
+
if (fsSync.readdirSync(path.join(tablePath, "_indices", dir.name)).length > 0)
|
|
51
|
+
count++;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// unreadable dir — count it conservatively
|
|
55
|
+
count++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return count;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Remove stale empty index-version directories (husks of versions whose files
|
|
66
|
+
* Lance already pruned). Empty directories are never referenced by the index,
|
|
67
|
+
* so removing them is safe; only husks older than `MIN_HUSK_AGE_MS` are swept
|
|
68
|
+
* so a concurrent createIndex from another process can never lose its
|
|
69
|
+
* freshly-created (and momentarily empty) version directory.
|
|
70
|
+
*
|
|
71
|
+
* @param tablePath - Filesystem path of the table directory (e.g. `.../chunks.lance`).
|
|
72
|
+
* @returns The number of removed directories.
|
|
73
|
+
*/
|
|
74
|
+
export function sweepEmptyIndexVersionDirs(tablePath) {
|
|
75
|
+
const MIN_HUSK_AGE_MS = 10 * 60 * 1000;
|
|
76
|
+
let removed = 0;
|
|
77
|
+
try {
|
|
78
|
+
const indicesDir = path.join(tablePath, "_indices");
|
|
79
|
+
for (const name of fsSync.readdirSync(indicesDir)) {
|
|
80
|
+
const full = path.join(indicesDir, name);
|
|
81
|
+
try {
|
|
82
|
+
const stat = fsSync.statSync(full);
|
|
83
|
+
if (!stat.isDirectory())
|
|
84
|
+
continue;
|
|
85
|
+
if (Date.now() - stat.mtimeMs < MIN_HUSK_AGE_MS)
|
|
86
|
+
continue;
|
|
87
|
+
if (fsSync.readdirSync(full).length > 0)
|
|
88
|
+
continue;
|
|
89
|
+
fsSync.rmdirSync(full);
|
|
90
|
+
removed++;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// best-effort — skip unreadable/racing entries
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// best-effort
|
|
99
|
+
}
|
|
100
|
+
return removed;
|
|
101
|
+
}
|
|
11
102
|
/**
|
|
12
103
|
* L2-normalize a vector to unit length. Cosine models require unit vectors
|
|
13
104
|
* for the dot product to equal cosine similarity.
|
|
@@ -108,6 +199,8 @@ export class LanceDbStore {
|
|
|
108
199
|
* back to brute-force. This repairs that stale index on first search.
|
|
109
200
|
*/
|
|
110
201
|
indexRepairPromise = null;
|
|
202
|
+
/** Consecutive failed repair attempts — bounded so a broken store cannot retrain forever. */
|
|
203
|
+
indexRepairFailures = 0;
|
|
111
204
|
/**
|
|
112
205
|
* Execute an async function under an exclusive write lock.
|
|
113
206
|
*
|
|
@@ -817,9 +910,9 @@ export class LanceDbStore {
|
|
|
817
910
|
* Callers must hold the write lock (searchInternal wraps the call in
|
|
818
911
|
* withWriteLock; optimize runs under it already).
|
|
819
912
|
*/
|
|
820
|
-
ensureCosineIndex() {
|
|
913
|
+
ensureCosineIndex(warn) {
|
|
821
914
|
if (!this.indexRepairPromise) {
|
|
822
|
-
this.indexRepairPromise = this.repairIndexMetricOnce();
|
|
915
|
+
this.indexRepairPromise = this.repairIndexMetricOnce(warn);
|
|
823
916
|
}
|
|
824
917
|
return this.indexRepairPromise;
|
|
825
918
|
}
|
|
@@ -831,8 +924,25 @@ export class LanceDbStore {
|
|
|
831
924
|
* stale index in place. On failure the memo is cleared so the next
|
|
832
925
|
* search/optimize retries.
|
|
833
926
|
*/
|
|
834
|
-
async repairIndexMetricOnce() {
|
|
927
|
+
async repairIndexMetricOnce(warn) {
|
|
928
|
+
const report = (message) => {
|
|
929
|
+
(warn ?? console.warn)(message);
|
|
930
|
+
};
|
|
835
931
|
try {
|
|
932
|
+
// Guard against a store that never converges: each failed createIndex
|
|
933
|
+
// leaves a new index-version directory behind. Empty directories are
|
|
934
|
+
// the husks of already-pruned versions and are harmless — only
|
|
935
|
+
// versions that still carry index files count (see countIndexVersionDirs).
|
|
936
|
+
const activeVersions = this.dbPath.startsWith("memory://")
|
|
937
|
+
? 0
|
|
938
|
+
: countIndexVersionDirs(path.join(this.dbPath, TABLE_NAME + ".lance"), { withFiles: true });
|
|
939
|
+
if (activeVersions > MAX_STALE_INDEX_VERSIONS) {
|
|
940
|
+
report(`[lancedb] ${activeVersions} active index versions detected in ${this.dbPath} — ` +
|
|
941
|
+
`index rebuild skipped to avoid retrain churn. The store cannot converge; ` +
|
|
942
|
+
`delete the rag_db directory and re-index.`);
|
|
943
|
+
// Resolve the memo so no further attempts run this process.
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
836
946
|
const table = await this.getTable();
|
|
837
947
|
const count = await table.countRows().catch(() => 0);
|
|
838
948
|
const indices = await table.listIndices();
|
|
@@ -854,6 +964,17 @@ export class LanceDbStore {
|
|
|
854
964
|
// race a background index-creation from another process.
|
|
855
965
|
waitTimeoutSeconds: 120,
|
|
856
966
|
});
|
|
967
|
+
// Verify the index actually registered. On a store whose index
|
|
968
|
+
// commits never register, createIndex can return successfully
|
|
969
|
+
// while leaving the table without a visible index — the failure
|
|
970
|
+
// mode that caused constant retraining and "partition N is empty,
|
|
971
|
+
// skipping" warning spam. Detect it and stop retraining.
|
|
972
|
+
const verify = await table.indexStats(idxName).catch(() => undefined);
|
|
973
|
+
if (!verify || verify.distanceType !== "cosine") {
|
|
974
|
+
throw new Error(`index build did not register (store cannot converge) — ` +
|
|
975
|
+
`delete the rag_db directory and re-index`);
|
|
976
|
+
}
|
|
977
|
+
this.indexRepairFailures = 0;
|
|
857
978
|
return;
|
|
858
979
|
}
|
|
859
980
|
catch (err) {
|
|
@@ -866,9 +987,19 @@ export class LanceDbStore {
|
|
|
866
987
|
}
|
|
867
988
|
}
|
|
868
989
|
catch (err) {
|
|
990
|
+
this.indexRepairFailures++;
|
|
991
|
+
const message = `[lancedb] index metric repair failed (queries continue in brute-force): ` +
|
|
992
|
+
`${err instanceof Error ? err.message : String(err)}`;
|
|
993
|
+
if (this.indexRepairFailures >= MAX_INDEX_REPAIR_ATTEMPTS) {
|
|
994
|
+
// Stop retrying for this process — a store that fails repeatedly would
|
|
995
|
+
// otherwise be retrained (with KMeans "partition empty" warning spam)
|
|
996
|
+
// on every optimize() and search() call.
|
|
997
|
+
report(`${message} — giving up after ${this.indexRepairFailures} attempts this session. ` +
|
|
998
|
+
`Delete the rag_db directory and re-index.`);
|
|
999
|
+
return; // memo stays resolved: no further attempts this process
|
|
1000
|
+
}
|
|
869
1001
|
this.indexRepairPromise = null; // allow retry on a later search/optimize
|
|
870
|
-
|
|
871
|
-
`${err instanceof Error ? err.message : String(err)}`);
|
|
1002
|
+
report(message);
|
|
872
1003
|
}
|
|
873
1004
|
}
|
|
874
1005
|
/**
|
|
@@ -894,15 +1025,31 @@ export class LanceDbStore {
|
|
|
894
1025
|
const threshold = new Date(Date.now() - 60 * 60 * 1000);
|
|
895
1026
|
await table.optimize({ cleanupOlderThan: threshold, deleteUnverified: false });
|
|
896
1027
|
}
|
|
1028
|
+
// Remove stale empty index-version directories (husks Lance leaves
|
|
1029
|
+
// behind after pruning a version's files). Keeps the directory count
|
|
1030
|
+
// from growing unboundedly and prevents false positives in the
|
|
1031
|
+
// index-repair stale-version guard.
|
|
1032
|
+
if (!this.dbPath.startsWith("memory://")) {
|
|
1033
|
+
const swept = sweepEmptyIndexVersionDirs(path.join(this.dbPath, TABLE_NAME + ".lance"));
|
|
1034
|
+
if (swept > 0) {
|
|
1035
|
+
const message = `[lancedb] swept ${swept} stale index-version directories`;
|
|
1036
|
+
(options?.logger ?? console.warn)(message);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
897
1039
|
// Build the ANN vector index — without it every vectorSearch() is a
|
|
898
1040
|
// brute-force O(N) flat scan (slow at 50k+ chunks). Skips early when
|
|
899
|
-
// an index with the correct cosine metric already exists
|
|
900
|
-
|
|
1041
|
+
// an index with the correct cosine metric already exists, and can be
|
|
1042
|
+
// skipped entirely for a private temporary store that is never
|
|
1043
|
+
// searched (the rebuild pipeline builds the index once at the end).
|
|
1044
|
+
if (!options?.skipIndex) {
|
|
1045
|
+
await this.ensureCosineIndex(options?.logger);
|
|
1046
|
+
}
|
|
901
1047
|
}
|
|
902
1048
|
catch (err) {
|
|
903
1049
|
// Optimize is best-effort — must not break indexing, but surface the
|
|
904
1050
|
// failure instead of swallowing it silently.
|
|
905
|
-
|
|
1051
|
+
const message = `[lancedb] optimize failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
1052
|
+
(options?.logger ?? console.warn)(message);
|
|
906
1053
|
}
|
|
907
1054
|
});
|
|
908
1055
|
}
|