From 22d8748af2e858681929cbcdd4465321be2c4102 Mon Sep 17 00:00:00 2001 From: baizenghu Date: Mon, 9 Mar 2026 23:51:20 -0700 Subject: [PATCH 1/2] feat: add includeVectors option to list() and include vectors in backup JSONL Backup JSONL now contains vector data so restores work without re-embedding (critical for intranet environments where embedding API may be unavailable). Backup files include a _meta header line with model/dimensions/timestamp. list() gains an optional includeVectors parameter (default false) to keep existing callers unaffected. Co-Authored-By: Claude Opus 4.6 --- index.ts | 18 +++++++++++++++--- src/store.ts | 29 +++++++++++++++++++---------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/index.ts b/index.ts index 4aeefb8..9ebfabd 100644 --- a/index.ts +++ b/index.ts @@ -2555,16 +2555,28 @@ const memoryLanceDBProPlugin = { ); await mkdir(backupDir, { recursive: true }); - const allMemories = await store.list(undefined, undefined, 10000, 0); + // Include vectors so backup can be restored without re-embedding + const allMemories = await store.list(undefined, undefined, 10000, 0, true); if (allMemories.length === 0) return; const dateStr = new Date().toISOString().split("T")[0]; const backupFile = join(backupDir, `memory-backup-${dateStr}.jsonl`); - const lines = allMemories.map((m) => + // First line: metadata header for restore compatibility + const embeddingModel = config.embedding.model || "text-embedding-3-small"; + const metaLine = JSON.stringify({ + _meta: true, + version: "1.1", + model: embeddingModel, + dimensions: vectorDim, + timestamp: new Date().toISOString(), + }); + + const dataLines = allMemories.map((m) => JSON.stringify({ id: m.id, text: m.text, + vector: m.vector, category: m.category, scope: m.scope, importance: m.importance, @@ -2573,7 +2585,7 @@ const memoryLanceDBProPlugin = { }), ); - await writeFile(backupFile, lines.join("\n") + "\n"); + await writeFile(backupFile, metaLine + "\n" + dataLines.join("\n") + "\n"); // Keep only last 7 backups const files = (await readdir(backupDir)) diff --git a/src/store.ts b/src/store.ts index 454141e..4374927 100644 --- a/src/store.ts +++ b/src/store.ts @@ -592,6 +592,7 @@ export class MemoryStore { category?: string, limit = 20, offset = 0, + includeVectors = false, ): Promise { await this.ensureInitialized(); @@ -615,17 +616,23 @@ export class MemoryStore { query = query.where(conditions.join(" AND ")); } + // Select columns - optionally include the vector column for backup/export + const selectCols = [ + "id", + "text", + "category", + "scope", + "importance", + "timestamp", + "metadata", + ]; + if (includeVectors) { + selectCols.push("vector"); + } + // Fetch all matching rows (no pre-limit) so app-layer sort is correct across full dataset const results = await query - .select([ - "id", - "text", - "category", - "scope", - "importance", - "timestamp", - "metadata", - ]) + .select(selectCols) .toArray(); return results @@ -633,7 +640,9 @@ export class MemoryStore { (row): MemoryEntry => ({ id: row.id as string, text: row.text as string, - vector: [], // Don't include vectors in list results for performance + vector: includeVectors + ? (Array.isArray(row.vector) ? (row.vector as number[]) : []) + : [], // Don't include vectors in list results for performance category: row.category as MemoryEntry["category"], scope: (row.scope as string | undefined) ?? "global", importance: Number(row.importance), From 1bbb1200ad80de6810b325201e7e2f06d814b974 Mon Sep 17 00:00:00 2001 From: baizenghu Date: Tue, 10 Mar 2026 00:02:23 -0700 Subject: [PATCH 2/2] fix: use Array.from() for LanceDB typed array vectors in list() LanceDB returns Float32Array (not plain Array), so Array.isArray() returns false. Use Array.from() to correctly convert typed arrays when includeVectors is true. Co-Authored-By: Claude Opus 4.6 --- src/store.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/store.ts b/src/store.ts index 4374927..33c7c5a 100644 --- a/src/store.ts +++ b/src/store.ts @@ -640,9 +640,9 @@ export class MemoryStore { (row): MemoryEntry => ({ id: row.id as string, text: row.text as string, - vector: includeVectors - ? (Array.isArray(row.vector) ? (row.vector as number[]) : []) - : [], // Don't include vectors in list results for performance + vector: includeVectors && row.vector + ? Array.from(row.vector as Iterable) + : [], category: row.category as MemoryEntry["category"], scope: (row.scope as string | undefined) ?? "global", importance: Number(row.importance),