@aeriondyseti/vector-memory-mcp 2.3.0-rc.2 → 2.3.0-rc.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aeriondyseti/vector-memory-mcp",
3
- "version": "2.3.0-rc.2",
3
+ "version": "2.3.0-rc.3",
4
4
  "description": "A zero-configuration RAG memory server for MCP clients",
5
5
  "type": "module",
6
6
  "main": "server/index.ts",
@@ -127,8 +127,43 @@ export async function backfillVectors(
127
127
  db: Database,
128
128
  embeddings: EmbeddingsService,
129
129
  ): Promise<void> {
130
+ // Fast sentinel check: skip the LEFT JOIN queries entirely when backfill is done
131
+ const sentinel = db
132
+ .prepare("SELECT 1 FROM memories_vec LIMIT 1")
133
+ .get();
134
+ const memoriesExist = db.prepare("SELECT 1 FROM memories LIMIT 1").get();
135
+ const convosExist = db.prepare("SELECT 1 FROM conversation_history LIMIT 1").get();
136
+
137
+ // If vec tables have data and source tables have data, backfill is likely complete.
138
+ // Only run the expensive LEFT JOIN when there's reason to suspect gaps.
139
+ const convoSentinel = db
140
+ .prepare("SELECT 1 FROM conversation_history_vec LIMIT 1")
141
+ .get();
142
+ const mayNeedMemoryBackfill = memoriesExist && !sentinel;
143
+ const mayNeedConvoBackfill = convosExist && !convoSentinel;
144
+
145
+ // If both vec tables are populated, do a quick count check to confirm
146
+ if (!mayNeedMemoryBackfill && !mayNeedConvoBackfill) {
147
+ if (memoriesExist) {
148
+ const gap = db.prepare(
149
+ `SELECT 1 FROM memories m LEFT JOIN memories_vec v ON m.id = v.id
150
+ WHERE v.id IS NULL OR length(v.vector) = 0 LIMIT 1`,
151
+ ).get();
152
+ if (!gap && convosExist) {
153
+ const convoGap = db.prepare(
154
+ `SELECT 1 FROM conversation_history c LEFT JOIN conversation_history_vec v ON c.id = v.id
155
+ WHERE v.id IS NULL OR length(v.vector) = 0 LIMIT 1`,
156
+ ).get();
157
+ if (!convoGap) return;
158
+ } else if (!gap && !convosExist) {
159
+ return;
160
+ }
161
+ } else {
162
+ return; // No data at all
163
+ }
164
+ }
165
+
130
166
  // ── Memories ──────────────────────────────────────────────────────
131
- // Catch both missing rows (v.id IS NULL) and corrupt 0-byte BLOBs
132
167
  const missingMemories = db
133
168
  .prepare(
134
169
  `SELECT m.id, m.content, json_extract(m.metadata, '$.type') AS type
@@ -151,14 +186,27 @@ export async function backfillVectors(
151
186
  new Array(embeddings.dimension).fill(0),
152
187
  );
153
188
 
154
- for (const row of missingMemories) {
155
- // Waypoints use a zero vector (not semantically searched)
156
- const blob =
157
- row.type === "waypoint"
158
- ? zeroVector
159
- : serializeVector(await embeddings.embed(row.content));
189
+ // Separate waypoints from content that needs embedding
190
+ const toEmbed = missingMemories.filter((r) => r.type !== "waypoint");
191
+ const waypoints = missingMemories.filter((r) => r.type === "waypoint");
192
+
193
+ // Batch embed all non-waypoint content
194
+ const vectors = toEmbed.length > 0
195
+ ? await embeddings.embedBatch(toEmbed.map((r) => r.content))
196
+ : [];
160
197
 
161
- insertVec.run(row.id, blob);
198
+ db.exec("BEGIN");
199
+ try {
200
+ for (const row of waypoints) {
201
+ insertVec.run(row.id, zeroVector);
202
+ }
203
+ for (let i = 0; i < toEmbed.length; i++) {
204
+ insertVec.run(toEmbed[i].id, serializeVector(vectors[i]));
205
+ }
206
+ db.exec("COMMIT");
207
+ } catch (e) {
208
+ db.exec("ROLLBACK");
209
+ throw e;
162
210
  }
163
211
 
164
212
  console.error(
@@ -185,17 +233,27 @@ export async function backfillVectors(
185
233
  "INSERT OR REPLACE INTO conversation_history_vec (id, vector) VALUES (?, ?)",
186
234
  );
187
235
 
188
- for (let i = 0; i < missingConvos.length; i++) {
189
- const row = missingConvos[i];
190
- const vec = serializeVector(await embeddings.embed(row.content));
191
- insertConvoVec.run(row.id, vec);
236
+ // Batch embed in chunks of 32
237
+ const BATCH_SIZE = 32;
238
+ db.exec("BEGIN");
239
+ try {
240
+ for (let i = 0; i < missingConvos.length; i += BATCH_SIZE) {
241
+ const batch = missingConvos.slice(i, i + BATCH_SIZE);
242
+ const vecs = await embeddings.embedBatch(batch.map((r) => r.content));
243
+ for (let j = 0; j < batch.length; j++) {
244
+ insertConvoVec.run(batch[j].id, serializeVector(vecs[j]));
245
+ }
192
246
 
193
- // Log progress every 100 chunks
194
- if ((i + 1) % 100 === 0) {
195
- console.error(
196
- `[vector-memory-mcp] ...${i + 1}/${missingConvos.length} conversation chunks`,
197
- );
247
+ if ((i + BATCH_SIZE) % 100 < BATCH_SIZE) {
248
+ console.error(
249
+ `[vector-memory-mcp] ...${Math.min(i + BATCH_SIZE, missingConvos.length)}/${missingConvos.length} conversation chunks`,
250
+ );
251
+ }
198
252
  }
253
+ db.exec("COMMIT");
254
+ } catch (e) {
255
+ db.exec("ROLLBACK");
256
+ throw e;
199
257
  }
200
258
 
201
259
  console.error(
package/server/index.ts CHANGED
@@ -25,17 +25,15 @@ async function main(): Promise<void> {
25
25
  const overrides = parseCliArgs(args);
26
26
  const config = loadConfig(overrides);
27
27
 
28
- // Initialize database
28
+ // Initialize database and backfill any missing vectors before services start
29
29
  const db = connectToDatabase(config.dbPath);
30
+ const embeddings = new EmbeddingsService(config.embeddingModel, config.embeddingDimension);
31
+ await backfillVectors(db, embeddings);
30
32
 
31
33
  // Initialize layers
32
34
  const repository = new MemoryRepository(db);
33
- const embeddings = new EmbeddingsService(config.embeddingModel, config.embeddingDimension);
34
35
  const memoryService = new MemoryService(repository, embeddings);
35
36
 
36
- // Backfill any missing vectors (e.g. after vec0-to-BLOB migration)
37
- await backfillVectors(db, embeddings);
38
-
39
37
  if (config.pluginMode) {
40
38
  console.error("[vector-memory-mcp] Running in plugin mode");
41
39
  }