@mastra/lance 0.0.0-error-handler-fix-20251020202607 → 0.0.0-execa-dynamic-import-20260304221256

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +1570 -3
  2. package/LICENSE.md +15 -0
  3. package/README.md +61 -4
  4. package/dist/docs/SKILL.md +27 -0
  5. package/dist/docs/assets/SOURCE_MAP.json +6 -0
  6. package/dist/docs/references/docs-rag-vector-databases.md +645 -0
  7. package/dist/docs/references/reference-storage-lance.md +131 -0
  8. package/dist/docs/references/reference-vectors-lance.md +220 -0
  9. package/dist/index.cjs +1719 -1786
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.js +1717 -1787
  12. package/dist/index.js.map +1 -1
  13. package/dist/storage/{domains/operations → db}/index.d.ts +21 -2
  14. package/dist/storage/db/index.d.ts.map +1 -0
  15. package/dist/storage/db/utils.d.ts.map +1 -0
  16. package/dist/storage/domains/memory/index.d.ts +21 -46
  17. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  18. package/dist/storage/domains/scores/index.d.ts +24 -27
  19. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  20. package/dist/storage/domains/workflows/index.d.ts +21 -24
  21. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  22. package/dist/storage/index.d.ts +103 -246
  23. package/dist/storage/index.d.ts.map +1 -1
  24. package/dist/vector/filter.d.ts +5 -5
  25. package/dist/vector/index.d.ts +29 -11
  26. package/dist/vector/index.d.ts.map +1 -1
  27. package/package.json +17 -13
  28. package/dist/storage/domains/legacy-evals/index.d.ts +0 -25
  29. package/dist/storage/domains/legacy-evals/index.d.ts.map +0 -1
  30. package/dist/storage/domains/operations/index.d.ts.map +0 -1
  31. package/dist/storage/domains/traces/index.d.ts +0 -34
  32. package/dist/storage/domains/traces/index.d.ts.map +0 -1
  33. package/dist/storage/domains/utils.d.ts.map +0 -1
  34. /package/dist/storage/{domains → db}/utils.d.ts +0 -0
package/dist/index.cjs CHANGED
@@ -4,132 +4,17 @@ var lancedb = require('@lancedb/lancedb');
4
4
  var error = require('@mastra/core/error');
5
5
  var storage = require('@mastra/core/storage');
6
6
  var agent = require('@mastra/core/agent');
7
+ var base = require('@mastra/core/base');
7
8
  var apacheArrow = require('apache-arrow');
8
- var scores = require('@mastra/core/scores');
9
+ var evals = require('@mastra/core/evals');
9
10
  var vector = require('@mastra/core/vector');
10
11
  var filter = require('@mastra/core/vector/filter');
11
12
 
12
13
  // src/storage/index.ts
13
- var StoreLegacyEvalsLance = class extends storage.LegacyEvalsStorage {
14
- client;
15
- constructor({ client }) {
16
- super();
17
- this.client = client;
18
- }
19
- async getEvalsByAgentName(agentName, type) {
20
- try {
21
- const table = await this.client.openTable(storage.TABLE_EVALS);
22
- const query = table.query().where(`agent_name = '${agentName}'`);
23
- const records = await query.toArray();
24
- let filteredRecords = records;
25
- if (type === "live") {
26
- filteredRecords = records.filter((record) => record.test_info === null);
27
- } else if (type === "test") {
28
- filteredRecords = records.filter((record) => record.test_info !== null);
29
- }
30
- return filteredRecords.map((record) => {
31
- return {
32
- id: record.id,
33
- input: record.input,
34
- output: record.output,
35
- agentName: record.agent_name,
36
- metricName: record.metric_name,
37
- result: JSON.parse(record.result),
38
- instructions: record.instructions,
39
- testInfo: record.test_info ? JSON.parse(record.test_info) : null,
40
- globalRunId: record.global_run_id,
41
- runId: record.run_id,
42
- createdAt: new Date(record.created_at).toString()
43
- };
44
- });
45
- } catch (error$1) {
46
- throw new error.MastraError(
47
- {
48
- id: "LANCE_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
49
- domain: error.ErrorDomain.STORAGE,
50
- category: error.ErrorCategory.THIRD_PARTY,
51
- details: { agentName }
52
- },
53
- error$1
54
- );
55
- }
56
- }
57
- async getEvals(options) {
58
- try {
59
- const table = await this.client.openTable(storage.TABLE_EVALS);
60
- const conditions = [];
61
- if (options.agentName) {
62
- conditions.push(`agent_name = '${options.agentName}'`);
63
- }
64
- if (options.type === "live") {
65
- conditions.push("length(test_info) = 0");
66
- } else if (options.type === "test") {
67
- conditions.push("length(test_info) > 0");
68
- }
69
- const startDate = options.dateRange?.start || options.fromDate;
70
- const endDate = options.dateRange?.end || options.toDate;
71
- if (startDate) {
72
- conditions.push(`\`created_at\` >= ${startDate.getTime()}`);
73
- }
74
- if (endDate) {
75
- conditions.push(`\`created_at\` <= ${endDate.getTime()}`);
76
- }
77
- let total = 0;
78
- if (conditions.length > 0) {
79
- total = await table.countRows(conditions.join(" AND "));
80
- } else {
81
- total = await table.countRows();
82
- }
83
- const query = table.query();
84
- if (conditions.length > 0) {
85
- const whereClause = conditions.join(" AND ");
86
- query.where(whereClause);
87
- }
88
- const records = await query.toArray();
89
- const evals = records.sort((a, b) => b.created_at - a.created_at).map((record) => {
90
- return {
91
- id: record.id,
92
- input: record.input,
93
- output: record.output,
94
- agentName: record.agent_name,
95
- metricName: record.metric_name,
96
- result: JSON.parse(record.result),
97
- instructions: record.instructions,
98
- testInfo: record.test_info ? JSON.parse(record.test_info) : null,
99
- globalRunId: record.global_run_id,
100
- runId: record.run_id,
101
- createdAt: new Date(record.created_at).toISOString()
102
- };
103
- });
104
- const page = options.page || 0;
105
- const perPage = options.perPage || 10;
106
- const pagedEvals = evals.slice(page * perPage, (page + 1) * perPage);
107
- return {
108
- evals: pagedEvals,
109
- total,
110
- page,
111
- perPage,
112
- hasMore: total > (page + 1) * perPage
113
- };
114
- } catch (error$1) {
115
- throw new error.MastraError(
116
- {
117
- id: "LANCE_STORE_GET_EVALS_FAILED",
118
- domain: error.ErrorDomain.STORAGE,
119
- category: error.ErrorCategory.THIRD_PARTY,
120
- details: { agentName: options.agentName ?? "" }
121
- },
122
- error$1
123
- );
124
- }
125
- }
126
- };
127
14
  function getPrimaryKeys(tableName) {
128
15
  let primaryId = ["id"];
129
16
  if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
130
17
  primaryId = ["workflow_name", "run_id"];
131
- } else if (tableName === storage.TABLE_EVALS) {
132
- primaryId = ["agent_name", "metric_name", "run_id"];
133
18
  }
134
19
  return primaryId;
135
20
  }
@@ -207,7 +92,7 @@ async function getTableSchema({
207
92
  } catch (validationError) {
208
93
  throw new error.MastraError(
209
94
  {
210
- id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_INVALID_ARGS",
95
+ id: storage.createStorageErrorId("LANCE", "GET_TABLE_SCHEMA", "INVALID_ARGS"),
211
96
  domain: error.ErrorDomain.STORAGE,
212
97
  category: error.ErrorCategory.USER,
213
98
  text: validationError.message,
@@ -230,7 +115,7 @@ async function getTableSchema({
230
115
  } catch (error$1) {
231
116
  throw new error.MastraError(
232
117
  {
233
- id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_FAILED",
118
+ id: storage.createStorageErrorId("LANCE", "GET_TABLE_SCHEMA", "FAILED"),
234
119
  domain: error.ErrorDomain.STORAGE,
235
120
  category: error.ErrorCategory.THIRD_PARTY,
236
121
  details: { tableName }
@@ -240,1202 +125,1285 @@ async function getTableSchema({
240
125
  }
241
126
  }
242
127
 
243
- // src/storage/domains/memory/index.ts
244
- var StoreMemoryLance = class extends storage.MemoryStorage {
128
+ // src/storage/db/index.ts
129
+ function resolveLanceConfig(config) {
130
+ return config.client;
131
+ }
132
+ var LanceDB = class extends base.MastraBase {
245
133
  client;
246
- operations;
247
- constructor({ client, operations }) {
248
- super();
134
+ constructor({ client }) {
135
+ super({ name: "lance-db" });
249
136
  this.client = client;
250
- this.operations = operations;
251
137
  }
252
- async getThreadById({ threadId }) {
138
+ getDefaultValue(type) {
139
+ switch (type) {
140
+ case "text":
141
+ return "''";
142
+ case "timestamp":
143
+ return "CURRENT_TIMESTAMP";
144
+ case "integer":
145
+ case "bigint":
146
+ return "0";
147
+ case "jsonb":
148
+ return "'{}'";
149
+ case "uuid":
150
+ return "''";
151
+ default:
152
+ return storage.getDefaultValue(type);
153
+ }
154
+ }
155
+ async hasColumn(tableName, columnName) {
156
+ const table = await this.client.openTable(tableName);
157
+ const schema = await table.schema();
158
+ return schema.fields.some((field) => field.name === columnName);
159
+ }
160
+ translateSchema(schema) {
161
+ const fields = Object.entries(schema).map(([name, column]) => {
162
+ let arrowType;
163
+ switch (column.type.toLowerCase()) {
164
+ case "text":
165
+ case "uuid":
166
+ arrowType = new apacheArrow.Utf8();
167
+ break;
168
+ case "int":
169
+ case "integer":
170
+ arrowType = new apacheArrow.Int32();
171
+ break;
172
+ case "bigint":
173
+ arrowType = new apacheArrow.Float64();
174
+ break;
175
+ case "float":
176
+ arrowType = new apacheArrow.Float32();
177
+ break;
178
+ case "jsonb":
179
+ case "json":
180
+ arrowType = new apacheArrow.Utf8();
181
+ break;
182
+ case "binary":
183
+ arrowType = new apacheArrow.Binary();
184
+ break;
185
+ case "timestamp":
186
+ arrowType = new apacheArrow.Float64();
187
+ break;
188
+ default:
189
+ arrowType = new apacheArrow.Utf8();
190
+ }
191
+ return new apacheArrow.Field(name, arrowType, column.nullable ?? true);
192
+ });
193
+ return new apacheArrow.Schema(fields);
194
+ }
195
+ async createTable({
196
+ tableName,
197
+ schema
198
+ }) {
253
199
  try {
254
- const thread = await this.operations.load({ tableName: storage.TABLE_THREADS, keys: { id: threadId } });
255
- if (!thread) {
256
- return null;
200
+ if (!this.client) {
201
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
202
+ }
203
+ if (!tableName) {
204
+ throw new Error("tableName is required for createTable.");
205
+ }
206
+ if (!schema) {
207
+ throw new Error("schema is required for createTable.");
257
208
  }
258
- return {
259
- ...thread,
260
- createdAt: new Date(thread.createdAt),
261
- updatedAt: new Date(thread.updatedAt)
262
- };
263
209
  } catch (error$1) {
264
210
  throw new error.MastraError(
265
211
  {
266
- id: "LANCE_STORE_GET_THREAD_BY_ID_FAILED",
212
+ id: storage.createStorageErrorId("LANCE", "CREATE_TABLE", "INVALID_ARGS"),
267
213
  domain: error.ErrorDomain.STORAGE,
268
- category: error.ErrorCategory.THIRD_PARTY
214
+ category: error.ErrorCategory.USER,
215
+ details: { tableName }
269
216
  },
270
217
  error$1
271
218
  );
272
219
  }
273
- }
274
- async getThreadsByResourceId({ resourceId }) {
275
220
  try {
276
- const table = await this.client.openTable(storage.TABLE_THREADS);
277
- const query = table.query().where(`\`resourceId\` = '${resourceId}'`);
278
- const records = await query.toArray();
279
- return processResultWithTypeConversion(
280
- records,
281
- await getTableSchema({ tableName: storage.TABLE_THREADS, client: this.client })
282
- );
221
+ const arrowSchema = this.translateSchema(schema);
222
+ await this.client.createEmptyTable(tableName, arrowSchema);
283
223
  } catch (error$1) {
224
+ if (error$1.message?.includes("already exists")) {
225
+ this.logger.debug(`Table '${tableName}' already exists, skipping create`);
226
+ return;
227
+ }
284
228
  throw new error.MastraError(
285
229
  {
286
- id: "LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
230
+ id: storage.createStorageErrorId("LANCE", "CREATE_TABLE", "FAILED"),
287
231
  domain: error.ErrorDomain.STORAGE,
288
- category: error.ErrorCategory.THIRD_PARTY
232
+ category: error.ErrorCategory.THIRD_PARTY,
233
+ details: { tableName }
289
234
  },
290
235
  error$1
291
236
  );
292
237
  }
293
238
  }
294
- /**
295
- * Saves a thread to the database. This function doesn't overwrite existing threads.
296
- * @param thread - The thread to save
297
- * @returns The saved thread
298
- */
299
- async saveThread({ thread }) {
239
+ async dropTable({ tableName }) {
300
240
  try {
301
- const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
302
- const table = await this.client.openTable(storage.TABLE_THREADS);
303
- await table.add([record], { mode: "append" });
304
- return thread;
305
- } catch (error$1) {
241
+ if (!this.client) {
242
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
243
+ }
244
+ if (!tableName) {
245
+ throw new Error("tableName is required for dropTable.");
246
+ }
247
+ } catch (validationError) {
306
248
  throw new error.MastraError(
307
249
  {
308
- id: "LANCE_STORE_SAVE_THREAD_FAILED",
250
+ id: storage.createStorageErrorId("LANCE", "DROP_TABLE", "INVALID_ARGS"),
309
251
  domain: error.ErrorDomain.STORAGE,
310
- category: error.ErrorCategory.THIRD_PARTY
252
+ category: error.ErrorCategory.USER,
253
+ text: validationError.message,
254
+ details: { tableName }
311
255
  },
312
- error$1
256
+ validationError
313
257
  );
314
258
  }
315
- }
316
- async updateThread({
317
- id,
318
- title,
319
- metadata
320
- }) {
321
- const maxRetries = 5;
322
- for (let attempt = 0; attempt < maxRetries; attempt++) {
323
- try {
324
- const current = await this.getThreadById({ threadId: id });
325
- if (!current) {
326
- throw new Error(`Thread with id ${id} not found`);
327
- }
328
- const mergedMetadata = { ...current.metadata, ...metadata };
329
- const record = {
330
- id,
331
- title,
332
- metadata: JSON.stringify(mergedMetadata),
333
- updatedAt: (/* @__PURE__ */ new Date()).getTime()
334
- };
335
- const table = await this.client.openTable(storage.TABLE_THREADS);
336
- await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
337
- const updatedThread = await this.getThreadById({ threadId: id });
338
- if (!updatedThread) {
339
- throw new Error(`Failed to retrieve updated thread ${id}`);
340
- }
341
- return updatedThread;
342
- } catch (error$1) {
343
- if (error$1.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
344
- const delay = Math.pow(2, attempt) * 10;
345
- await new Promise((resolve) => setTimeout(resolve, delay));
346
- continue;
347
- }
348
- throw new error.MastraError(
349
- {
350
- id: "LANCE_STORE_UPDATE_THREAD_FAILED",
351
- domain: error.ErrorDomain.STORAGE,
352
- category: error.ErrorCategory.THIRD_PARTY
353
- },
354
- error$1
355
- );
356
- }
357
- }
358
- throw new error.MastraError(
359
- {
360
- id: "LANCE_STORE_UPDATE_THREAD_FAILED",
361
- domain: error.ErrorDomain.STORAGE,
362
- category: error.ErrorCategory.THIRD_PARTY
363
- },
364
- new Error("All retries exhausted")
365
- );
366
- }
367
- async deleteThread({ threadId }) {
368
259
  try {
369
- const table = await this.client.openTable(storage.TABLE_THREADS);
370
- await table.delete(`id = '${threadId}'`);
371
- const messagesTable = await this.client.openTable(storage.TABLE_MESSAGES);
372
- await messagesTable.delete(`thread_id = '${threadId}'`);
260
+ await this.client.dropTable(tableName);
373
261
  } catch (error$1) {
262
+ if (error$1.toString().includes("was not found") || error$1.message?.includes("Table not found")) {
263
+ this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
264
+ return;
265
+ }
374
266
  throw new error.MastraError(
375
267
  {
376
- id: "LANCE_STORE_DELETE_THREAD_FAILED",
268
+ id: storage.createStorageErrorId("LANCE", "DROP_TABLE", "FAILED"),
377
269
  domain: error.ErrorDomain.STORAGE,
378
- category: error.ErrorCategory.THIRD_PARTY
270
+ category: error.ErrorCategory.THIRD_PARTY,
271
+ details: { tableName }
379
272
  },
380
273
  error$1
381
274
  );
382
275
  }
383
276
  }
384
- normalizeMessage(message) {
385
- const { thread_id, ...rest } = message;
386
- return {
387
- ...rest,
388
- threadId: thread_id,
389
- content: typeof message.content === "string" ? (() => {
390
- try {
391
- return JSON.parse(message.content);
392
- } catch {
393
- return message.content;
394
- }
395
- })() : message.content
396
- };
397
- }
398
- async getMessages({
399
- threadId,
400
- resourceId,
401
- selectBy,
402
- format,
403
- threadConfig
404
- }) {
277
+ async alterTable({
278
+ tableName,
279
+ schema,
280
+ ifNotExists
281
+ }) {
405
282
  try {
406
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
407
- if (threadConfig) {
408
- throw new Error("ThreadConfig is not supported by LanceDB storage");
283
+ if (!this.client) {
284
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
409
285
  }
410
- const limit = storage.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
411
- const table = await this.client.openTable(storage.TABLE_MESSAGES);
412
- let allRecords = [];
413
- if (selectBy?.include && selectBy.include.length > 0) {
414
- const threadIds = [...new Set(selectBy.include.map((item) => item.threadId))];
415
- for (const threadId2 of threadIds) {
416
- const threadQuery = table.query().where(`thread_id = '${threadId2}'`);
417
- let threadRecords = await threadQuery.toArray();
418
- allRecords.push(...threadRecords);
419
- }
420
- } else {
421
- let query = table.query().where(`\`thread_id\` = '${threadId}'`);
422
- allRecords = await query.toArray();
286
+ if (!tableName) {
287
+ throw new Error("tableName is required for alterTable.");
423
288
  }
424
- allRecords.sort((a, b) => {
425
- const dateA = new Date(a.createdAt).getTime();
426
- const dateB = new Date(b.createdAt).getTime();
427
- return dateA - dateB;
428
- });
429
- if (selectBy?.include && selectBy.include.length > 0) {
430
- allRecords = this.processMessagesWithContext(allRecords, selectBy.include);
289
+ if (!schema) {
290
+ throw new Error("schema is required for alterTable.");
431
291
  }
432
- if (limit !== Number.MAX_SAFE_INTEGER) {
433
- allRecords = allRecords.slice(-limit);
292
+ if (!ifNotExists || ifNotExists.length === 0) {
293
+ this.logger.debug("No columns specified to add in alterTable, skipping.");
294
+ return;
434
295
  }
435
- const messages = processResultWithTypeConversion(
436
- allRecords,
437
- await getTableSchema({ tableName: storage.TABLE_MESSAGES, client: this.client })
438
- );
439
- const list = new agent.MessageList({ threadId, resourceId }).add(messages.map(this.normalizeMessage), "memory");
440
- if (format === "v2") return list.get.all.v2();
441
- return list.get.all.v1();
442
- } catch (error$1) {
296
+ } catch (validationError) {
443
297
  throw new error.MastraError(
444
298
  {
445
- id: "LANCE_STORE_GET_MESSAGES_FAILED",
299
+ id: storage.createStorageErrorId("LANCE", "ALTER_TABLE", "INVALID_ARGS"),
446
300
  domain: error.ErrorDomain.STORAGE,
447
- category: error.ErrorCategory.THIRD_PARTY,
448
- details: {
449
- threadId,
450
- resourceId: resourceId ?? ""
451
- }
301
+ category: error.ErrorCategory.USER,
302
+ text: validationError.message,
303
+ details: { tableName }
452
304
  },
453
- error$1
305
+ validationError
454
306
  );
455
307
  }
456
- }
457
- async getMessagesById({
458
- messageIds,
459
- format
460
- }) {
461
- if (messageIds.length === 0) return [];
462
308
  try {
463
- const table = await this.client.openTable(storage.TABLE_MESSAGES);
464
- const quotedIds = messageIds.map((id) => `'${id}'`).join(", ");
465
- const allRecords = await table.query().where(`id IN (${quotedIds})`).toArray();
466
- const messages = processResultWithTypeConversion(
467
- allRecords,
468
- await getTableSchema({ tableName: storage.TABLE_MESSAGES, client: this.client })
469
- );
470
- const list = new agent.MessageList().add(messages.map(this.normalizeMessage), "memory");
471
- if (format === `v1`) return list.get.all.v1();
472
- return list.get.all.v2();
309
+ const table = await this.client.openTable(tableName);
310
+ const currentSchema = await table.schema();
311
+ const existingFields = new Set(currentSchema.fields.map((f) => f.name));
312
+ const typeMap = {
313
+ text: "string",
314
+ integer: "int",
315
+ bigint: "bigint",
316
+ timestamp: "timestamp",
317
+ jsonb: "string",
318
+ uuid: "string"
319
+ };
320
+ const columnsToAdd = ifNotExists.filter((col) => schema[col] && !existingFields.has(col)).map((col) => {
321
+ const colDef = schema[col];
322
+ return {
323
+ name: col,
324
+ valueSql: colDef?.nullable ? `cast(NULL as ${typeMap[colDef.type ?? "text"]})` : `cast(${this.getDefaultValue(colDef?.type ?? "text")} as ${typeMap[colDef?.type ?? "text"]})`
325
+ };
326
+ });
327
+ if (columnsToAdd.length > 0) {
328
+ await table.addColumns(columnsToAdd);
329
+ this.logger?.info?.(`Added columns [${columnsToAdd.map((c) => c.name).join(", ")}] to table ${tableName}`);
330
+ }
473
331
  } catch (error$1) {
474
332
  throw new error.MastraError(
475
333
  {
476
- id: "LANCE_STORE_GET_MESSAGES_BY_ID_FAILED",
334
+ id: storage.createStorageErrorId("LANCE", "ALTER_TABLE", "FAILED"),
477
335
  domain: error.ErrorDomain.STORAGE,
478
336
  category: error.ErrorCategory.THIRD_PARTY,
479
- details: {
480
- messageIds: JSON.stringify(messageIds)
481
- }
337
+ details: { tableName }
482
338
  },
483
339
  error$1
484
340
  );
485
341
  }
486
342
  }
487
- async saveMessages(args) {
343
+ async clearTable({ tableName }) {
488
344
  try {
489
- const { messages, format = "v1" } = args;
490
- if (messages.length === 0) {
491
- return [];
492
- }
493
- const threadId = messages[0]?.threadId;
494
- if (!threadId) {
495
- throw new Error("Thread ID is required");
345
+ if (!this.client) {
346
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
496
347
  }
497
- for (const message of messages) {
498
- if (!message.id) {
499
- throw new Error("Message ID is required");
500
- }
501
- if (!message.threadId) {
502
- throw new Error("Thread ID is required for all messages");
503
- }
504
- if (message.resourceId === null || message.resourceId === void 0) {
505
- throw new Error("Resource ID cannot be null or undefined");
506
- }
507
- if (!message.content) {
508
- throw new Error("Message content is required");
509
- }
348
+ if (!tableName) {
349
+ throw new Error("tableName is required for clearTable.");
510
350
  }
511
- const transformedMessages = messages.map((message) => {
512
- const { threadId: threadId2, type, ...rest } = message;
513
- return {
514
- ...rest,
515
- thread_id: threadId2,
516
- type: type ?? "v2",
517
- content: JSON.stringify(message.content)
518
- };
519
- });
520
- const table = await this.client.openTable(storage.TABLE_MESSAGES);
521
- await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
522
- const threadsTable = await this.client.openTable(storage.TABLE_THREADS);
523
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
524
- const updateRecord = { id: threadId, updatedAt: currentTime };
525
- await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([updateRecord]);
526
- const list = new agent.MessageList().add(messages, "memory");
527
- if (format === `v2`) return list.get.all.v2();
528
- return list.get.all.v1();
529
- } catch (error$1) {
351
+ } catch (validationError) {
530
352
  throw new error.MastraError(
531
353
  {
532
- id: "LANCE_STORE_SAVE_MESSAGES_FAILED",
354
+ id: storage.createStorageErrorId("LANCE", "CLEAR_TABLE", "INVALID_ARGS"),
533
355
  domain: error.ErrorDomain.STORAGE,
534
- category: error.ErrorCategory.THIRD_PARTY
356
+ category: error.ErrorCategory.USER,
357
+ text: validationError.message,
358
+ details: { tableName }
535
359
  },
536
- error$1
360
+ validationError
537
361
  );
538
362
  }
539
- }
540
- async getThreadsByResourceIdPaginated(args) {
541
363
  try {
542
- const { resourceId, page = 0, perPage = 10 } = args;
543
- const table = await this.client.openTable(storage.TABLE_THREADS);
544
- const total = await table.countRows(`\`resourceId\` = '${resourceId}'`);
545
- const query = table.query().where(`\`resourceId\` = '${resourceId}'`);
546
- const offset = page * perPage;
547
- query.limit(perPage);
548
- if (offset > 0) {
549
- query.offset(offset);
550
- }
551
- const records = await query.toArray();
552
- records.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
553
- const schema = await getTableSchema({ tableName: storage.TABLE_THREADS, client: this.client });
554
- const threads = records.map((record) => processResultWithTypeConversion(record, schema));
555
- return {
556
- threads,
557
- total,
558
- page,
559
- perPage,
560
- hasMore: total > (page + 1) * perPage
561
- };
364
+ const table = await this.client.openTable(tableName);
365
+ await table.delete("1=1");
562
366
  } catch (error$1) {
563
367
  throw new error.MastraError(
564
368
  {
565
- id: "LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
369
+ id: storage.createStorageErrorId("LANCE", "CLEAR_TABLE", "FAILED"),
566
370
  domain: error.ErrorDomain.STORAGE,
567
- category: error.ErrorCategory.THIRD_PARTY
371
+ category: error.ErrorCategory.THIRD_PARTY,
372
+ details: { tableName }
568
373
  },
569
374
  error$1
570
375
  );
571
376
  }
572
377
  }
573
- /**
574
- * Processes messages to include context messages based on withPreviousMessages and withNextMessages
575
- * @param records - The sorted array of records to process
576
- * @param include - The array of include specifications with context parameters
577
- * @returns The processed array with context messages included
578
- */
579
- processMessagesWithContext(records, include) {
580
- const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
581
- if (messagesWithContext.length === 0) {
582
- return records;
583
- }
584
- const messageIndexMap = /* @__PURE__ */ new Map();
585
- records.forEach((message, index) => {
586
- messageIndexMap.set(message.id, index);
587
- });
588
- const additionalIndices = /* @__PURE__ */ new Set();
589
- for (const item of messagesWithContext) {
590
- const messageIndex = messageIndexMap.get(item.id);
591
- if (messageIndex !== void 0) {
592
- if (item.withPreviousMessages) {
593
- const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
594
- for (let i = startIdx; i < messageIndex; i++) {
595
- additionalIndices.add(i);
596
- }
597
- }
598
- if (item.withNextMessages) {
599
- const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
600
- for (let i = messageIndex + 1; i <= endIdx; i++) {
601
- additionalIndices.add(i);
602
- }
603
- }
604
- }
605
- }
606
- if (additionalIndices.size === 0) {
607
- return records;
608
- }
609
- const originalMatchIds = new Set(include.map((item) => item.id));
610
- const allIndices = /* @__PURE__ */ new Set();
611
- records.forEach((record, index) => {
612
- if (originalMatchIds.has(record.id)) {
613
- allIndices.add(index);
614
- }
615
- });
616
- additionalIndices.forEach((index) => {
617
- allIndices.add(index);
618
- });
619
- return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
620
- }
621
- async getMessagesPaginated(args) {
622
- const { threadId, resourceId, selectBy, format = "v1" } = args;
623
- const page = selectBy?.pagination?.page ?? 0;
624
- const perPage = selectBy?.pagination?.perPage ?? 10;
378
+ async insert({ tableName, record }) {
625
379
  try {
626
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
627
- const dateRange = selectBy?.pagination?.dateRange;
628
- const fromDate = dateRange?.start;
629
- const toDate = dateRange?.end;
630
- const table = await this.client.openTable(storage.TABLE_MESSAGES);
631
- const messages = [];
632
- if (selectBy?.include && Array.isArray(selectBy.include)) {
633
- const threadIds = [...new Set(selectBy.include.map((item) => item.threadId))];
634
- const allThreadMessages = [];
635
- for (const threadId2 of threadIds) {
636
- const threadQuery = table.query().where(`thread_id = '${threadId2}'`);
637
- let threadRecords = await threadQuery.toArray();
638
- if (fromDate) threadRecords = threadRecords.filter((m) => m.createdAt >= fromDate.getTime());
639
- if (toDate) threadRecords = threadRecords.filter((m) => m.createdAt <= toDate.getTime());
640
- allThreadMessages.push(...threadRecords);
641
- }
642
- allThreadMessages.sort((a, b) => a.createdAt - b.createdAt);
643
- const contextMessages = this.processMessagesWithContext(allThreadMessages, selectBy.include);
644
- messages.push(...contextMessages);
645
- }
646
- const conditions = [`thread_id = '${threadId}'`];
647
- if (resourceId) {
648
- conditions.push(`\`resourceId\` = '${resourceId}'`);
649
- }
650
- if (fromDate) {
651
- conditions.push(`\`createdAt\` >= ${fromDate.getTime()}`);
652
- }
653
- if (toDate) {
654
- conditions.push(`\`createdAt\` <= ${toDate.getTime()}`);
380
+ if (!this.client) {
381
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
655
382
  }
656
- let total = 0;
657
- if (conditions.length > 0) {
658
- total = await table.countRows(conditions.join(" AND "));
659
- } else {
660
- total = await table.countRows();
383
+ if (!tableName) {
384
+ throw new Error("tableName is required for insert.");
661
385
  }
662
- if (total === 0 && messages.length === 0) {
663
- return {
664
- messages: [],
665
- total: 0,
666
- page,
667
- perPage,
668
- hasMore: false
669
- };
386
+ if (!record || Object.keys(record).length === 0) {
387
+ throw new Error("record is required and cannot be empty for insert.");
670
388
  }
671
- const excludeIds = messages.map((m) => m.id);
672
- let selectedMessages = [];
673
- if (selectBy?.last && selectBy.last > 0) {
674
- const query = table.query();
675
- if (conditions.length > 0) {
676
- query.where(conditions.join(" AND "));
677
- }
678
- let records = await query.toArray();
679
- records = records.sort((a, b) => a.createdAt - b.createdAt);
680
- if (excludeIds.length > 0) {
681
- records = records.filter((m) => !excludeIds.includes(m.id));
682
- }
683
- selectedMessages = records.slice(-selectBy.last);
684
- } else {
685
- const query = table.query();
686
- if (conditions.length > 0) {
687
- query.where(conditions.join(" AND "));
688
- }
689
- let records = await query.toArray();
690
- records = records.sort((a, b) => a.createdAt - b.createdAt);
691
- if (excludeIds.length > 0) {
692
- records = records.filter((m) => !excludeIds.includes(m.id));
389
+ } catch (validationError) {
390
+ throw new error.MastraError(
391
+ {
392
+ id: storage.createStorageErrorId("LANCE", "INSERT", "INVALID_ARGS"),
393
+ domain: error.ErrorDomain.STORAGE,
394
+ category: error.ErrorCategory.USER,
395
+ text: validationError.message,
396
+ details: { tableName }
397
+ },
398
+ validationError
399
+ );
400
+ }
401
+ try {
402
+ const table = await this.client.openTable(tableName);
403
+ const primaryId = getPrimaryKeys(tableName);
404
+ const processedRecord = { ...record };
405
+ for (const key in processedRecord) {
406
+ if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
407
+ this.logger.debug("Converting object to JSON string: ", processedRecord[key]);
408
+ processedRecord[key] = JSON.stringify(processedRecord[key]);
693
409
  }
694
- selectedMessages = records.slice(page * perPage, (page + 1) * perPage);
695
- }
696
- const allMessages = [...messages, ...selectedMessages];
697
- const seen = /* @__PURE__ */ new Set();
698
- const dedupedMessages = allMessages.filter((m) => {
699
- const key = `${m.id}:${m.thread_id}`;
700
- if (seen.has(key)) return false;
701
- seen.add(key);
702
- return true;
703
- });
704
- const formattedMessages = dedupedMessages.map((msg) => {
705
- const { thread_id, ...rest } = msg;
706
- return {
707
- ...rest,
708
- threadId: thread_id,
709
- content: typeof msg.content === "string" ? (() => {
710
- try {
711
- return JSON.parse(msg.content);
712
- } catch {
713
- return msg.content;
714
- }
715
- })() : msg.content
716
- };
717
- });
718
- const list = new agent.MessageList().add(formattedMessages, "memory");
719
- return {
720
- messages: format === "v2" ? list.get.all.v2() : list.get.all.v1(),
721
- total,
722
- // Total should be the count of messages matching the filters
723
- page,
724
- perPage,
725
- hasMore: total > (page + 1) * perPage
726
- };
410
+ }
411
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
727
412
  } catch (error$1) {
728
- const mastraError = new error.MastraError(
413
+ throw new error.MastraError(
729
414
  {
730
- id: "LANCE_STORE_GET_MESSAGES_PAGINATED_FAILED",
415
+ id: storage.createStorageErrorId("LANCE", "INSERT", "FAILED"),
731
416
  domain: error.ErrorDomain.STORAGE,
732
417
  category: error.ErrorCategory.THIRD_PARTY,
733
- details: {
734
- threadId,
735
- resourceId: resourceId ?? ""
736
- }
418
+ details: { tableName }
737
419
  },
738
420
  error$1
739
421
  );
740
- this.logger?.trackException?.(mastraError);
741
- this.logger?.error?.(mastraError.toString());
742
- return { messages: [], total: 0, page, perPage, hasMore: false };
743
422
  }
744
423
  }
745
- /**
746
- * Parse message data from LanceDB record format to MastraMessageV2 format
747
- */
748
- parseMessageData(data) {
749
- const { thread_id, ...rest } = data;
750
- return {
751
- ...rest,
752
- threadId: thread_id,
753
- content: typeof data.content === "string" ? (() => {
754
- try {
755
- return JSON.parse(data.content);
756
- } catch {
757
- return data.content;
758
- }
759
- })() : data.content,
760
- createdAt: new Date(data.createdAt),
761
- updatedAt: new Date(data.updatedAt)
762
- };
763
- }
764
- async updateMessages(args) {
765
- const { messages } = args;
766
- this.logger.debug("Updating messages", { count: messages.length });
767
- if (!messages.length) {
768
- return [];
424
+ async batchInsert({ tableName, records }) {
425
+ try {
426
+ if (!this.client) {
427
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
428
+ }
429
+ if (!tableName) {
430
+ throw new Error("tableName is required for batchInsert.");
431
+ }
432
+ if (!records || records.length === 0) {
433
+ throw new Error("records array is required and cannot be empty for batchInsert.");
434
+ }
435
+ } catch (validationError) {
436
+ throw new error.MastraError(
437
+ {
438
+ id: storage.createStorageErrorId("LANCE", "BATCH_INSERT", "INVALID_ARGS"),
439
+ domain: error.ErrorDomain.STORAGE,
440
+ category: error.ErrorCategory.USER,
441
+ text: validationError.message,
442
+ details: { tableName }
443
+ },
444
+ validationError
445
+ );
769
446
  }
770
- const updatedMessages = [];
771
- const affectedThreadIds = /* @__PURE__ */ new Set();
772
447
  try {
773
- for (const updateData of messages) {
774
- const { id, ...updates } = updateData;
775
- const existingMessage = await this.operations.load({ tableName: storage.TABLE_MESSAGES, keys: { id } });
776
- if (!existingMessage) {
777
- this.logger.warn("Message not found for update", { id });
778
- continue;
779
- }
780
- const existingMsg = this.parseMessageData(existingMessage);
781
- const originalThreadId = existingMsg.threadId;
782
- affectedThreadIds.add(originalThreadId);
783
- const updatePayload = {};
784
- if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
785
- if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
786
- if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
787
- if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
788
- updatePayload.thread_id = updates.threadId;
789
- affectedThreadIds.add(updates.threadId);
790
- }
791
- if (updates.content) {
792
- const existingContent = existingMsg.content;
793
- let newContent = { ...existingContent };
794
- if (updates.content.metadata !== void 0) {
795
- newContent.metadata = {
796
- ...existingContent.metadata || {},
797
- ...updates.content.metadata || {}
798
- };
799
- }
800
- if (updates.content.content !== void 0) {
801
- newContent.content = updates.content.content;
802
- }
803
- if ("parts" in updates.content && updates.content.parts !== void 0) {
804
- newContent.parts = updates.content.parts;
448
+ const table = await this.client.openTable(tableName);
449
+ const primaryId = getPrimaryKeys(tableName);
450
+ const processedRecords = records.map((record) => {
451
+ const processedRecord = { ...record };
452
+ for (const key in processedRecord) {
453
+ if (processedRecord[key] == null) continue;
454
+ if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
455
+ processedRecord[key] = JSON.stringify(processedRecord[key]);
805
456
  }
806
- updatePayload.content = JSON.stringify(newContent);
807
457
  }
808
- await this.operations.insert({ tableName: storage.TABLE_MESSAGES, record: { id, ...updatePayload } });
809
- const updatedMessage = await this.operations.load({ tableName: storage.TABLE_MESSAGES, keys: { id } });
810
- if (updatedMessage) {
811
- updatedMessages.push(this.parseMessageData(updatedMessage));
812
- }
813
- }
814
- for (const threadId of affectedThreadIds) {
815
- await this.operations.insert({
816
- tableName: storage.TABLE_THREADS,
817
- record: { id: threadId, updatedAt: Date.now() }
818
- });
819
- }
820
- return updatedMessages;
458
+ return processedRecord;
459
+ });
460
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
821
461
  } catch (error$1) {
822
462
  throw new error.MastraError(
823
463
  {
824
- id: "LANCE_STORE_UPDATE_MESSAGES_FAILED",
464
+ id: storage.createStorageErrorId("LANCE", "BATCH_INSERT", "FAILED"),
825
465
  domain: error.ErrorDomain.STORAGE,
826
466
  category: error.ErrorCategory.THIRD_PARTY,
827
- details: { count: messages.length }
467
+ details: { tableName }
828
468
  },
829
469
  error$1
830
470
  );
831
471
  }
832
472
  }
833
- async getResourceById({ resourceId }) {
473
+ async load({ tableName, keys }) {
834
474
  try {
835
- const resource = await this.operations.load({ tableName: storage.TABLE_RESOURCES, keys: { id: resourceId } });
836
- if (!resource) {
837
- return null;
838
- }
839
- let createdAt;
840
- let updatedAt;
841
- try {
842
- if (resource.createdAt instanceof Date) {
843
- createdAt = resource.createdAt;
844
- } else if (typeof resource.createdAt === "string") {
845
- createdAt = new Date(resource.createdAt);
846
- } else if (typeof resource.createdAt === "number") {
847
- createdAt = new Date(resource.createdAt);
848
- } else {
849
- createdAt = /* @__PURE__ */ new Date();
850
- }
851
- if (isNaN(createdAt.getTime())) {
852
- createdAt = /* @__PURE__ */ new Date();
853
- }
854
- } catch {
855
- createdAt = /* @__PURE__ */ new Date();
856
- }
857
- try {
858
- if (resource.updatedAt instanceof Date) {
859
- updatedAt = resource.updatedAt;
860
- } else if (typeof resource.updatedAt === "string") {
861
- updatedAt = new Date(resource.updatedAt);
862
- } else if (typeof resource.updatedAt === "number") {
863
- updatedAt = new Date(resource.updatedAt);
864
- } else {
865
- updatedAt = /* @__PURE__ */ new Date();
866
- }
867
- if (isNaN(updatedAt.getTime())) {
868
- updatedAt = /* @__PURE__ */ new Date();
869
- }
870
- } catch {
871
- updatedAt = /* @__PURE__ */ new Date();
475
+ if (!this.client) {
476
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
872
477
  }
873
- let workingMemory = resource.workingMemory;
874
- if (workingMemory === null || workingMemory === void 0) {
875
- workingMemory = void 0;
876
- } else if (workingMemory === "") {
877
- workingMemory = "";
878
- } else if (typeof workingMemory === "object") {
879
- workingMemory = JSON.stringify(workingMemory);
478
+ if (!tableName) {
479
+ throw new Error("tableName is required for load.");
880
480
  }
881
- let metadata = resource.metadata;
882
- if (metadata === "" || metadata === null || metadata === void 0) {
883
- metadata = void 0;
884
- } else if (typeof metadata === "string") {
885
- try {
886
- metadata = JSON.parse(metadata);
887
- } catch {
888
- metadata = metadata;
889
- }
481
+ if (!keys || Object.keys(keys).length === 0) {
482
+ throw new Error("keys are required and cannot be empty for load.");
890
483
  }
891
- return {
892
- ...resource,
893
- createdAt,
894
- updatedAt,
895
- workingMemory,
896
- metadata
897
- };
898
- } catch (error$1) {
484
+ } catch (validationError) {
899
485
  throw new error.MastraError(
900
486
  {
901
- id: "LANCE_STORE_GET_RESOURCE_BY_ID_FAILED",
487
+ id: storage.createStorageErrorId("LANCE", "LOAD", "INVALID_ARGS"),
902
488
  domain: error.ErrorDomain.STORAGE,
903
- category: error.ErrorCategory.THIRD_PARTY
489
+ category: error.ErrorCategory.USER,
490
+ text: validationError.message,
491
+ details: { tableName }
904
492
  },
905
- error$1
493
+ validationError
906
494
  );
907
495
  }
908
- }
909
- async saveResource({ resource }) {
910
496
  try {
911
- const record = {
912
- ...resource,
913
- metadata: resource.metadata ? JSON.stringify(resource.metadata) : "",
914
- createdAt: resource.createdAt.getTime(),
915
- // Store as timestamp (milliseconds)
916
- updatedAt: resource.updatedAt.getTime()
917
- // Store as timestamp (milliseconds)
918
- };
919
- const table = await this.client.openTable(storage.TABLE_RESOURCES);
920
- await table.add([record], { mode: "append" });
921
- return resource;
497
+ const table = await this.client.openTable(tableName);
498
+ const tableSchema = await getTableSchema({ tableName, client: this.client });
499
+ const query = table.query();
500
+ if (Object.keys(keys).length > 0) {
501
+ validateKeyTypes(keys, tableSchema);
502
+ const filterConditions = Object.entries(keys).map(([key, value]) => {
503
+ const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
504
+ const quotedKey = isCamelCase ? `\`${key}\`` : key;
505
+ if (typeof value === "string") {
506
+ return `${quotedKey} = '${value}'`;
507
+ } else if (value === null) {
508
+ return `${quotedKey} IS NULL`;
509
+ } else {
510
+ return `${quotedKey} = ${value}`;
511
+ }
512
+ }).join(" AND ");
513
+ this.logger.debug("where clause generated: " + filterConditions);
514
+ query.where(filterConditions);
515
+ }
516
+ const result = await query.limit(1).toArray();
517
+ if (result.length === 0) {
518
+ this.logger.debug("No record found");
519
+ return null;
520
+ }
521
+ return processResultWithTypeConversion(result[0], tableSchema);
922
522
  } catch (error$1) {
523
+ if (error$1 instanceof error.MastraError) throw error$1;
923
524
  throw new error.MastraError(
924
525
  {
925
- id: "LANCE_STORE_SAVE_RESOURCE_FAILED",
526
+ id: storage.createStorageErrorId("LANCE", "LOAD", "FAILED"),
926
527
  domain: error.ErrorDomain.STORAGE,
927
- category: error.ErrorCategory.THIRD_PARTY
528
+ category: error.ErrorCategory.THIRD_PARTY,
529
+ details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? "" }
928
530
  },
929
531
  error$1
930
532
  );
931
533
  }
932
534
  }
933
- async updateResource({
934
- resourceId,
935
- workingMemory,
936
- metadata
937
- }) {
938
- const maxRetries = 3;
939
- for (let attempt = 0; attempt < maxRetries; attempt++) {
940
- try {
941
- const existingResource = await this.getResourceById({ resourceId });
942
- if (!existingResource) {
943
- const newResource = {
944
- id: resourceId,
945
- workingMemory,
946
- metadata: metadata || {},
947
- createdAt: /* @__PURE__ */ new Date(),
948
- updatedAt: /* @__PURE__ */ new Date()
949
- };
950
- return this.saveResource({ resource: newResource });
951
- }
952
- const updatedResource = {
953
- ...existingResource,
954
- workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
955
- metadata: {
956
- ...existingResource.metadata,
957
- ...metadata
958
- },
959
- updatedAt: /* @__PURE__ */ new Date()
960
- };
961
- const record = {
962
- id: resourceId,
963
- workingMemory: updatedResource.workingMemory || "",
964
- metadata: updatedResource.metadata ? JSON.stringify(updatedResource.metadata) : "",
965
- updatedAt: updatedResource.updatedAt.getTime()
966
- // Store as timestamp (milliseconds)
967
- };
968
- const table = await this.client.openTable(storage.TABLE_RESOURCES);
969
- await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
970
- return updatedResource;
971
- } catch (error$1) {
972
- if (error$1.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
973
- const delay = Math.pow(2, attempt) * 10;
974
- await new Promise((resolve) => setTimeout(resolve, delay));
975
- continue;
976
- }
977
- throw new error.MastraError(
978
- {
979
- id: "LANCE_STORE_UPDATE_RESOURCE_FAILED",
980
- domain: error.ErrorDomain.STORAGE,
981
- category: error.ErrorCategory.THIRD_PARTY
982
- },
983
- error$1
984
- );
985
- }
986
- }
987
- throw new Error("Unexpected end of retry loop");
988
- }
989
535
  };
990
- var StoreOperationsLance = class extends storage.StoreOperations {
536
+
537
+ // src/storage/domains/memory/index.ts
538
+ var StoreMemoryLance = class extends storage.MemoryStorage {
991
539
  client;
992
- constructor({ client }) {
540
+ #db;
541
+ constructor(config) {
993
542
  super();
543
+ const client = resolveLanceConfig(config);
994
544
  this.client = client;
995
- }
996
- getDefaultValue(type) {
997
- switch (type) {
998
- case "text":
999
- return "''";
1000
- case "timestamp":
1001
- return "CURRENT_TIMESTAMP";
1002
- case "integer":
1003
- case "bigint":
1004
- return "0";
1005
- case "jsonb":
1006
- return "'{}'";
1007
- case "uuid":
1008
- return "''";
1009
- default:
1010
- return super.getDefaultValue(type);
1011
- }
1012
- }
1013
- async hasColumn(tableName, columnName) {
1014
- const table = await this.client.openTable(tableName);
1015
- const schema = await table.schema();
1016
- return schema.fields.some((field) => field.name === columnName);
1017
- }
1018
- translateSchema(schema) {
1019
- const fields = Object.entries(schema).map(([name, column]) => {
1020
- let arrowType;
1021
- switch (column.type.toLowerCase()) {
1022
- case "text":
1023
- case "uuid":
1024
- arrowType = new apacheArrow.Utf8();
1025
- break;
1026
- case "int":
1027
- case "integer":
1028
- arrowType = new apacheArrow.Int32();
1029
- break;
1030
- case "bigint":
1031
- arrowType = new apacheArrow.Float64();
1032
- break;
1033
- case "float":
1034
- arrowType = new apacheArrow.Float32();
1035
- break;
1036
- case "jsonb":
1037
- case "json":
1038
- arrowType = new apacheArrow.Utf8();
1039
- break;
1040
- case "binary":
1041
- arrowType = new apacheArrow.Binary();
1042
- break;
1043
- case "timestamp":
1044
- arrowType = new apacheArrow.Float64();
1045
- break;
1046
- default:
1047
- arrowType = new apacheArrow.Utf8();
1048
- }
1049
- return new apacheArrow.Field(name, arrowType, column.nullable ?? true);
545
+ this.#db = new LanceDB({ client });
546
+ }
547
+ async init() {
548
+ await this.#db.createTable({ tableName: storage.TABLE_THREADS, schema: storage.TABLE_SCHEMAS[storage.TABLE_THREADS] });
549
+ await this.#db.createTable({ tableName: storage.TABLE_MESSAGES, schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES] });
550
+ await this.#db.createTable({ tableName: storage.TABLE_RESOURCES, schema: storage.TABLE_SCHEMAS[storage.TABLE_RESOURCES] });
551
+ await this.#db.alterTable({
552
+ tableName: storage.TABLE_MESSAGES,
553
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES],
554
+ ifNotExists: ["resourceId"]
1050
555
  });
1051
- return new apacheArrow.Schema(fields);
1052
556
  }
1053
- async createTable({
1054
- tableName,
1055
- schema
1056
- }) {
557
+ async dangerouslyClearAll() {
558
+ await this.#db.clearTable({ tableName: storage.TABLE_THREADS });
559
+ await this.#db.clearTable({ tableName: storage.TABLE_MESSAGES });
560
+ await this.#db.clearTable({ tableName: storage.TABLE_RESOURCES });
561
+ }
562
+ async deleteMessages(messageIds) {
563
+ if (!messageIds || messageIds.length === 0) {
564
+ return;
565
+ }
566
+ this.logger.debug("Deleting messages", { count: messageIds.length });
1057
567
  try {
1058
- if (!this.client) {
1059
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
568
+ const threadIds = /* @__PURE__ */ new Set();
569
+ for (const messageId of messageIds) {
570
+ const message = await this.#db.load({ tableName: storage.TABLE_MESSAGES, keys: { id: messageId } });
571
+ if (message?.thread_id) {
572
+ threadIds.add(message.thread_id);
573
+ }
1060
574
  }
1061
- if (!tableName) {
1062
- throw new Error("tableName is required for createTable.");
575
+ const messagesTable = await this.client.openTable(storage.TABLE_MESSAGES);
576
+ const idConditions = messageIds.map((id) => `id = '${this.escapeSql(id)}'`).join(" OR ");
577
+ await messagesTable.delete(idConditions);
578
+ const now = (/* @__PURE__ */ new Date()).getTime();
579
+ const threadsTable = await this.client.openTable(storage.TABLE_THREADS);
580
+ for (const threadId of threadIds) {
581
+ const thread = await this.getThreadById({ threadId });
582
+ if (thread) {
583
+ const record = {
584
+ id: threadId,
585
+ resourceId: thread.resourceId,
586
+ title: thread.title,
587
+ metadata: JSON.stringify(thread.metadata),
588
+ createdAt: new Date(thread.createdAt).getTime(),
589
+ updatedAt: now
590
+ };
591
+ await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
592
+ }
1063
593
  }
1064
- if (!schema) {
1065
- throw new Error("schema is required for createTable.");
594
+ } catch (error$1) {
595
+ throw new error.MastraError(
596
+ {
597
+ id: storage.createStorageErrorId("LANCE", "DELETE_MESSAGES", "FAILED"),
598
+ domain: error.ErrorDomain.STORAGE,
599
+ category: error.ErrorCategory.THIRD_PARTY,
600
+ details: { count: messageIds.length }
601
+ },
602
+ error$1
603
+ );
604
+ }
605
+ }
606
+ // Utility to escape single quotes in SQL strings
607
+ escapeSql(str) {
608
+ return str.replace(/'/g, "''");
609
+ }
610
+ async getThreadById({ threadId }) {
611
+ try {
612
+ const thread = await this.#db.load({ tableName: storage.TABLE_THREADS, keys: { id: threadId } });
613
+ if (!thread) {
614
+ return null;
1066
615
  }
616
+ return {
617
+ ...thread,
618
+ createdAt: new Date(thread.createdAt),
619
+ updatedAt: new Date(thread.updatedAt)
620
+ };
1067
621
  } catch (error$1) {
1068
622
  throw new error.MastraError(
1069
623
  {
1070
- id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_INVALID_ARGS",
624
+ id: storage.createStorageErrorId("LANCE", "GET_THREAD_BY_ID", "FAILED"),
1071
625
  domain: error.ErrorDomain.STORAGE,
1072
- category: error.ErrorCategory.USER,
1073
- details: { tableName }
626
+ category: error.ErrorCategory.THIRD_PARTY
1074
627
  },
1075
628
  error$1
1076
629
  );
1077
630
  }
631
+ }
632
+ /**
633
+ * Saves a thread to the database. This function doesn't overwrite existing threads.
634
+ * @param thread - The thread to save
635
+ * @returns The saved thread
636
+ */
637
+ async saveThread({ thread }) {
1078
638
  try {
1079
- const arrowSchema = this.translateSchema(schema);
1080
- await this.client.createEmptyTable(tableName, arrowSchema);
639
+ const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
640
+ const table = await this.client.openTable(storage.TABLE_THREADS);
641
+ await table.add([record], { mode: "append" });
642
+ return thread;
1081
643
  } catch (error$1) {
1082
- if (error$1.message?.includes("already exists")) {
1083
- this.logger.debug(`Table '${tableName}' already exists, skipping create`);
1084
- return;
1085
- }
1086
644
  throw new error.MastraError(
1087
645
  {
1088
- id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_FAILED",
646
+ id: storage.createStorageErrorId("LANCE", "SAVE_THREAD", "FAILED"),
1089
647
  domain: error.ErrorDomain.STORAGE,
1090
- category: error.ErrorCategory.THIRD_PARTY,
1091
- details: { tableName }
648
+ category: error.ErrorCategory.THIRD_PARTY
1092
649
  },
1093
650
  error$1
1094
651
  );
1095
652
  }
1096
653
  }
1097
- async dropTable({ tableName }) {
1098
- try {
1099
- if (!this.client) {
1100
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1101
- }
1102
- if (!tableName) {
1103
- throw new Error("tableName is required for dropTable.");
654
+ async updateThread({
655
+ id,
656
+ title,
657
+ metadata
658
+ }) {
659
+ const maxRetries = 5;
660
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
661
+ try {
662
+ const current = await this.getThreadById({ threadId: id });
663
+ if (!current) {
664
+ throw new Error(`Thread with id ${id} not found`);
665
+ }
666
+ const mergedMetadata = { ...current.metadata, ...metadata };
667
+ const record = {
668
+ id,
669
+ title,
670
+ metadata: JSON.stringify(mergedMetadata),
671
+ updatedAt: (/* @__PURE__ */ new Date()).getTime()
672
+ };
673
+ const table = await this.client.openTable(storage.TABLE_THREADS);
674
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
675
+ const updatedThread = await this.getThreadById({ threadId: id });
676
+ if (!updatedThread) {
677
+ throw new Error(`Failed to retrieve updated thread ${id}`);
678
+ }
679
+ return updatedThread;
680
+ } catch (error$1) {
681
+ if (error$1.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
682
+ const delay = Math.pow(2, attempt) * 10;
683
+ await new Promise((resolve) => setTimeout(resolve, delay));
684
+ continue;
685
+ }
686
+ throw new error.MastraError(
687
+ {
688
+ id: storage.createStorageErrorId("LANCE", "UPDATE_THREAD", "FAILED"),
689
+ domain: error.ErrorDomain.STORAGE,
690
+ category: error.ErrorCategory.THIRD_PARTY
691
+ },
692
+ error$1
693
+ );
1104
694
  }
1105
- } catch (validationError) {
695
+ }
696
+ throw new error.MastraError(
697
+ {
698
+ id: storage.createStorageErrorId("LANCE", "UPDATE_THREAD", "FAILED"),
699
+ domain: error.ErrorDomain.STORAGE,
700
+ category: error.ErrorCategory.THIRD_PARTY
701
+ },
702
+ new Error("All retries exhausted")
703
+ );
704
+ }
705
+ async deleteThread({ threadId }) {
706
+ try {
707
+ const table = await this.client.openTable(storage.TABLE_THREADS);
708
+ await table.delete(`id = '${threadId}'`);
709
+ const messagesTable = await this.client.openTable(storage.TABLE_MESSAGES);
710
+ await messagesTable.delete(`thread_id = '${threadId}'`);
711
+ } catch (error$1) {
1106
712
  throw new error.MastraError(
1107
713
  {
1108
- id: "STORAGE_LANCE_STORAGE_DROP_TABLE_INVALID_ARGS",
714
+ id: storage.createStorageErrorId("LANCE", "DELETE_THREAD", "FAILED"),
1109
715
  domain: error.ErrorDomain.STORAGE,
1110
- category: error.ErrorCategory.USER,
1111
- text: validationError.message,
1112
- details: { tableName }
716
+ category: error.ErrorCategory.THIRD_PARTY
1113
717
  },
1114
- validationError
718
+ error$1
1115
719
  );
1116
720
  }
721
+ }
722
+ normalizeMessage(message) {
723
+ const { thread_id, ...rest } = message;
724
+ return {
725
+ ...rest,
726
+ threadId: thread_id,
727
+ content: typeof message.content === "string" ? (() => {
728
+ try {
729
+ return JSON.parse(message.content);
730
+ } catch {
731
+ return message.content;
732
+ }
733
+ })() : message.content
734
+ };
735
+ }
736
+ async listMessagesById({ messageIds }) {
737
+ if (messageIds.length === 0) return { messages: [] };
1117
738
  try {
1118
- await this.client.dropTable(tableName);
739
+ const table = await this.client.openTable(storage.TABLE_MESSAGES);
740
+ const quotedIds = messageIds.map((id) => `'${id}'`).join(", ");
741
+ const allRecords = await table.query().where(`id IN (${quotedIds})`).toArray();
742
+ const messages = processResultWithTypeConversion(
743
+ allRecords,
744
+ await getTableSchema({ tableName: storage.TABLE_MESSAGES, client: this.client })
745
+ );
746
+ const list = new agent.MessageList().add(
747
+ messages.map(this.normalizeMessage),
748
+ "memory"
749
+ );
750
+ return { messages: list.get.all.db() };
1119
751
  } catch (error$1) {
1120
- if (error$1.toString().includes("was not found") || error$1.message?.includes("Table not found")) {
1121
- this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
1122
- return;
1123
- }
1124
752
  throw new error.MastraError(
1125
753
  {
1126
- id: "STORAGE_LANCE_STORAGE_DROP_TABLE_FAILED",
754
+ id: storage.createStorageErrorId("LANCE", "LIST_MESSAGES_BY_ID", "FAILED"),
1127
755
  domain: error.ErrorDomain.STORAGE,
1128
756
  category: error.ErrorCategory.THIRD_PARTY,
1129
- details: { tableName }
757
+ details: {
758
+ messageIds: JSON.stringify(messageIds)
759
+ }
1130
760
  },
1131
761
  error$1
1132
762
  );
1133
763
  }
1134
764
  }
1135
- async alterTable({
1136
- tableName,
1137
- schema,
1138
- ifNotExists
1139
- }) {
1140
- try {
1141
- if (!this.client) {
1142
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1143
- }
1144
- if (!tableName) {
1145
- throw new Error("tableName is required for alterTable.");
1146
- }
1147
- if (!schema) {
1148
- throw new Error("schema is required for alterTable.");
1149
- }
1150
- if (!ifNotExists || ifNotExists.length === 0) {
1151
- this.logger.debug("No columns specified to add in alterTable, skipping.");
1152
- return;
1153
- }
1154
- } catch (validationError) {
765
+ async listMessages(args) {
766
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
767
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
768
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
1155
769
  throw new error.MastraError(
1156
770
  {
1157
- id: "STORAGE_LANCE_STORAGE_ALTER_TABLE_INVALID_ARGS",
771
+ id: storage.createStorageErrorId("LANCE", "LIST_MESSAGES", "INVALID_THREAD_ID"),
1158
772
  domain: error.ErrorDomain.STORAGE,
1159
- category: error.ErrorCategory.USER,
1160
- text: validationError.message,
1161
- details: { tableName }
773
+ category: error.ErrorCategory.THIRD_PARTY,
774
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
1162
775
  },
1163
- validationError
776
+ new Error("threadId must be a non-empty string or array of non-empty strings")
1164
777
  );
1165
778
  }
779
+ const perPage = storage.normalizePerPage(perPageInput, 40);
780
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1166
781
  try {
1167
- const table = await this.client.openTable(tableName);
1168
- const currentSchema = await table.schema();
1169
- const existingFields = new Set(currentSchema.fields.map((f) => f.name));
1170
- const typeMap = {
1171
- text: "string",
1172
- integer: "int",
1173
- bigint: "bigint",
1174
- timestamp: "timestamp",
1175
- jsonb: "string",
1176
- uuid: "string"
1177
- };
1178
- const columnsToAdd = ifNotExists.filter((col) => schema[col] && !existingFields.has(col)).map((col) => {
1179
- const colDef = schema[col];
782
+ if (page < 0) {
783
+ throw new error.MastraError(
784
+ {
785
+ id: storage.createStorageErrorId("LANCE", "LIST_MESSAGES", "INVALID_PAGE"),
786
+ domain: error.ErrorDomain.STORAGE,
787
+ category: error.ErrorCategory.USER,
788
+ details: { page }
789
+ },
790
+ new Error("page must be >= 0")
791
+ );
792
+ }
793
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
794
+ const table = await this.client.openTable(storage.TABLE_MESSAGES);
795
+ const threadCondition = threadIds.length === 1 ? `thread_id = '${this.escapeSql(threadIds[0])}'` : `thread_id IN (${threadIds.map((t) => `'${this.escapeSql(t)}'`).join(", ")})`;
796
+ const conditions = [threadCondition];
797
+ if (resourceId) {
798
+ conditions.push(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
799
+ }
800
+ if (filter?.dateRange?.start) {
801
+ const startTime = filter.dateRange.start instanceof Date ? filter.dateRange.start.getTime() : new Date(filter.dateRange.start).getTime();
802
+ const startOp = filter.dateRange.startExclusive ? ">" : ">=";
803
+ conditions.push(`\`createdAt\` ${startOp} ${startTime}`);
804
+ }
805
+ if (filter?.dateRange?.end) {
806
+ const endTime = filter.dateRange.end instanceof Date ? filter.dateRange.end.getTime() : new Date(filter.dateRange.end).getTime();
807
+ const endOp = filter.dateRange.endExclusive ? "<" : "<=";
808
+ conditions.push(`\`createdAt\` ${endOp} ${endTime}`);
809
+ }
810
+ const whereClause = conditions.join(" AND ");
811
+ const total = await table.countRows(whereClause);
812
+ const query = table.query().where(whereClause);
813
+ let allRecords = await query.toArray();
814
+ allRecords.sort((a, b) => {
815
+ const aValue = field === "createdAt" ? a.createdAt : a[field];
816
+ const bValue = field === "createdAt" ? b.createdAt : b[field];
817
+ if (aValue == null && bValue == null) return 0;
818
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
819
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
820
+ if (typeof aValue === "string" && typeof bValue === "string") {
821
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
822
+ }
823
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
824
+ });
825
+ const paginatedRecords = allRecords.slice(offset, offset + perPage);
826
+ const messages = paginatedRecords.map((row) => this.normalizeMessage(row));
827
+ if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
1180
828
  return {
1181
- name: col,
1182
- valueSql: colDef?.nullable ? `cast(NULL as ${typeMap[colDef.type ?? "text"]})` : `cast(${this.getDefaultValue(colDef?.type ?? "text")} as ${typeMap[colDef?.type ?? "text"]})`
829
+ messages: [],
830
+ total: 0,
831
+ page,
832
+ perPage: perPageForResponse,
833
+ hasMore: false
1183
834
  };
1184
- });
1185
- if (columnsToAdd.length > 0) {
1186
- await table.addColumns(columnsToAdd);
1187
- this.logger?.info?.(`Added columns [${columnsToAdd.map((c) => c.name).join(", ")}] to table ${tableName}`);
1188
835
  }
836
+ const messageIds = new Set(messages.map((m) => m.id));
837
+ if (include && include.length > 0) {
838
+ const threadIds2 = [...new Set(include.map((item) => item.threadId || threadId))];
839
+ const allThreadMessages = [];
840
+ for (const tid of threadIds2) {
841
+ const threadQuery = table.query().where(`thread_id = '${tid}'`);
842
+ let threadRecords = await threadQuery.toArray();
843
+ allThreadMessages.push(...threadRecords);
844
+ }
845
+ allThreadMessages.sort((a, b) => a.createdAt - b.createdAt);
846
+ const contextMessages = this.processMessagesWithContext(allThreadMessages, include);
847
+ const includedMessages = contextMessages.map((row) => this.normalizeMessage(row));
848
+ for (const includeMsg of includedMessages) {
849
+ if (!messageIds.has(includeMsg.id)) {
850
+ messages.push(includeMsg);
851
+ messageIds.add(includeMsg.id);
852
+ }
853
+ }
854
+ }
855
+ const list = new agent.MessageList().add(messages, "memory");
856
+ let finalMessages = list.get.all.db();
857
+ finalMessages = finalMessages.sort((a, b) => {
858
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
859
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
860
+ if (aValue == null && bValue == null) return 0;
861
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
862
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
863
+ if (typeof aValue === "string" && typeof bValue === "string") {
864
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
865
+ }
866
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
867
+ });
868
+ const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
869
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
870
+ const fetchedAll = perPageInput === false || allThreadMessagesReturned;
871
+ const hasMore = !fetchedAll && offset + perPage < total;
872
+ return {
873
+ messages: finalMessages,
874
+ total,
875
+ page,
876
+ perPage: perPageForResponse,
877
+ hasMore
878
+ };
1189
879
  } catch (error$1) {
1190
- throw new error.MastraError(
880
+ const mastraError = new error.MastraError(
1191
881
  {
1192
- id: "STORAGE_LANCE_STORAGE_ALTER_TABLE_FAILED",
882
+ id: storage.createStorageErrorId("LANCE", "LIST_MESSAGES", "FAILED"),
1193
883
  domain: error.ErrorDomain.STORAGE,
1194
884
  category: error.ErrorCategory.THIRD_PARTY,
1195
- details: { tableName }
885
+ details: {
886
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
887
+ resourceId: resourceId ?? ""
888
+ }
1196
889
  },
1197
890
  error$1
1198
891
  );
892
+ this.logger?.error?.(mastraError.toString());
893
+ this.logger?.trackException?.(mastraError);
894
+ return {
895
+ messages: [],
896
+ total: 0,
897
+ page,
898
+ perPage: perPageForResponse,
899
+ hasMore: false
900
+ };
1199
901
  }
1200
902
  }
1201
- async clearTable({ tableName }) {
903
+ async saveMessages(args) {
1202
904
  try {
1203
- if (!this.client) {
1204
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
905
+ const { messages } = args;
906
+ if (messages.length === 0) {
907
+ return { messages: [] };
1205
908
  }
1206
- if (!tableName) {
1207
- throw new Error("tableName is required for clearTable.");
909
+ const threadId = messages[0]?.threadId;
910
+ if (!threadId) {
911
+ throw new Error("Thread ID is required");
1208
912
  }
1209
- } catch (validationError) {
913
+ for (const message of messages) {
914
+ if (!message.id) {
915
+ throw new Error("Message ID is required");
916
+ }
917
+ if (!message.threadId) {
918
+ throw new Error("Thread ID is required for all messages");
919
+ }
920
+ if (message.resourceId === null || message.resourceId === void 0) {
921
+ throw new Error("Resource ID cannot be null or undefined");
922
+ }
923
+ if (!message.content) {
924
+ throw new Error("Message content is required");
925
+ }
926
+ }
927
+ const transformedMessages = messages.map((message) => {
928
+ const { threadId: threadId2, type, ...rest } = message;
929
+ return {
930
+ ...rest,
931
+ thread_id: threadId2,
932
+ type: type ?? "v2",
933
+ content: JSON.stringify(message.content)
934
+ };
935
+ });
936
+ const table = await this.client.openTable(storage.TABLE_MESSAGES);
937
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
938
+ const threadsTable = await this.client.openTable(storage.TABLE_THREADS);
939
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
940
+ const updateRecord = { id: threadId, updatedAt: currentTime };
941
+ await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([updateRecord]);
942
+ const list = new agent.MessageList().add(messages, "memory");
943
+ return { messages: list.get.all.db() };
944
+ } catch (error$1) {
1210
945
  throw new error.MastraError(
1211
946
  {
1212
- id: "STORAGE_LANCE_STORAGE_CLEAR_TABLE_INVALID_ARGS",
947
+ id: storage.createStorageErrorId("LANCE", "SAVE_MESSAGES", "FAILED"),
1213
948
  domain: error.ErrorDomain.STORAGE,
1214
- category: error.ErrorCategory.USER,
1215
- text: validationError.message,
1216
- details: { tableName }
949
+ category: error.ErrorCategory.THIRD_PARTY
1217
950
  },
1218
- validationError
951
+ error$1
1219
952
  );
1220
953
  }
954
+ }
955
+ async listThreads(args) {
956
+ const { page = 0, perPage: perPageInput, orderBy, filter } = args;
1221
957
  try {
1222
- const table = await this.client.openTable(tableName);
1223
- await table.delete("1=1");
958
+ this.validatePaginationInput(page, perPageInput ?? 100);
1224
959
  } catch (error$1) {
1225
960
  throw new error.MastraError(
1226
961
  {
1227
- id: "STORAGE_LANCE_STORAGE_CLEAR_TABLE_FAILED",
962
+ id: storage.createStorageErrorId("LANCE", "LIST_THREADS", "INVALID_PAGE"),
1228
963
  domain: error.ErrorDomain.STORAGE,
1229
- category: error.ErrorCategory.THIRD_PARTY,
1230
- details: { tableName }
964
+ category: error.ErrorCategory.USER,
965
+ details: { page, ...perPageInput !== void 0 && { perPage: perPageInput } }
1231
966
  },
1232
- error$1
967
+ error$1 instanceof Error ? error$1 : new Error("Invalid pagination parameters")
1233
968
  );
1234
969
  }
1235
- }
1236
- async insert({ tableName, record }) {
970
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1237
971
  try {
1238
- if (!this.client) {
1239
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1240
- }
1241
- if (!tableName) {
1242
- throw new Error("tableName is required for insert.");
1243
- }
1244
- if (!record || Object.keys(record).length === 0) {
1245
- throw new Error("record is required and cannot be empty for insert.");
1246
- }
1247
- } catch (validationError) {
972
+ this.validateMetadataKeys(filter?.metadata);
973
+ } catch (error$1) {
1248
974
  throw new error.MastraError(
1249
975
  {
1250
- id: "STORAGE_LANCE_STORAGE_INSERT_INVALID_ARGS",
976
+ id: storage.createStorageErrorId("LANCE", "LIST_THREADS", "INVALID_METADATA_KEY"),
1251
977
  domain: error.ErrorDomain.STORAGE,
1252
978
  category: error.ErrorCategory.USER,
1253
- text: validationError.message,
1254
- details: { tableName }
979
+ details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
1255
980
  },
1256
- validationError
981
+ error$1 instanceof Error ? error$1 : new Error("Invalid metadata key")
1257
982
  );
1258
983
  }
1259
984
  try {
1260
- const table = await this.client.openTable(tableName);
1261
- const primaryId = getPrimaryKeys(tableName);
1262
- const processedRecord = { ...record };
1263
- for (const key in processedRecord) {
1264
- if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
1265
- this.logger.debug("Converting object to JSON string: ", processedRecord[key]);
1266
- processedRecord[key] = JSON.stringify(processedRecord[key]);
1267
- }
985
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
986
+ const { field, direction } = this.parseOrderBy(orderBy);
987
+ const table = await this.client.openTable(storage.TABLE_THREADS);
988
+ const whereClauses = [];
989
+ if (filter?.resourceId) {
990
+ whereClauses.push(`\`resourceId\` = '${this.escapeSql(filter.resourceId)}'`);
991
+ }
992
+ const whereClause = whereClauses.length > 0 ? whereClauses.join(" AND ") : "";
993
+ const query = whereClause ? table.query().where(whereClause) : table.query();
994
+ let records = await query.toArray();
995
+ if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
996
+ records = records.filter((record) => {
997
+ if (!record.metadata) return false;
998
+ let recordMeta;
999
+ if (typeof record.metadata === "string") {
1000
+ try {
1001
+ recordMeta = JSON.parse(record.metadata);
1002
+ } catch {
1003
+ return false;
1004
+ }
1005
+ } else {
1006
+ recordMeta = record.metadata;
1007
+ }
1008
+ return Object.entries(filter.metadata).every(([key, value]) => recordMeta[key] === value);
1009
+ });
1268
1010
  }
1269
- console.info(await table.schema());
1270
- await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
1011
+ const total = records.length;
1012
+ records.sort((a, b) => {
1013
+ const aValue = ["createdAt", "updatedAt"].includes(field) ? new Date(a[field]).getTime() : a[field];
1014
+ const bValue = ["createdAt", "updatedAt"].includes(field) ? new Date(b[field]).getTime() : b[field];
1015
+ if (aValue == null && bValue == null) return 0;
1016
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
1017
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
1018
+ if (typeof aValue === "string" && typeof bValue === "string") {
1019
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
1020
+ }
1021
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
1022
+ });
1023
+ const paginatedRecords = records.slice(offset, offset + perPage);
1024
+ const schema = await getTableSchema({ tableName: storage.TABLE_THREADS, client: this.client });
1025
+ const threads = paginatedRecords.map(
1026
+ (record) => processResultWithTypeConversion(record, schema)
1027
+ );
1028
+ return {
1029
+ threads,
1030
+ total,
1031
+ page,
1032
+ perPage: perPageForResponse,
1033
+ hasMore: offset + perPage < total
1034
+ };
1271
1035
  } catch (error$1) {
1272
1036
  throw new error.MastraError(
1273
1037
  {
1274
- id: "STORAGE_LANCE_STORAGE_INSERT_FAILED",
1038
+ id: storage.createStorageErrorId("LANCE", "LIST_THREADS", "FAILED"),
1275
1039
  domain: error.ErrorDomain.STORAGE,
1276
- category: error.ErrorCategory.THIRD_PARTY,
1277
- details: { tableName }
1040
+ category: error.ErrorCategory.THIRD_PARTY
1278
1041
  },
1279
1042
  error$1
1280
1043
  );
1281
1044
  }
1282
1045
  }
1283
- async batchInsert({ tableName, records }) {
1284
- try {
1285
- if (!this.client) {
1286
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1046
+ /**
1047
+ * Processes messages to include context messages based on withPreviousMessages and withNextMessages
1048
+ * @param records - The sorted array of records to process
1049
+ * @param include - The array of include specifications with context parameters
1050
+ * @returns The processed array with context messages included
1051
+ */
1052
+ processMessagesWithContext(records, include) {
1053
+ const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
1054
+ if (messagesWithContext.length === 0) {
1055
+ return records;
1056
+ }
1057
+ const messageIndexMap = /* @__PURE__ */ new Map();
1058
+ records.forEach((message, index) => {
1059
+ messageIndexMap.set(message.id, index);
1060
+ });
1061
+ const additionalIndices = /* @__PURE__ */ new Set();
1062
+ for (const item of messagesWithContext) {
1063
+ const messageIndex = messageIndexMap.get(item.id);
1064
+ if (messageIndex !== void 0) {
1065
+ if (item.withPreviousMessages) {
1066
+ const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
1067
+ for (let i = startIdx; i < messageIndex; i++) {
1068
+ additionalIndices.add(i);
1069
+ }
1070
+ }
1071
+ if (item.withNextMessages) {
1072
+ const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
1073
+ for (let i = messageIndex + 1; i <= endIdx; i++) {
1074
+ additionalIndices.add(i);
1075
+ }
1076
+ }
1287
1077
  }
1288
- if (!tableName) {
1289
- throw new Error("tableName is required for batchInsert.");
1078
+ }
1079
+ if (additionalIndices.size === 0) {
1080
+ return records;
1081
+ }
1082
+ const originalMatchIds = new Set(include.map((item) => item.id));
1083
+ const allIndices = /* @__PURE__ */ new Set();
1084
+ records.forEach((record, index) => {
1085
+ if (originalMatchIds.has(record.id)) {
1086
+ allIndices.add(index);
1290
1087
  }
1291
- if (!records || records.length === 0) {
1292
- throw new Error("records array is required and cannot be empty for batchInsert.");
1088
+ });
1089
+ additionalIndices.forEach((index) => {
1090
+ allIndices.add(index);
1091
+ });
1092
+ return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
1093
+ }
1094
+ /**
1095
+ * Parse message data from LanceDB record format to MastraDBMessage format
1096
+ */
1097
+ parseMessageData(data) {
1098
+ const { thread_id, ...rest } = data;
1099
+ return {
1100
+ ...rest,
1101
+ threadId: thread_id,
1102
+ content: typeof data.content === "string" ? (() => {
1103
+ try {
1104
+ return JSON.parse(data.content);
1105
+ } catch {
1106
+ return data.content;
1107
+ }
1108
+ })() : data.content,
1109
+ createdAt: new Date(data.createdAt),
1110
+ updatedAt: new Date(data.updatedAt)
1111
+ };
1112
+ }
1113
+ async updateMessages(args) {
1114
+ const { messages } = args;
1115
+ this.logger.debug("Updating messages", { count: messages.length });
1116
+ if (!messages.length) {
1117
+ return [];
1118
+ }
1119
+ const updatedMessages = [];
1120
+ const affectedThreadIds = /* @__PURE__ */ new Set();
1121
+ try {
1122
+ for (const updateData of messages) {
1123
+ const { id, ...updates } = updateData;
1124
+ const existingMessage = await this.#db.load({ tableName: storage.TABLE_MESSAGES, keys: { id } });
1125
+ if (!existingMessage) {
1126
+ this.logger.warn("Message not found for update", { id });
1127
+ continue;
1128
+ }
1129
+ const existingMsg = this.parseMessageData(existingMessage);
1130
+ const originalThreadId = existingMsg.threadId;
1131
+ affectedThreadIds.add(originalThreadId);
1132
+ const updatePayload = {};
1133
+ if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
1134
+ if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
1135
+ if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
1136
+ if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
1137
+ updatePayload.thread_id = updates.threadId;
1138
+ affectedThreadIds.add(updates.threadId);
1139
+ }
1140
+ if (updates.content) {
1141
+ const existingContent = existingMsg.content;
1142
+ let newContent = { ...existingContent };
1143
+ if (updates.content.metadata !== void 0) {
1144
+ newContent.metadata = {
1145
+ ...existingContent.metadata || {},
1146
+ ...updates.content.metadata || {}
1147
+ };
1148
+ }
1149
+ if (updates.content.content !== void 0) {
1150
+ newContent.content = updates.content.content;
1151
+ }
1152
+ if ("parts" in updates.content && updates.content.parts !== void 0) {
1153
+ newContent.parts = updates.content.parts;
1154
+ }
1155
+ updatePayload.content = JSON.stringify(newContent);
1156
+ }
1157
+ await this.#db.insert({ tableName: storage.TABLE_MESSAGES, record: { id, ...updatePayload } });
1158
+ const updatedMessage = await this.#db.load({ tableName: storage.TABLE_MESSAGES, keys: { id } });
1159
+ if (updatedMessage) {
1160
+ updatedMessages.push(this.parseMessageData(updatedMessage));
1161
+ }
1293
1162
  }
1294
- } catch (validationError) {
1163
+ for (const threadId of affectedThreadIds) {
1164
+ await this.#db.insert({
1165
+ tableName: storage.TABLE_THREADS,
1166
+ record: { id: threadId, updatedAt: Date.now() }
1167
+ });
1168
+ }
1169
+ return updatedMessages;
1170
+ } catch (error$1) {
1295
1171
  throw new error.MastraError(
1296
1172
  {
1297
- id: "STORAGE_LANCE_STORAGE_BATCH_INSERT_INVALID_ARGS",
1173
+ id: storage.createStorageErrorId("LANCE", "UPDATE_MESSAGES", "FAILED"),
1298
1174
  domain: error.ErrorDomain.STORAGE,
1299
- category: error.ErrorCategory.USER,
1300
- text: validationError.message,
1301
- details: { tableName }
1175
+ category: error.ErrorCategory.THIRD_PARTY,
1176
+ details: { count: messages.length }
1302
1177
  },
1303
- validationError
1178
+ error$1
1304
1179
  );
1305
1180
  }
1181
+ }
1182
+ async getResourceById({ resourceId }) {
1306
1183
  try {
1307
- const table = await this.client.openTable(tableName);
1308
- const primaryId = getPrimaryKeys(tableName);
1309
- const processedRecords = records.map((record) => {
1310
- const processedRecord = { ...record };
1311
- for (const key in processedRecord) {
1312
- if (processedRecord[key] == null) continue;
1313
- if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
1314
- processedRecord[key] = JSON.stringify(processedRecord[key]);
1315
- }
1184
+ const resource = await this.#db.load({ tableName: storage.TABLE_RESOURCES, keys: { id: resourceId } });
1185
+ if (!resource) {
1186
+ return null;
1187
+ }
1188
+ let createdAt;
1189
+ let updatedAt;
1190
+ try {
1191
+ if (resource.createdAt instanceof Date) {
1192
+ createdAt = resource.createdAt;
1193
+ } else if (typeof resource.createdAt === "string") {
1194
+ createdAt = new Date(resource.createdAt);
1195
+ } else if (typeof resource.createdAt === "number") {
1196
+ createdAt = new Date(resource.createdAt);
1197
+ } else {
1198
+ createdAt = /* @__PURE__ */ new Date();
1316
1199
  }
1317
- return processedRecord;
1318
- });
1319
- await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
1200
+ if (isNaN(createdAt.getTime())) {
1201
+ createdAt = /* @__PURE__ */ new Date();
1202
+ }
1203
+ } catch {
1204
+ createdAt = /* @__PURE__ */ new Date();
1205
+ }
1206
+ try {
1207
+ if (resource.updatedAt instanceof Date) {
1208
+ updatedAt = resource.updatedAt;
1209
+ } else if (typeof resource.updatedAt === "string") {
1210
+ updatedAt = new Date(resource.updatedAt);
1211
+ } else if (typeof resource.updatedAt === "number") {
1212
+ updatedAt = new Date(resource.updatedAt);
1213
+ } else {
1214
+ updatedAt = /* @__PURE__ */ new Date();
1215
+ }
1216
+ if (isNaN(updatedAt.getTime())) {
1217
+ updatedAt = /* @__PURE__ */ new Date();
1218
+ }
1219
+ } catch {
1220
+ updatedAt = /* @__PURE__ */ new Date();
1221
+ }
1222
+ let workingMemory = resource.workingMemory;
1223
+ if (workingMemory === null || workingMemory === void 0) {
1224
+ workingMemory = void 0;
1225
+ } else if (workingMemory === "") {
1226
+ workingMemory = "";
1227
+ } else if (typeof workingMemory === "object") {
1228
+ workingMemory = JSON.stringify(workingMemory);
1229
+ }
1230
+ let metadata = resource.metadata;
1231
+ if (metadata === "" || metadata === null || metadata === void 0) {
1232
+ metadata = void 0;
1233
+ } else if (typeof metadata === "string") {
1234
+ try {
1235
+ metadata = JSON.parse(metadata);
1236
+ } catch {
1237
+ metadata = metadata;
1238
+ }
1239
+ }
1240
+ return {
1241
+ ...resource,
1242
+ createdAt,
1243
+ updatedAt,
1244
+ workingMemory,
1245
+ metadata
1246
+ };
1320
1247
  } catch (error$1) {
1321
1248
  throw new error.MastraError(
1322
1249
  {
1323
- id: "STORAGE_LANCE_STORAGE_BATCH_INSERT_FAILED",
1250
+ id: storage.createStorageErrorId("LANCE", "GET_RESOURCE_BY_ID", "FAILED"),
1324
1251
  domain: error.ErrorDomain.STORAGE,
1325
- category: error.ErrorCategory.THIRD_PARTY,
1326
- details: { tableName }
1252
+ category: error.ErrorCategory.THIRD_PARTY
1327
1253
  },
1328
1254
  error$1
1329
1255
  );
1330
1256
  }
1331
1257
  }
1332
- async load({ tableName, keys }) {
1333
- try {
1334
- if (!this.client) {
1335
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1336
- }
1337
- if (!tableName) {
1338
- throw new Error("tableName is required for load.");
1339
- }
1340
- if (!keys || Object.keys(keys).length === 0) {
1341
- throw new Error("keys are required and cannot be empty for load.");
1342
- }
1343
- } catch (validationError) {
1344
- throw new error.MastraError(
1345
- {
1346
- id: "STORAGE_LANCE_STORAGE_LOAD_INVALID_ARGS",
1347
- domain: error.ErrorDomain.STORAGE,
1348
- category: error.ErrorCategory.USER,
1349
- text: validationError.message,
1350
- details: { tableName }
1351
- },
1352
- validationError
1353
- );
1354
- }
1258
+ async saveResource({ resource }) {
1355
1259
  try {
1356
- const table = await this.client.openTable(tableName);
1357
- const tableSchema = await getTableSchema({ tableName, client: this.client });
1358
- const query = table.query();
1359
- if (Object.keys(keys).length > 0) {
1360
- validateKeyTypes(keys, tableSchema);
1361
- const filterConditions = Object.entries(keys).map(([key, value]) => {
1362
- const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
1363
- const quotedKey = isCamelCase ? `\`${key}\`` : key;
1364
- if (typeof value === "string") {
1365
- return `${quotedKey} = '${value}'`;
1366
- } else if (value === null) {
1367
- return `${quotedKey} IS NULL`;
1368
- } else {
1369
- return `${quotedKey} = ${value}`;
1370
- }
1371
- }).join(" AND ");
1372
- this.logger.debug("where clause generated: " + filterConditions);
1373
- query.where(filterConditions);
1374
- }
1375
- const result = await query.limit(1).toArray();
1376
- if (result.length === 0) {
1377
- this.logger.debug("No record found");
1378
- return null;
1379
- }
1380
- return processResultWithTypeConversion(result[0], tableSchema);
1260
+ const record = {
1261
+ ...resource,
1262
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : "",
1263
+ createdAt: resource.createdAt.getTime(),
1264
+ // Store as timestamp (milliseconds)
1265
+ updatedAt: resource.updatedAt.getTime()
1266
+ // Store as timestamp (milliseconds)
1267
+ };
1268
+ const table = await this.client.openTable(storage.TABLE_RESOURCES);
1269
+ await table.add([record], { mode: "append" });
1270
+ return resource;
1381
1271
  } catch (error$1) {
1382
- if (error$1 instanceof error.MastraError) throw error$1;
1383
1272
  throw new error.MastraError(
1384
1273
  {
1385
- id: "STORAGE_LANCE_STORAGE_LOAD_FAILED",
1274
+ id: storage.createStorageErrorId("LANCE", "SAVE_RESOURCE", "FAILED"),
1386
1275
  domain: error.ErrorDomain.STORAGE,
1387
- category: error.ErrorCategory.THIRD_PARTY,
1388
- details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? "" }
1276
+ category: error.ErrorCategory.THIRD_PARTY
1389
1277
  },
1390
1278
  error$1
1391
1279
  );
1392
1280
  }
1393
1281
  }
1282
+ async updateResource({
1283
+ resourceId,
1284
+ workingMemory,
1285
+ metadata
1286
+ }) {
1287
+ const maxRetries = 3;
1288
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1289
+ try {
1290
+ const existingResource = await this.getResourceById({ resourceId });
1291
+ if (!existingResource) {
1292
+ const newResource = {
1293
+ id: resourceId,
1294
+ workingMemory,
1295
+ metadata: metadata || {},
1296
+ createdAt: /* @__PURE__ */ new Date(),
1297
+ updatedAt: /* @__PURE__ */ new Date()
1298
+ };
1299
+ return this.saveResource({ resource: newResource });
1300
+ }
1301
+ const updatedResource = {
1302
+ ...existingResource,
1303
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1304
+ metadata: {
1305
+ ...existingResource.metadata,
1306
+ ...metadata
1307
+ },
1308
+ updatedAt: /* @__PURE__ */ new Date()
1309
+ };
1310
+ const record = {
1311
+ id: resourceId,
1312
+ workingMemory: updatedResource.workingMemory || "",
1313
+ metadata: updatedResource.metadata ? JSON.stringify(updatedResource.metadata) : "",
1314
+ updatedAt: updatedResource.updatedAt.getTime()
1315
+ // Store as timestamp (milliseconds)
1316
+ };
1317
+ const table = await this.client.openTable(storage.TABLE_RESOURCES);
1318
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
1319
+ return updatedResource;
1320
+ } catch (error$1) {
1321
+ if (error$1.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
1322
+ const delay = Math.pow(2, attempt) * 10;
1323
+ await new Promise((resolve) => setTimeout(resolve, delay));
1324
+ continue;
1325
+ }
1326
+ throw new error.MastraError(
1327
+ {
1328
+ id: storage.createStorageErrorId("LANCE", "UPDATE_RESOURCE", "FAILED"),
1329
+ domain: error.ErrorDomain.STORAGE,
1330
+ category: error.ErrorCategory.THIRD_PARTY
1331
+ },
1332
+ error$1
1333
+ );
1334
+ }
1335
+ }
1336
+ throw new Error("Unexpected end of retry loop");
1337
+ }
1394
1338
  };
1395
1339
  var StoreScoresLance = class extends storage.ScoresStorage {
1396
1340
  client;
1397
- constructor({ client }) {
1341
+ #db;
1342
+ constructor(config) {
1398
1343
  super();
1344
+ const client = resolveLanceConfig(config);
1399
1345
  this.client = client;
1346
+ this.#db = new LanceDB({ client });
1347
+ }
1348
+ async init() {
1349
+ await this.#db.createTable({ tableName: storage.TABLE_SCORERS, schema: storage.SCORERS_SCHEMA });
1350
+ await this.#db.alterTable({
1351
+ tableName: storage.TABLE_SCORERS,
1352
+ schema: storage.SCORERS_SCHEMA,
1353
+ ifNotExists: ["spanId", "requestContext"]
1354
+ });
1355
+ }
1356
+ async dangerouslyClearAll() {
1357
+ await this.#db.clearTable({ tableName: storage.TABLE_SCORERS });
1400
1358
  }
1401
1359
  async saveScore(score) {
1402
1360
  let validatedScore;
1403
1361
  try {
1404
- validatedScore = scores.saveScorePayloadSchema.parse(score);
1362
+ validatedScore = evals.saveScorePayloadSchema.parse(score);
1405
1363
  } catch (error$1) {
1406
1364
  throw new error.MastraError(
1407
1365
  {
1408
- id: "LANCE_STORAGE_SAVE_SCORE_FAILED",
1366
+ id: storage.createStorageErrorId("LANCE", "SAVE_SCORE", "VALIDATION_FAILED"),
1409
1367
  text: "Failed to save score in LanceStorage",
1410
1368
  domain: error.ErrorDomain.STORAGE,
1411
- category: error.ErrorCategory.THIRD_PARTY
1369
+ category: error.ErrorCategory.USER,
1370
+ details: {
1371
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
1372
+ entityId: score.entityId ?? "unknown",
1373
+ entityType: score.entityType ?? "unknown",
1374
+ traceId: score.traceId ?? "",
1375
+ spanId: score.spanId ?? ""
1376
+ }
1412
1377
  },
1413
1378
  error$1
1414
1379
  );
1415
1380
  }
1381
+ const id = crypto.randomUUID();
1382
+ const now = /* @__PURE__ */ new Date();
1416
1383
  try {
1417
- const id = crypto.randomUUID();
1418
1384
  const table = await this.client.openTable(storage.TABLE_SCORERS);
1419
1385
  const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1420
1386
  const allowedFields = new Set(schema.fields.map((f) => f.name));
1421
1387
  const filteredScore = {};
1422
- Object.keys(validatedScore).forEach((key) => {
1388
+ for (const key of Object.keys(validatedScore)) {
1423
1389
  if (allowedFields.has(key)) {
1424
- filteredScore[key] = score[key];
1390
+ filteredScore[key] = validatedScore[key];
1425
1391
  }
1426
- });
1392
+ }
1427
1393
  for (const key in filteredScore) {
1428
1394
  if (filteredScore[key] !== null && typeof filteredScore[key] === "object" && !(filteredScore[key] instanceof Date)) {
1429
1395
  filteredScore[key] = JSON.stringify(filteredScore[key]);
1430
1396
  }
1431
1397
  }
1432
1398
  filteredScore.id = id;
1399
+ filteredScore.createdAt = now;
1400
+ filteredScore.updatedAt = now;
1433
1401
  await table.add([filteredScore], { mode: "append" });
1434
- return { score };
1402
+ return { score: { ...validatedScore, id, createdAt: now, updatedAt: now } };
1435
1403
  } catch (error$1) {
1436
1404
  throw new error.MastraError(
1437
1405
  {
1438
- id: "LANCE_STORAGE_SAVE_SCORE_FAILED",
1406
+ id: storage.createStorageErrorId("LANCE", "SAVE_SCORE", "FAILED"),
1439
1407
  text: "Failed to save score in LanceStorage",
1440
1408
  domain: error.ErrorDomain.STORAGE,
1441
1409
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1451,12 +1419,11 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1451
1419
  const query = table.query().where(`id = '${id}'`).limit(1);
1452
1420
  const records = await query.toArray();
1453
1421
  if (records.length === 0) return null;
1454
- const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1455
- return processResultWithTypeConversion(records[0], schema);
1422
+ return await this.transformScoreRow(records[0]);
1456
1423
  } catch (error$1) {
1457
1424
  throw new error.MastraError(
1458
1425
  {
1459
- id: "LANCE_STORAGE_GET_SCORE_BY_ID_FAILED",
1426
+ id: storage.createStorageErrorId("LANCE", "GET_SCORE_BY_ID", "FAILED"),
1460
1427
  text: "Failed to get score by id in LanceStorage",
1461
1428
  domain: error.ErrorDomain.STORAGE,
1462
1429
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1466,7 +1433,23 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1466
1433
  );
1467
1434
  }
1468
1435
  }
1469
- async getScoresByScorerId({
1436
+ /**
1437
+ * LanceDB-specific score row transformation.
1438
+ *
1439
+ * Note: This implementation does NOT use coreTransformScoreRow because:
1440
+ * 1. LanceDB stores schema information in the table itself (requires async fetch)
1441
+ * 2. Uses processResultWithTypeConversion utility for LanceDB-specific type handling
1442
+ */
1443
+ async transformScoreRow(row) {
1444
+ const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1445
+ const transformed = processResultWithTypeConversion(row, schema);
1446
+ return {
1447
+ ...transformed,
1448
+ createdAt: row.createdAt,
1449
+ updatedAt: row.updatedAt
1450
+ };
1451
+ }
1452
+ async listScoresByScorerId({
1470
1453
  scorerId,
1471
1454
  pagination,
1472
1455
  entityId,
@@ -1474,9 +1457,10 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1474
1457
  source
1475
1458
  }) {
1476
1459
  try {
1460
+ const { page, perPage: perPageInput } = pagination;
1461
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1462
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1477
1463
  const table = await this.client.openTable(storage.TABLE_SCORERS);
1478
- const { page = 0, perPage = 10 } = pagination || {};
1479
- const offset = page * perPage;
1480
1464
  let query = table.query().where(`\`scorerId\` = '${scorerId}'`);
1481
1465
  if (source) {
1482
1466
  query = query.where(`\`source\` = '${source}'`);
@@ -1487,30 +1471,38 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1487
1471
  if (entityType) {
1488
1472
  query = query.where(`\`entityType\` = '${entityType}'`);
1489
1473
  }
1490
- query = query.limit(perPage);
1491
- if (offset > 0) query.offset(offset);
1492
- const records = await query.toArray();
1493
- const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1494
- const scores = processResultWithTypeConversion(records, schema);
1495
1474
  let totalQuery = table.query().where(`\`scorerId\` = '${scorerId}'`);
1496
1475
  if (source) {
1497
1476
  totalQuery = totalQuery.where(`\`source\` = '${source}'`);
1498
1477
  }
1478
+ if (entityId) {
1479
+ totalQuery = totalQuery.where(`\`entityId\` = '${entityId}'`);
1480
+ }
1481
+ if (entityType) {
1482
+ totalQuery = totalQuery.where(`\`entityType\` = '${entityType}'`);
1483
+ }
1499
1484
  const allRecords = await totalQuery.toArray();
1500
1485
  const total = allRecords.length;
1486
+ const end = perPageInput === false ? total : start + perPage;
1487
+ if (perPageInput !== false) {
1488
+ query = query.limit(perPage);
1489
+ if (start > 0) query = query.offset(start);
1490
+ }
1491
+ const records = await query.toArray();
1492
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1501
1493
  return {
1502
1494
  pagination: {
1503
1495
  page,
1504
- perPage,
1496
+ perPage: perPageForResponse,
1505
1497
  total,
1506
- hasMore: offset + scores.length < total
1498
+ hasMore: end < total
1507
1499
  },
1508
1500
  scores
1509
1501
  };
1510
1502
  } catch (error$1) {
1511
1503
  throw new error.MastraError(
1512
1504
  {
1513
- id: "LANCE_STORAGE_GET_SCORES_BY_SCORER_ID_FAILED",
1505
+ id: storage.createStorageErrorId("LANCE", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
1514
1506
  text: "Failed to get scores by scorerId in LanceStorage",
1515
1507
  domain: error.ErrorDomain.STORAGE,
1516
1508
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1520,34 +1512,38 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1520
1512
  );
1521
1513
  }
1522
1514
  }
1523
- async getScoresByRunId({
1515
+ async listScoresByRunId({
1524
1516
  runId,
1525
1517
  pagination
1526
1518
  }) {
1527
1519
  try {
1520
+ const { page, perPage: perPageInput } = pagination;
1521
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1522
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1528
1523
  const table = await this.client.openTable(storage.TABLE_SCORERS);
1529
- const { page = 0, perPage = 10 } = pagination || {};
1530
- const offset = page * perPage;
1531
- const query = table.query().where(`\`runId\` = '${runId}'`).limit(perPage);
1532
- if (offset > 0) query.offset(offset);
1533
- const records = await query.toArray();
1534
- const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1535
- const scores = processResultWithTypeConversion(records, schema);
1536
1524
  const allRecords = await table.query().where(`\`runId\` = '${runId}'`).toArray();
1537
1525
  const total = allRecords.length;
1526
+ const end = perPageInput === false ? total : start + perPage;
1527
+ let query = table.query().where(`\`runId\` = '${runId}'`);
1528
+ if (perPageInput !== false) {
1529
+ query = query.limit(perPage);
1530
+ if (start > 0) query = query.offset(start);
1531
+ }
1532
+ const records = await query.toArray();
1533
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1538
1534
  return {
1539
1535
  pagination: {
1540
1536
  page,
1541
- perPage,
1537
+ perPage: perPageForResponse,
1542
1538
  total,
1543
- hasMore: offset + scores.length < total
1539
+ hasMore: end < total
1544
1540
  },
1545
1541
  scores
1546
1542
  };
1547
1543
  } catch (error$1) {
1548
1544
  throw new error.MastraError(
1549
1545
  {
1550
- id: "LANCE_STORAGE_GET_SCORES_BY_RUN_ID_FAILED",
1546
+ id: storage.createStorageErrorId("LANCE", "LIST_SCORES_BY_RUN_ID", "FAILED"),
1551
1547
  text: "Failed to get scores by runId in LanceStorage",
1552
1548
  domain: error.ErrorDomain.STORAGE,
1553
1549
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1557,35 +1553,39 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1557
1553
  );
1558
1554
  }
1559
1555
  }
1560
- async getScoresByEntityId({
1556
+ async listScoresByEntityId({
1561
1557
  entityId,
1562
1558
  entityType,
1563
1559
  pagination
1564
1560
  }) {
1565
1561
  try {
1562
+ const { page, perPage: perPageInput } = pagination;
1563
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1564
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1566
1565
  const table = await this.client.openTable(storage.TABLE_SCORERS);
1567
- const { page = 0, perPage = 10 } = pagination || {};
1568
- const offset = page * perPage;
1569
- const query = table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).limit(perPage);
1570
- if (offset > 0) query.offset(offset);
1571
- const records = await query.toArray();
1572
- const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1573
- const scores = processResultWithTypeConversion(records, schema);
1574
1566
  const allRecords = await table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).toArray();
1575
1567
  const total = allRecords.length;
1568
+ const end = perPageInput === false ? total : start + perPage;
1569
+ let query = table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`);
1570
+ if (perPageInput !== false) {
1571
+ query = query.limit(perPage);
1572
+ if (start > 0) query = query.offset(start);
1573
+ }
1574
+ const records = await query.toArray();
1575
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1576
1576
  return {
1577
1577
  pagination: {
1578
1578
  page,
1579
- perPage,
1579
+ perPage: perPageForResponse,
1580
1580
  total,
1581
- hasMore: offset + scores.length < total
1581
+ hasMore: end < total
1582
1582
  },
1583
1583
  scores
1584
1584
  };
1585
1585
  } catch (error$1) {
1586
1586
  throw new error.MastraError(
1587
1587
  {
1588
- id: "LANCE_STORAGE_GET_SCORES_BY_ENTITY_ID_FAILED",
1588
+ id: storage.createStorageErrorId("LANCE", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
1589
1589
  text: "Failed to get scores by entityId and entityType in LanceStorage",
1590
1590
  domain: error.ErrorDomain.STORAGE,
1591
1591
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1595,307 +1595,138 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1595
1595
  );
1596
1596
  }
1597
1597
  }
1598
- async getScoresBySpan({
1598
+ async listScoresBySpan({
1599
1599
  traceId,
1600
1600
  spanId,
1601
1601
  pagination
1602
1602
  }) {
1603
1603
  try {
1604
+ const { page, perPage: perPageInput } = pagination;
1605
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1606
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1604
1607
  const table = await this.client.openTable(storage.TABLE_SCORERS);
1605
- const { page = 0, perPage = 10 } = pagination || {};
1606
- const offset = page * perPage;
1607
- const query = table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`).limit(perPage);
1608
- if (offset > 0) query.offset(offset);
1609
- const records = await query.toArray();
1610
- const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1611
- const scores = processResultWithTypeConversion(records, schema);
1612
- const allRecords = await table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`).toArray();
1613
- const total = allRecords.length;
1614
- return {
1615
- pagination: {
1616
- page,
1617
- perPage,
1618
- total,
1619
- hasMore: offset + scores.length < total
1620
- },
1621
- scores
1622
- };
1623
- } catch (error$1) {
1624
- throw new error.MastraError(
1625
- {
1626
- id: "LANCE_STORAGE_GET_SCORES_BY_SPAN_FAILED",
1627
- text: "Failed to get scores by traceId and spanId in LanceStorage",
1628
- domain: error.ErrorDomain.STORAGE,
1629
- category: error.ErrorCategory.THIRD_PARTY,
1630
- details: { error: error$1?.message }
1631
- },
1632
- error$1
1633
- );
1634
- }
1635
- }
1636
- };
1637
- var StoreTracesLance = class extends storage.TracesStorage {
1638
- client;
1639
- operations;
1640
- constructor({ client, operations }) {
1641
- super();
1642
- this.client = client;
1643
- this.operations = operations;
1644
- }
1645
- async saveTrace({ trace }) {
1646
- try {
1647
- const table = await this.client.openTable(storage.TABLE_TRACES);
1648
- const record = {
1649
- ...trace,
1650
- attributes: JSON.stringify(trace.attributes),
1651
- status: JSON.stringify(trace.status),
1652
- events: JSON.stringify(trace.events),
1653
- links: JSON.stringify(trace.links),
1654
- other: JSON.stringify(trace.other)
1655
- };
1656
- await table.add([record], { mode: "append" });
1657
- return trace;
1658
- } catch (error$1) {
1659
- throw new error.MastraError(
1660
- {
1661
- id: "LANCE_STORE_SAVE_TRACE_FAILED",
1662
- domain: error.ErrorDomain.STORAGE,
1663
- category: error.ErrorCategory.THIRD_PARTY
1664
- },
1665
- error$1
1666
- );
1667
- }
1668
- }
1669
- async getTraceById({ traceId }) {
1670
- try {
1671
- const table = await this.client.openTable(storage.TABLE_TRACES);
1672
- const query = table.query().where(`id = '${traceId}'`);
1673
- const records = await query.toArray();
1674
- return records[0];
1675
- } catch (error$1) {
1676
- throw new error.MastraError(
1677
- {
1678
- id: "LANCE_STORE_GET_TRACE_BY_ID_FAILED",
1679
- domain: error.ErrorDomain.STORAGE,
1680
- category: error.ErrorCategory.THIRD_PARTY
1681
- },
1682
- error$1
1683
- );
1684
- }
1685
- }
1686
- async getTraces({
1687
- name,
1688
- scope,
1689
- page = 1,
1690
- perPage = 10,
1691
- attributes
1692
- }) {
1693
- try {
1694
- const table = await this.client.openTable(storage.TABLE_TRACES);
1695
- const query = table.query();
1696
- if (name) {
1697
- query.where(`name = '${name}'`);
1698
- }
1699
- if (scope) {
1700
- query.where(`scope = '${scope}'`);
1701
- }
1702
- if (attributes) {
1703
- query.where(`attributes = '${JSON.stringify(attributes)}'`);
1704
- }
1705
- const offset = (page - 1) * perPage;
1706
- query.limit(perPage);
1707
- if (offset > 0) {
1708
- query.offset(offset);
1709
- }
1710
- const records = await query.toArray();
1711
- return records.map((record) => {
1712
- const processed = {
1713
- ...record,
1714
- attributes: record.attributes ? JSON.parse(record.attributes) : {},
1715
- status: record.status ? JSON.parse(record.status) : {},
1716
- events: record.events ? JSON.parse(record.events) : [],
1717
- links: record.links ? JSON.parse(record.links) : [],
1718
- other: record.other ? JSON.parse(record.other) : {},
1719
- startTime: new Date(record.startTime),
1720
- endTime: new Date(record.endTime),
1721
- createdAt: new Date(record.createdAt)
1722
- };
1723
- if (processed.parentSpanId === null || processed.parentSpanId === void 0) {
1724
- processed.parentSpanId = "";
1725
- } else {
1726
- processed.parentSpanId = String(processed.parentSpanId);
1727
- }
1728
- return processed;
1729
- });
1730
- } catch (error$1) {
1731
- throw new error.MastraError(
1732
- {
1733
- id: "LANCE_STORE_GET_TRACES_FAILED",
1734
- domain: error.ErrorDomain.STORAGE,
1735
- category: error.ErrorCategory.THIRD_PARTY,
1736
- details: { name: name ?? "", scope: scope ?? "" }
1737
- },
1738
- error$1
1739
- );
1740
- }
1741
- }
1742
- async getTracesPaginated(args) {
1743
- try {
1744
- const table = await this.client.openTable(storage.TABLE_TRACES);
1745
- const query = table.query();
1746
- const conditions = [];
1747
- if (args.name) {
1748
- conditions.push(`name = '${args.name}'`);
1749
- }
1750
- if (args.scope) {
1751
- conditions.push(`scope = '${args.scope}'`);
1752
- }
1753
- if (args.attributes) {
1754
- const attributesStr = JSON.stringify(args.attributes);
1755
- conditions.push(`attributes LIKE '%${attributesStr.replace(/"/g, '\\"')}%'`);
1756
- }
1757
- if (args.dateRange?.start) {
1758
- conditions.push(`\`createdAt\` >= ${args.dateRange.start.getTime()}`);
1759
- }
1760
- if (args.dateRange?.end) {
1761
- conditions.push(`\`createdAt\` <= ${args.dateRange.end.getTime()}`);
1762
- }
1763
- if (conditions.length > 0) {
1764
- const whereClause = conditions.join(" AND ");
1765
- query.where(whereClause);
1766
- }
1767
- let total = 0;
1768
- if (conditions.length > 0) {
1769
- const countQuery = table.query().where(conditions.join(" AND "));
1770
- const allRecords = await countQuery.toArray();
1771
- total = allRecords.length;
1772
- } else {
1773
- total = await table.countRows();
1774
- }
1775
- const page = args.page || 0;
1776
- const perPage = args.perPage || 10;
1777
- const offset = page * perPage;
1778
- query.limit(perPage);
1779
- if (offset > 0) {
1780
- query.offset(offset);
1608
+ const allRecords = await table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`).toArray();
1609
+ const total = allRecords.length;
1610
+ const end = perPageInput === false ? total : start + perPage;
1611
+ let query = table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`);
1612
+ if (perPageInput !== false) {
1613
+ query = query.limit(perPage);
1614
+ if (start > 0) query = query.offset(start);
1781
1615
  }
1782
1616
  const records = await query.toArray();
1783
- const traces = records.map((record) => {
1784
- const processed = {
1785
- ...record,
1786
- attributes: record.attributes ? JSON.parse(record.attributes) : {},
1787
- status: record.status ? JSON.parse(record.status) : {},
1788
- events: record.events ? JSON.parse(record.events) : [],
1789
- links: record.links ? JSON.parse(record.links) : [],
1790
- other: record.other ? JSON.parse(record.other) : {},
1791
- startTime: new Date(record.startTime),
1792
- endTime: new Date(record.endTime),
1793
- createdAt: new Date(record.createdAt)
1794
- };
1795
- if (processed.parentSpanId === null || processed.parentSpanId === void 0) {
1796
- processed.parentSpanId = "";
1797
- } else {
1798
- processed.parentSpanId = String(processed.parentSpanId);
1799
- }
1800
- return processed;
1801
- });
1617
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1802
1618
  return {
1803
- traces,
1804
- total,
1805
- page,
1806
- perPage,
1807
- hasMore: total > (page + 1) * perPage
1619
+ pagination: {
1620
+ page,
1621
+ perPage: perPageForResponse,
1622
+ total,
1623
+ hasMore: end < total
1624
+ },
1625
+ scores
1808
1626
  };
1809
1627
  } catch (error$1) {
1810
1628
  throw new error.MastraError(
1811
1629
  {
1812
- id: "LANCE_STORE_GET_TRACES_PAGINATED_FAILED",
1630
+ id: storage.createStorageErrorId("LANCE", "LIST_SCORES_BY_SPAN", "FAILED"),
1631
+ text: "Failed to get scores by traceId and spanId in LanceStorage",
1813
1632
  domain: error.ErrorDomain.STORAGE,
1814
1633
  category: error.ErrorCategory.THIRD_PARTY,
1815
- details: { name: args.name ?? "", scope: args.scope ?? "" }
1634
+ details: { error: error$1?.message }
1816
1635
  },
1817
1636
  error$1
1818
1637
  );
1819
1638
  }
1820
1639
  }
1821
- async batchTraceInsert({ records }) {
1822
- this.logger.debug("Batch inserting traces", { count: records.length });
1823
- await this.operations.batchInsert({
1824
- tableName: storage.TABLE_TRACES,
1825
- records
1826
- });
1827
- }
1828
1640
  };
1829
- function parseWorkflowRun(row) {
1830
- let parsedSnapshot = row.snapshot;
1831
- if (typeof parsedSnapshot === "string") {
1832
- try {
1833
- parsedSnapshot = JSON.parse(row.snapshot);
1834
- } catch (e) {
1835
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1836
- }
1837
- }
1838
- return {
1839
- workflowName: row.workflow_name,
1840
- runId: row.run_id,
1841
- snapshot: parsedSnapshot,
1842
- createdAt: storage.ensureDate(row.createdAt),
1843
- updatedAt: storage.ensureDate(row.updatedAt),
1844
- resourceId: row.resourceId
1845
- };
1641
+ function escapeSql(str) {
1642
+ return str.replace(/'/g, "''");
1846
1643
  }
1847
1644
  var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1848
1645
  client;
1849
- constructor({ client }) {
1646
+ #db;
1647
+ constructor(config) {
1850
1648
  super();
1649
+ const client = resolveLanceConfig(config);
1851
1650
  this.client = client;
1651
+ this.#db = new LanceDB({ client });
1852
1652
  }
1853
- updateWorkflowResults({
1854
- // workflowName,
1855
- // runId,
1856
- // stepId,
1857
- // result,
1858
- // runtimeContext,
1859
- }) {
1860
- throw new Error("Method not implemented.");
1653
+ supportsConcurrentUpdates() {
1654
+ return false;
1861
1655
  }
1862
- updateWorkflowState({
1863
- // workflowName,
1864
- // runId,
1865
- // opts,
1866
- }) {
1867
- throw new Error("Method not implemented.");
1656
+ parseWorkflowRun(row) {
1657
+ let parsedSnapshot = row.snapshot;
1658
+ if (typeof parsedSnapshot === "string") {
1659
+ try {
1660
+ parsedSnapshot = JSON.parse(row.snapshot);
1661
+ } catch (e) {
1662
+ this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1663
+ }
1664
+ }
1665
+ return {
1666
+ workflowName: row.workflow_name,
1667
+ runId: row.run_id,
1668
+ snapshot: parsedSnapshot,
1669
+ createdAt: storage.ensureDate(row.createdAt),
1670
+ updatedAt: storage.ensureDate(row.updatedAt),
1671
+ resourceId: row.resourceId
1672
+ };
1673
+ }
1674
+ async init() {
1675
+ const schema = storage.TABLE_SCHEMAS[storage.TABLE_WORKFLOW_SNAPSHOT];
1676
+ await this.#db.createTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT, schema });
1677
+ await this.#db.alterTable({
1678
+ tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
1679
+ schema,
1680
+ ifNotExists: ["resourceId"]
1681
+ });
1682
+ }
1683
+ async dangerouslyClearAll() {
1684
+ await this.#db.clearTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
1685
+ }
1686
+ async updateWorkflowResults(_args) {
1687
+ throw new Error(
1688
+ "updateWorkflowResults is not implemented for LanceDB storage. LanceDB does not support atomic read-modify-write operations needed for concurrent workflow updates."
1689
+ );
1690
+ }
1691
+ async updateWorkflowState(_args) {
1692
+ throw new Error(
1693
+ "updateWorkflowState is not implemented for LanceDB storage. LanceDB does not support atomic read-modify-write operations needed for concurrent workflow updates."
1694
+ );
1868
1695
  }
1869
1696
  async persistWorkflowSnapshot({
1870
1697
  workflowName,
1871
1698
  runId,
1872
1699
  resourceId,
1873
- snapshot
1700
+ snapshot,
1701
+ createdAt,
1702
+ updatedAt
1874
1703
  }) {
1875
1704
  try {
1876
1705
  const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1877
- const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
1706
+ const query = table.query().where(`workflow_name = '${escapeSql(workflowName)}' AND run_id = '${escapeSql(runId)}'`);
1878
1707
  const records = await query.toArray();
1879
- let createdAt;
1880
- const now = Date.now();
1708
+ let createdAtValue;
1709
+ const now = createdAt?.getTime() ?? Date.now();
1881
1710
  if (records.length > 0) {
1882
- createdAt = records[0].createdAt ?? now;
1711
+ createdAtValue = records[0].createdAt ?? now;
1883
1712
  } else {
1884
- createdAt = now;
1713
+ createdAtValue = now;
1885
1714
  }
1715
+ const { status, value, ...rest } = snapshot;
1886
1716
  const record = {
1887
1717
  workflow_name: workflowName,
1888
1718
  run_id: runId,
1889
1719
  resourceId,
1890
- snapshot: JSON.stringify(snapshot),
1891
- createdAt,
1892
- updatedAt: now
1720
+ snapshot: JSON.stringify({ status, value, ...rest }),
1721
+ // this is to ensure status is always just before value, for when querying the db by status
1722
+ createdAt: createdAtValue,
1723
+ updatedAt: updatedAt ?? now
1893
1724
  };
1894
1725
  await table.mergeInsert(["workflow_name", "run_id"]).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
1895
1726
  } catch (error$1) {
1896
1727
  throw new error.MastraError(
1897
1728
  {
1898
- id: "LANCE_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
1729
+ id: storage.createStorageErrorId("LANCE", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
1899
1730
  domain: error.ErrorDomain.STORAGE,
1900
1731
  category: error.ErrorCategory.THIRD_PARTY,
1901
1732
  details: { workflowName, runId }
@@ -1910,13 +1741,13 @@ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1910
1741
  }) {
1911
1742
  try {
1912
1743
  const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1913
- const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
1744
+ const query = table.query().where(`workflow_name = '${escapeSql(workflowName)}' AND run_id = '${escapeSql(runId)}'`);
1914
1745
  const records = await query.toArray();
1915
1746
  return records.length > 0 ? JSON.parse(records[0].snapshot) : null;
1916
1747
  } catch (error$1) {
1917
1748
  throw new error.MastraError(
1918
1749
  {
1919
- id: "LANCE_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
1750
+ id: storage.createStorageErrorId("LANCE", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
1920
1751
  domain: error.ErrorDomain.STORAGE,
1921
1752
  category: error.ErrorCategory.THIRD_PARTY,
1922
1753
  details: { workflowName, runId }
@@ -1928,19 +1759,19 @@ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1928
1759
  async getWorkflowRunById(args) {
1929
1760
  try {
1930
1761
  const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1931
- let whereClause = `run_id = '${args.runId}'`;
1762
+ let whereClause = `run_id = '${escapeSql(args.runId)}'`;
1932
1763
  if (args.workflowName) {
1933
- whereClause += ` AND workflow_name = '${args.workflowName}'`;
1764
+ whereClause += ` AND workflow_name = '${escapeSql(args.workflowName)}'`;
1934
1765
  }
1935
1766
  const query = table.query().where(whereClause);
1936
1767
  const records = await query.toArray();
1937
1768
  if (records.length === 0) return null;
1938
1769
  const record = records[0];
1939
- return parseWorkflowRun(record);
1770
+ return this.parseWorkflowRun(record);
1940
1771
  } catch (error$1) {
1941
1772
  throw new error.MastraError(
1942
1773
  {
1943
- id: "LANCE_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
1774
+ id: storage.createStorageErrorId("LANCE", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
1944
1775
  domain: error.ErrorDomain.STORAGE,
1945
1776
  category: error.ErrorCategory.THIRD_PARTY,
1946
1777
  details: { runId: args.runId, workflowName: args.workflowName ?? "" }
@@ -1949,359 +1780,182 @@ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1949
1780
  );
1950
1781
  }
1951
1782
  }
1952
- async getWorkflowRuns(args) {
1783
+ async deleteWorkflowRunById({ runId, workflowName }) {
1953
1784
  try {
1954
1785
  const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1955
- let query = table.query();
1956
- const conditions = [];
1957
- if (args?.workflowName) {
1958
- conditions.push(`workflow_name = '${args.workflowName.replace(/'/g, "''")}'`);
1959
- }
1960
- if (args?.resourceId) {
1961
- conditions.push(`\`resourceId\` = '${args.resourceId}'`);
1962
- }
1963
- if (args?.fromDate instanceof Date) {
1964
- conditions.push(`\`createdAt\` >= ${args.fromDate.getTime()}`);
1965
- }
1966
- if (args?.toDate instanceof Date) {
1967
- conditions.push(`\`createdAt\` <= ${args.toDate.getTime()}`);
1968
- }
1969
- let total = 0;
1970
- if (conditions.length > 0) {
1971
- query = query.where(conditions.join(" AND "));
1972
- total = await table.countRows(conditions.join(" AND "));
1973
- } else {
1974
- total = await table.countRows();
1975
- }
1976
- if (args?.limit) {
1977
- query.limit(args.limit);
1978
- }
1979
- if (args?.offset) {
1980
- query.offset(args.offset);
1981
- }
1982
- const records = await query.toArray();
1983
- return {
1984
- runs: records.map((record) => parseWorkflowRun(record)),
1985
- total: total || records.length
1986
- };
1786
+ const whereClause = `run_id = '${escapeSql(runId)}' AND workflow_name = '${escapeSql(workflowName)}'`;
1787
+ await table.delete(whereClause);
1987
1788
  } catch (error$1) {
1988
1789
  throw new error.MastraError(
1989
1790
  {
1990
- id: "LANCE_STORE_GET_WORKFLOW_RUNS_FAILED",
1791
+ id: storage.createStorageErrorId("LANCE", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
1991
1792
  domain: error.ErrorDomain.STORAGE,
1992
1793
  category: error.ErrorCategory.THIRD_PARTY,
1993
- details: { namespace: args?.namespace ?? "", workflowName: args?.workflowName ?? "" }
1794
+ details: { runId, workflowName }
1994
1795
  },
1995
1796
  error$1
1996
1797
  );
1997
1798
  }
1998
1799
  }
1999
- };
2000
-
2001
- // src/storage/index.ts
2002
- var LanceStorage = class _LanceStorage extends storage.MastraStorage {
2003
- stores;
2004
- lanceClient;
2005
- /**
2006
- * Creates a new instance of LanceStorage
2007
- * @param uri The URI to connect to LanceDB
2008
- * @param options connection options
2009
- *
2010
- * Usage:
2011
- *
2012
- * Connect to a local database
2013
- * ```ts
2014
- * const store = await LanceStorage.create('/path/to/db');
2015
- * ```
2016
- *
2017
- * Connect to a LanceDB cloud database
2018
- * ```ts
2019
- * const store = await LanceStorage.create('db://host:port');
2020
- * ```
2021
- *
2022
- * Connect to a cloud database
2023
- * ```ts
2024
- * const store = await LanceStorage.create('s3://bucket/db', { storageOptions: { timeout: '60s' } });
2025
- * ```
2026
- */
2027
- static async create(name, uri, options) {
2028
- const instance = new _LanceStorage(name);
1800
+ async listWorkflowRuns(args) {
2029
1801
  try {
2030
- instance.lanceClient = await lancedb.connect(uri, options);
2031
- const operations = new StoreOperationsLance({ client: instance.lanceClient });
2032
- instance.stores = {
2033
- operations: new StoreOperationsLance({ client: instance.lanceClient }),
2034
- workflows: new StoreWorkflowsLance({ client: instance.lanceClient }),
2035
- traces: new StoreTracesLance({ client: instance.lanceClient, operations }),
2036
- scores: new StoreScoresLance({ client: instance.lanceClient }),
2037
- memory: new StoreMemoryLance({ client: instance.lanceClient, operations }),
2038
- legacyEvals: new StoreLegacyEvalsLance({ client: instance.lanceClient })
2039
- };
2040
- return instance;
2041
- } catch (e) {
2042
- throw new error.MastraError(
2043
- {
2044
- id: "STORAGE_LANCE_STORAGE_CONNECT_FAILED",
2045
- domain: error.ErrorDomain.STORAGE,
2046
- category: error.ErrorCategory.THIRD_PARTY,
2047
- text: `Failed to connect to LanceDB: ${e.message || e}`,
2048
- details: { uri, optionsProvided: !!options }
2049
- },
2050
- e
2051
- );
2052
- }
2053
- }
2054
- /**
2055
- * @internal
2056
- * Private constructor to enforce using the create factory method
2057
- */
2058
- constructor(name) {
2059
- super({ name });
2060
- const operations = new StoreOperationsLance({ client: this.lanceClient });
2061
- this.stores = {
2062
- operations: new StoreOperationsLance({ client: this.lanceClient }),
2063
- workflows: new StoreWorkflowsLance({ client: this.lanceClient }),
2064
- traces: new StoreTracesLance({ client: this.lanceClient, operations }),
2065
- scores: new StoreScoresLance({ client: this.lanceClient }),
2066
- legacyEvals: new StoreLegacyEvalsLance({ client: this.lanceClient }),
2067
- memory: new StoreMemoryLance({ client: this.lanceClient, operations })
2068
- };
2069
- }
2070
- async createTable({
2071
- tableName,
2072
- schema
2073
- }) {
2074
- return this.stores.operations.createTable({ tableName, schema });
2075
- }
2076
- async dropTable({ tableName }) {
2077
- return this.stores.operations.dropTable({ tableName });
2078
- }
2079
- async alterTable({
2080
- tableName,
2081
- schema,
2082
- ifNotExists
2083
- }) {
2084
- return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
2085
- }
2086
- async clearTable({ tableName }) {
2087
- return this.stores.operations.clearTable({ tableName });
2088
- }
2089
- async insert({ tableName, record }) {
2090
- return this.stores.operations.insert({ tableName, record });
2091
- }
2092
- async batchInsert({ tableName, records }) {
2093
- return this.stores.operations.batchInsert({ tableName, records });
2094
- }
2095
- async load({ tableName, keys }) {
2096
- return this.stores.operations.load({ tableName, keys });
2097
- }
2098
- async getThreadById({ threadId }) {
2099
- return this.stores.memory.getThreadById({ threadId });
2100
- }
2101
- async getThreadsByResourceId({ resourceId }) {
2102
- return this.stores.memory.getThreadsByResourceId({ resourceId });
2103
- }
2104
- /**
2105
- * Saves a thread to the database. This function doesn't overwrite existing threads.
2106
- * @param thread - The thread to save
2107
- * @returns The saved thread
2108
- */
2109
- async saveThread({ thread }) {
2110
- return this.stores.memory.saveThread({ thread });
2111
- }
2112
- async updateThread({
2113
- id,
2114
- title,
2115
- metadata
2116
- }) {
2117
- return this.stores.memory.updateThread({ id, title, metadata });
2118
- }
2119
- async deleteThread({ threadId }) {
2120
- return this.stores.memory.deleteThread({ threadId });
2121
- }
2122
- get supports() {
2123
- return {
2124
- selectByIncludeResourceScope: true,
2125
- resourceWorkingMemory: true,
2126
- hasColumn: true,
2127
- createTable: true,
2128
- deleteMessages: false,
2129
- getScoresBySpan: true
2130
- };
2131
- }
2132
- async getResourceById({ resourceId }) {
2133
- return this.stores.memory.getResourceById({ resourceId });
2134
- }
2135
- async saveResource({ resource }) {
2136
- return this.stores.memory.saveResource({ resource });
2137
- }
2138
- async updateResource({
2139
- resourceId,
2140
- workingMemory,
2141
- metadata
2142
- }) {
2143
- return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
2144
- }
2145
- /**
2146
- * Processes messages to include context messages based on withPreviousMessages and withNextMessages
2147
- * @param records - The sorted array of records to process
2148
- * @param include - The array of include specifications with context parameters
2149
- * @returns The processed array with context messages included
2150
- */
2151
- processMessagesWithContext(records, include) {
2152
- const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
2153
- if (messagesWithContext.length === 0) {
2154
- return records;
2155
- }
2156
- const messageIndexMap = /* @__PURE__ */ new Map();
2157
- records.forEach((message, index) => {
2158
- messageIndexMap.set(message.id, index);
2159
- });
2160
- const additionalIndices = /* @__PURE__ */ new Set();
2161
- for (const item of messagesWithContext) {
2162
- const messageIndex = messageIndexMap.get(item.id);
2163
- if (messageIndex !== void 0) {
2164
- if (item.withPreviousMessages) {
2165
- const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
2166
- for (let i = startIdx; i < messageIndex; i++) {
2167
- additionalIndices.add(i);
2168
- }
2169
- }
2170
- if (item.withNextMessages) {
2171
- const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
2172
- for (let i = messageIndex + 1; i <= endIdx; i++) {
2173
- additionalIndices.add(i);
2174
- }
2175
- }
1802
+ const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1803
+ let query = table.query();
1804
+ const conditions = [];
1805
+ if (args?.workflowName) {
1806
+ conditions.push(`workflow_name = '${escapeSql(args.workflowName)}'`);
2176
1807
  }
2177
- }
2178
- if (additionalIndices.size === 0) {
2179
- return records;
2180
- }
2181
- const originalMatchIds = new Set(include.map((item) => item.id));
2182
- const allIndices = /* @__PURE__ */ new Set();
2183
- records.forEach((record, index) => {
2184
- if (originalMatchIds.has(record.id)) {
2185
- allIndices.add(index);
1808
+ if (args?.status) {
1809
+ const escapedStatus = args.status.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/%/g, "\\%").replace(/_/g, "\\_");
1810
+ conditions.push(`\`snapshot\` LIKE '%"status":"${escapedStatus}","value"%'`);
2186
1811
  }
2187
- });
2188
- additionalIndices.forEach((index) => {
2189
- allIndices.add(index);
2190
- });
2191
- return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
2192
- }
2193
- async getMessages({
2194
- threadId,
2195
- resourceId,
2196
- selectBy,
2197
- format,
2198
- threadConfig
2199
- }) {
2200
- return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format, threadConfig });
2201
- }
2202
- async getMessagesById({
2203
- messageIds,
2204
- format
2205
- }) {
2206
- return this.stores.memory.getMessagesById({ messageIds, format });
2207
- }
2208
- async saveMessages(args) {
2209
- return this.stores.memory.saveMessages(args);
2210
- }
2211
- async getThreadsByResourceIdPaginated(args) {
2212
- return this.stores.memory.getThreadsByResourceIdPaginated(args);
2213
- }
2214
- async getMessagesPaginated(args) {
2215
- return this.stores.memory.getMessagesPaginated(args);
2216
- }
2217
- async updateMessages(_args) {
2218
- return this.stores.memory.updateMessages(_args);
2219
- }
2220
- async getTraceById(args) {
2221
- return this.stores.traces.getTraceById(args);
2222
- }
2223
- async getTraces(args) {
2224
- return this.stores.traces.getTraces(args);
2225
- }
2226
- async getTracesPaginated(args) {
2227
- return this.stores.traces.getTracesPaginated(args);
2228
- }
2229
- async getEvalsByAgentName(agentName, type) {
2230
- return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
2231
- }
2232
- async getEvals(options) {
2233
- return this.stores.legacyEvals.getEvals(options);
2234
- }
2235
- async getWorkflowRuns(args) {
2236
- return this.stores.workflows.getWorkflowRuns(args);
2237
- }
2238
- async getWorkflowRunById(args) {
2239
- return this.stores.workflows.getWorkflowRunById(args);
2240
- }
2241
- async updateWorkflowResults({
2242
- workflowName,
2243
- runId,
2244
- stepId,
2245
- result,
2246
- runtimeContext
2247
- }) {
2248
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
2249
- }
2250
- async updateWorkflowState({
2251
- workflowName,
2252
- runId,
2253
- opts
2254
- }) {
2255
- return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
2256
- }
2257
- async persistWorkflowSnapshot({
2258
- workflowName,
2259
- runId,
2260
- resourceId,
2261
- snapshot
2262
- }) {
2263
- return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
2264
- }
2265
- async loadWorkflowSnapshot({
2266
- workflowName,
2267
- runId
2268
- }) {
2269
- return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
2270
- }
2271
- async getScoreById({ id: _id }) {
2272
- return this.stores.scores.getScoreById({ id: _id });
2273
- }
2274
- async getScoresByScorerId({
2275
- scorerId,
2276
- source,
2277
- entityId,
2278
- entityType,
2279
- pagination
2280
- }) {
2281
- return this.stores.scores.getScoresByScorerId({ scorerId, source, pagination, entityId, entityType });
2282
- }
2283
- async saveScore(_score) {
2284
- return this.stores.scores.saveScore(_score);
1812
+ if (args?.resourceId) {
1813
+ conditions.push(`\`resourceId\` = '${escapeSql(args.resourceId)}'`);
1814
+ }
1815
+ if (args?.fromDate instanceof Date) {
1816
+ conditions.push(`\`createdAt\` >= ${args.fromDate.getTime()}`);
1817
+ }
1818
+ if (args?.toDate instanceof Date) {
1819
+ conditions.push(`\`createdAt\` <= ${args.toDate.getTime()}`);
1820
+ }
1821
+ let total = 0;
1822
+ if (conditions.length > 0) {
1823
+ query = query.where(conditions.join(" AND "));
1824
+ total = await table.countRows(conditions.join(" AND "));
1825
+ } else {
1826
+ total = await table.countRows();
1827
+ }
1828
+ if (args?.perPage !== void 0 && args?.page !== void 0) {
1829
+ const normalizedPerPage = storage.normalizePerPage(args.perPage, Number.MAX_SAFE_INTEGER);
1830
+ if (args.page < 0 || !Number.isInteger(args.page)) {
1831
+ throw new error.MastraError(
1832
+ {
1833
+ id: storage.createStorageErrorId("LANCE", "LIST_WORKFLOW_RUNS", "INVALID_PAGINATION"),
1834
+ domain: error.ErrorDomain.STORAGE,
1835
+ category: error.ErrorCategory.USER,
1836
+ details: { page: args.page, perPage: args.perPage }
1837
+ },
1838
+ new Error(`Invalid pagination parameters: page=${args.page}, perPage=${args.perPage}`)
1839
+ );
1840
+ }
1841
+ const offset = args.page * normalizedPerPage;
1842
+ query.limit(normalizedPerPage);
1843
+ query.offset(offset);
1844
+ }
1845
+ const records = await query.toArray();
1846
+ return {
1847
+ runs: records.map((record) => this.parseWorkflowRun(record)),
1848
+ total: total || records.length
1849
+ };
1850
+ } catch (error$1) {
1851
+ throw new error.MastraError(
1852
+ {
1853
+ id: storage.createStorageErrorId("LANCE", "LIST_WORKFLOW_RUNS", "FAILED"),
1854
+ domain: error.ErrorDomain.STORAGE,
1855
+ category: error.ErrorCategory.THIRD_PARTY,
1856
+ details: { resourceId: args?.resourceId ?? "", workflowName: args?.workflowName ?? "" }
1857
+ },
1858
+ error$1
1859
+ );
1860
+ }
2285
1861
  }
2286
- async getScoresByRunId({
2287
- runId,
2288
- pagination
2289
- }) {
2290
- return this.stores.scores.getScoresByRunId({ runId, pagination });
1862
+ };
1863
+
1864
+ // src/storage/index.ts
1865
+ var LanceStorage = class _LanceStorage extends storage.MastraCompositeStore {
1866
+ stores;
1867
+ lanceClient;
1868
+ /**
1869
+ * Creates a new instance of LanceStorage
1870
+ * @param id The unique identifier for this storage instance
1871
+ * @param name The name for this storage instance
1872
+ * @param uri The URI to connect to LanceDB
1873
+ * @param connectionOptions connection options for LanceDB
1874
+ * @param storageOptions storage options including disableInit
1875
+ *
1876
+ * Usage:
1877
+ *
1878
+ * Connect to a local database
1879
+ * ```ts
1880
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', '/path/to/db');
1881
+ * ```
1882
+ *
1883
+ * Connect to a LanceDB cloud database
1884
+ * ```ts
1885
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', 'db://host:port');
1886
+ * ```
1887
+ *
1888
+ * Connect to a cloud database
1889
+ * ```ts
1890
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', 's3://bucket/db', { storageOptions: { timeout: '60s' } });
1891
+ * ```
1892
+ *
1893
+ * Disable auto-init for runtime (after CI/CD has run migrations)
1894
+ * ```ts
1895
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', '/path/to/db', undefined, { disableInit: true });
1896
+ * ```
1897
+ */
1898
+ static async create(id, name, uri, connectionOptions, storageOptions) {
1899
+ const instance = new _LanceStorage(id, name, storageOptions?.disableInit);
1900
+ try {
1901
+ instance.lanceClient = await lancedb.connect(uri, connectionOptions);
1902
+ instance.stores = {
1903
+ workflows: new StoreWorkflowsLance({ client: instance.lanceClient }),
1904
+ scores: new StoreScoresLance({ client: instance.lanceClient }),
1905
+ memory: new StoreMemoryLance({ client: instance.lanceClient })
1906
+ };
1907
+ return instance;
1908
+ } catch (e) {
1909
+ throw new error.MastraError(
1910
+ {
1911
+ id: storage.createStorageErrorId("LANCE", "CONNECT", "FAILED"),
1912
+ domain: error.ErrorDomain.STORAGE,
1913
+ category: error.ErrorCategory.THIRD_PARTY,
1914
+ text: `Failed to connect to LanceDB: ${e.message || e}`,
1915
+ details: { uri, optionsProvided: !!connectionOptions }
1916
+ },
1917
+ e
1918
+ );
1919
+ }
2291
1920
  }
2292
- async getScoresByEntityId({
2293
- entityId,
2294
- entityType,
2295
- pagination
2296
- }) {
2297
- return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
1921
+ /**
1922
+ * Creates a new instance of LanceStorage from a pre-configured LanceDB connection.
1923
+ * Use this when you need to configure the connection before initialization.
1924
+ *
1925
+ * @param id The unique identifier for this storage instance
1926
+ * @param name The name for this storage instance
1927
+ * @param client Pre-configured LanceDB connection
1928
+ * @param options Storage options including disableInit
1929
+ *
1930
+ * @example
1931
+ * ```typescript
1932
+ * import { connect } from '@lancedb/lancedb';
1933
+ *
1934
+ * const client = await connect('/path/to/db', {
1935
+ * // Custom connection options
1936
+ * });
1937
+ *
1938
+ * const store = LanceStorage.fromClient('my-id', 'MyStorage', client);
1939
+ * ```
1940
+ */
1941
+ static fromClient(id, name, client, options) {
1942
+ const instance = new _LanceStorage(id, name, options?.disableInit);
1943
+ instance.lanceClient = client;
1944
+ instance.stores = {
1945
+ workflows: new StoreWorkflowsLance({ client }),
1946
+ scores: new StoreScoresLance({ client }),
1947
+ memory: new StoreMemoryLance({ client })
1948
+ };
1949
+ return instance;
2298
1950
  }
2299
- async getScoresBySpan({
2300
- traceId,
2301
- spanId,
2302
- pagination
2303
- }) {
2304
- return this.stores.scores.getScoresBySpan({ traceId, spanId, pagination });
1951
+ /**
1952
+ * @internal
1953
+ * Private constructor to enforce using the create factory method.
1954
+ * Note: stores is initialized in create() after the lanceClient is connected.
1955
+ */
1956
+ constructor(id, name, disableInit) {
1957
+ super({ id, name, disableInit });
1958
+ this.stores = {};
2305
1959
  }
2306
1960
  };
2307
1961
  var LanceFilterTranslator = class extends filter.BaseFilterTranslator {
@@ -2650,14 +2304,14 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2650
2304
  * ```
2651
2305
  */
2652
2306
  static async create(uri, options) {
2653
- const instance = new _LanceVectorStore();
2307
+ const instance = new _LanceVectorStore(options?.id || crypto.randomUUID());
2654
2308
  try {
2655
2309
  instance.lanceClient = await lancedb.connect(uri, options);
2656
2310
  return instance;
2657
2311
  } catch (e) {
2658
2312
  throw new error.MastraError(
2659
2313
  {
2660
- id: "STORAGE_LANCE_VECTOR_CONNECT_FAILED",
2314
+ id: storage.createVectorErrorId("LANCE", "CONNECT", "FAILED"),
2661
2315
  domain: error.ErrorDomain.STORAGE,
2662
2316
  category: error.ErrorCategory.THIRD_PARTY,
2663
2317
  details: { uri }
@@ -2670,8 +2324,8 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2670
2324
  * @internal
2671
2325
  * Private constructor to enforce using the create factory method
2672
2326
  */
2673
- constructor() {
2674
- super();
2327
+ constructor(id) {
2328
+ super({ id });
2675
2329
  }
2676
2330
  close() {
2677
2331
  if (this.lanceClient) {
@@ -2680,6 +2334,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2680
2334
  }
2681
2335
  async query({
2682
2336
  tableName,
2337
+ indexName,
2683
2338
  queryVector,
2684
2339
  filter,
2685
2340
  includeVector = false,
@@ -2687,56 +2342,78 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2687
2342
  columns = [],
2688
2343
  includeAllColumns = false
2689
2344
  }) {
2345
+ const resolvedTableName = tableName ?? indexName;
2346
+ if (!queryVector) {
2347
+ throw new error.MastraError({
2348
+ id: storage.createVectorErrorId("LANCE", "QUERY", "MISSING_VECTOR"),
2349
+ text: "queryVector is required for Lance queries. Metadata-only queries are not supported by this vector store.",
2350
+ domain: error.ErrorDomain.STORAGE,
2351
+ category: error.ErrorCategory.USER,
2352
+ details: { indexName: resolvedTableName }
2353
+ });
2354
+ }
2690
2355
  try {
2691
2356
  if (!this.lanceClient) {
2692
2357
  throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
2693
2358
  }
2694
- if (!tableName) {
2695
- throw new Error("tableName is required");
2696
- }
2697
- if (!queryVector) {
2698
- throw new Error("queryVector is required");
2359
+ if (!resolvedTableName) {
2360
+ throw new Error("tableName or indexName is required");
2699
2361
  }
2700
2362
  } catch (error$1) {
2701
2363
  throw new error.MastraError(
2702
2364
  {
2703
- id: "STORAGE_LANCE_VECTOR_QUERY_FAILED_INVALID_ARGS",
2365
+ id: storage.createVectorErrorId("LANCE", "QUERY", "INVALID_ARGS"),
2704
2366
  domain: error.ErrorDomain.STORAGE,
2705
2367
  category: error.ErrorCategory.USER,
2706
- text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
2707
- details: { tableName }
2368
+ text: error$1 instanceof Error ? error$1.message : "Invalid query arguments",
2369
+ details: { tableName: resolvedTableName }
2708
2370
  },
2709
2371
  error$1
2710
2372
  );
2711
2373
  }
2712
2374
  try {
2713
- const table = await this.lanceClient.openTable(tableName);
2714
- const selectColumns = [...columns];
2715
- if (!selectColumns.includes("id")) {
2716
- selectColumns.push("id");
2375
+ const tables = await this.lanceClient.tableNames();
2376
+ if (!tables.includes(resolvedTableName)) {
2377
+ this.logger.debug(`Table ${resolvedTableName} does not exist. Returning empty results.`);
2378
+ return [];
2717
2379
  }
2380
+ const table = await this.lanceClient.openTable(resolvedTableName);
2718
2381
  let query = table.search(queryVector);
2719
2382
  if (filter && Object.keys(filter).length > 0) {
2720
2383
  const whereClause = this.filterTranslator(filter);
2721
2384
  this.logger.debug(`Where clause generated: ${whereClause}`);
2385
+ const schema = await table.schema();
2386
+ const schemaColumns = new Set(schema.fields.map((f) => f.name));
2387
+ const filterColumns = this.extractFilterColumns(whereClause);
2388
+ const missingColumns = filterColumns.filter((col) => !schemaColumns.has(col));
2389
+ if (missingColumns.length > 0) {
2390
+ this.logger.debug(
2391
+ `Filter references columns not in schema: ${missingColumns.join(", ")}. Returning empty results.`
2392
+ );
2393
+ return [];
2394
+ }
2722
2395
  query = query.where(whereClause);
2723
2396
  }
2724
- if (!includeAllColumns && selectColumns.length > 0) {
2397
+ if (!includeAllColumns && columns.length > 0) {
2398
+ const selectColumns = [...columns];
2399
+ if (!selectColumns.includes("id")) {
2400
+ selectColumns.push("id");
2401
+ }
2725
2402
  query = query.select(selectColumns);
2726
2403
  }
2727
2404
  query = query.limit(topK);
2728
2405
  const results = await query.toArray();
2729
2406
  return results.map((result) => {
2730
- const flatMetadata = {};
2731
- Object.keys(result).forEach((key) => {
2732
- if (key !== "id" && key !== "score" && key !== "vector" && key !== "_distance") {
2733
- if (key.startsWith("metadata_")) {
2734
- const metadataKey = key.substring("metadata_".length);
2735
- flatMetadata[metadataKey] = result[key];
2736
- }
2407
+ let metadata = {};
2408
+ if (result._metadata_json) {
2409
+ try {
2410
+ metadata = JSON.parse(result._metadata_json);
2411
+ } catch {
2412
+ metadata = this.extractFlatMetadata(result);
2737
2413
  }
2738
- });
2739
- const metadata = this.unflattenObject(flatMetadata);
2414
+ } else {
2415
+ metadata = this.extractFlatMetadata(result);
2416
+ }
2740
2417
  return {
2741
2418
  id: String(result.id || ""),
2742
2419
  metadata,
@@ -2748,10 +2425,10 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2748
2425
  } catch (error$1) {
2749
2426
  throw new error.MastraError(
2750
2427
  {
2751
- id: "STORAGE_LANCE_VECTOR_QUERY_FAILED",
2428
+ id: storage.createVectorErrorId("LANCE", "QUERY", "FAILED"),
2752
2429
  domain: error.ErrorDomain.STORAGE,
2753
2430
  category: error.ErrorCategory.THIRD_PARTY,
2754
- details: { tableName, includeVector, columnsCount: columns?.length, includeAllColumns }
2431
+ details: { tableName: resolvedTableName, includeVector, columnsCount: columns?.length, includeAllColumns }
2755
2432
  },
2756
2433
  error$1
2757
2434
  );
@@ -2786,13 +2463,14 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2786
2463
  const translator = new LanceFilterTranslator();
2787
2464
  return translator.translate(prefixedFilter);
2788
2465
  }
2789
- async upsert({ tableName, vectors, metadata = [], ids = [] }) {
2466
+ async upsert({ tableName, indexName, vectors, metadata = [], ids = [] }) {
2467
+ const resolvedTableName = tableName ?? indexName;
2790
2468
  try {
2791
2469
  if (!this.lanceClient) {
2792
2470
  throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
2793
2471
  }
2794
- if (!tableName) {
2795
- throw new Error("tableName is required");
2472
+ if (!resolvedTableName) {
2473
+ throw new Error("tableName or indexName is required");
2796
2474
  }
2797
2475
  if (!vectors || !Array.isArray(vectors) || vectors.length === 0) {
2798
2476
  throw new Error("vectors array is required and must not be empty");
@@ -2800,21 +2478,24 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2800
2478
  } catch (error$1) {
2801
2479
  throw new error.MastraError(
2802
2480
  {
2803
- id: "STORAGE_LANCE_VECTOR_UPSERT_FAILED_INVALID_ARGS",
2481
+ id: storage.createVectorErrorId("LANCE", "UPSERT", "INVALID_ARGS"),
2804
2482
  domain: error.ErrorDomain.STORAGE,
2805
2483
  category: error.ErrorCategory.USER,
2806
- text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
2807
- details: { tableName }
2484
+ text: error$1 instanceof Error ? error$1.message : "Invalid upsert arguments",
2485
+ details: { tableName: resolvedTableName }
2808
2486
  },
2809
2487
  error$1
2810
2488
  );
2811
2489
  }
2812
2490
  try {
2813
2491
  const tables = await this.lanceClient.tableNames();
2814
- if (!tables.includes(tableName)) {
2815
- throw new Error(`Table ${tableName} does not exist`);
2492
+ const tableExists = tables.includes(resolvedTableName);
2493
+ let table = null;
2494
+ if (!tableExists) {
2495
+ this.logger.debug(`Table ${resolvedTableName} does not exist. Creating it with the first upsert data.`);
2496
+ } else {
2497
+ table = await this.lanceClient.openTable(resolvedTableName);
2816
2498
  }
2817
- const table = await this.lanceClient.openTable(tableName);
2818
2499
  const vectorIds = ids.length === vectors.length ? ids : vectors.map((_, i) => ids[i] || crypto.randomUUID());
2819
2500
  const data = vectors.map((vector, i) => {
2820
2501
  const id = String(vectorIds[i]);
@@ -2828,18 +2509,61 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2828
2509
  Object.entries(flattenedMetadata).forEach(([key, value]) => {
2829
2510
  rowData[key] = value;
2830
2511
  });
2512
+ rowData["_metadata_json"] = JSON.stringify(metadataItem);
2513
+ } else {
2514
+ rowData["_metadata_json"] = "";
2831
2515
  }
2832
2516
  return rowData;
2833
2517
  });
2834
- await table.add(data, { mode: "overwrite" });
2518
+ if (table !== null) {
2519
+ const rowCount = await table.countRows();
2520
+ const schema = await table.schema();
2521
+ const existingColumns = new Set(schema.fields.map((f) => f.name));
2522
+ const dataColumns = new Set(Object.keys(data[0] || {}));
2523
+ const extraColumns = [...dataColumns].filter((col) => !existingColumns.has(col));
2524
+ const missingSchemaColumns = [...existingColumns].filter((col) => !dataColumns.has(col));
2525
+ const hasSchemaMismatch = extraColumns.length > 0 || missingSchemaColumns.length > 0;
2526
+ if (rowCount === 0 && extraColumns.length > 0) {
2527
+ this.logger.warn(
2528
+ `Table ${resolvedTableName} is empty and data has extra columns ${extraColumns.join(", ")}. Recreating with new schema.`
2529
+ );
2530
+ await this.lanceClient.dropTable(resolvedTableName);
2531
+ await this.lanceClient.createTable(resolvedTableName, data);
2532
+ } else if (hasSchemaMismatch) {
2533
+ if (extraColumns.length > 0) {
2534
+ this.logger.warn(
2535
+ `Table ${resolvedTableName} has ${rowCount} rows. Columns ${extraColumns.join(", ")} will be dropped from upsert.`
2536
+ );
2537
+ }
2538
+ const schemaFieldNames = schema.fields.map((f) => f.name);
2539
+ const normalizedData = data.map((row) => {
2540
+ const normalized = {};
2541
+ for (const col of schemaFieldNames) {
2542
+ normalized[col] = col in row ? row[col] : null;
2543
+ }
2544
+ return normalized;
2545
+ });
2546
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(normalizedData);
2547
+ } else {
2548
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(data);
2549
+ }
2550
+ } else {
2551
+ this.logger.debug(`Creating table ${resolvedTableName} with initial data`);
2552
+ await this.lanceClient.createTable(resolvedTableName, data);
2553
+ }
2835
2554
  return vectorIds;
2836
2555
  } catch (error$1) {
2837
2556
  throw new error.MastraError(
2838
2557
  {
2839
- id: "STORAGE_LANCE_VECTOR_UPSERT_FAILED",
2558
+ id: storage.createVectorErrorId("LANCE", "UPSERT", "FAILED"),
2840
2559
  domain: error.ErrorDomain.STORAGE,
2841
2560
  category: error.ErrorCategory.THIRD_PARTY,
2842
- details: { tableName, vectorCount: vectors.length, metadataCount: metadata.length, idsCount: ids.length }
2561
+ details: {
2562
+ tableName: resolvedTableName,
2563
+ vectorCount: vectors.length,
2564
+ metadataCount: metadata.length,
2565
+ idsCount: ids.length
2566
+ }
2843
2567
  },
2844
2568
  error$1
2845
2569
  );
@@ -2863,7 +2587,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2863
2587
  async createTable(tableName, data, options) {
2864
2588
  if (!this.lanceClient) {
2865
2589
  throw new error.MastraError({
2866
- id: "STORAGE_LANCE_VECTOR_CREATE_TABLE_FAILED_INVALID_ARGS",
2590
+ id: storage.createVectorErrorId("LANCE", "CREATE_TABLE", "INVALID_ARGS"),
2867
2591
  domain: error.ErrorDomain.STORAGE,
2868
2592
  category: error.ErrorCategory.USER,
2869
2593
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2878,7 +2602,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2878
2602
  } catch (error$1) {
2879
2603
  throw new error.MastraError(
2880
2604
  {
2881
- id: "STORAGE_LANCE_VECTOR_CREATE_TABLE_FAILED",
2605
+ id: storage.createVectorErrorId("LANCE", "CREATE_TABLE", "FAILED"),
2882
2606
  domain: error.ErrorDomain.STORAGE,
2883
2607
  category: error.ErrorCategory.THIRD_PARTY,
2884
2608
  details: { tableName }
@@ -2890,7 +2614,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2890
2614
  async listTables() {
2891
2615
  if (!this.lanceClient) {
2892
2616
  throw new error.MastraError({
2893
- id: "STORAGE_LANCE_VECTOR_LIST_TABLES_FAILED_INVALID_ARGS",
2617
+ id: storage.createVectorErrorId("LANCE", "LIST_TABLES", "INVALID_ARGS"),
2894
2618
  domain: error.ErrorDomain.STORAGE,
2895
2619
  category: error.ErrorCategory.USER,
2896
2620
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2902,7 +2626,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2902
2626
  } catch (error$1) {
2903
2627
  throw new error.MastraError(
2904
2628
  {
2905
- id: "STORAGE_LANCE_VECTOR_LIST_TABLES_FAILED",
2629
+ id: storage.createVectorErrorId("LANCE", "LIST_TABLES", "FAILED"),
2906
2630
  domain: error.ErrorDomain.STORAGE,
2907
2631
  category: error.ErrorCategory.THIRD_PARTY
2908
2632
  },
@@ -2913,7 +2637,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2913
2637
  async getTableSchema(tableName) {
2914
2638
  if (!this.lanceClient) {
2915
2639
  throw new error.MastraError({
2916
- id: "STORAGE_LANCE_VECTOR_GET_TABLE_SCHEMA_FAILED_INVALID_ARGS",
2640
+ id: storage.createVectorErrorId("LANCE", "GET_TABLE_SCHEMA", "INVALID_ARGS"),
2917
2641
  domain: error.ErrorDomain.STORAGE,
2918
2642
  category: error.ErrorCategory.USER,
2919
2643
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2926,7 +2650,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2926
2650
  } catch (error$1) {
2927
2651
  throw new error.MastraError(
2928
2652
  {
2929
- id: "STORAGE_LANCE_VECTOR_GET_TABLE_SCHEMA_FAILED",
2653
+ id: storage.createVectorErrorId("LANCE", "GET_TABLE_SCHEMA", "FAILED"),
2930
2654
  domain: error.ErrorDomain.STORAGE,
2931
2655
  category: error.ErrorCategory.THIRD_PARTY,
2932
2656
  details: { tableName }
@@ -2936,7 +2660,17 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2936
2660
  }
2937
2661
  }
2938
2662
  /**
2939
- * indexName is actually a column name in a table in lanceDB
2663
+ * Creates a vector index on a table.
2664
+ *
2665
+ * The behavior of `indexName` depends on whether `tableName` is provided:
2666
+ * - With `tableName`: `indexName` is the column to index (advanced use case)
2667
+ * - Without `tableName`: `indexName` becomes the table name, and 'vector' is used as the column (Memory compatibility)
2668
+ *
2669
+ * @param tableName - Optional table name. If not provided, defaults to indexName.
2670
+ * @param indexName - The index/column name, or table name if tableName is not provided.
2671
+ * @param dimension - Vector dimension size.
2672
+ * @param metric - Distance metric: 'cosine', 'euclidean', or 'dotproduct'.
2673
+ * @param indexConfig - Optional index configuration.
2940
2674
  */
2941
2675
  async createIndex({
2942
2676
  tableName,
@@ -2945,13 +2679,12 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2945
2679
  metric = "cosine",
2946
2680
  indexConfig = {}
2947
2681
  }) {
2682
+ const resolvedTableName = tableName ?? indexName;
2683
+ const columnToIndex = tableName ? indexName : "vector";
2948
2684
  try {
2949
2685
  if (!this.lanceClient) {
2950
2686
  throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
2951
2687
  }
2952
- if (!tableName) {
2953
- throw new Error("tableName is required");
2954
- }
2955
2688
  if (!indexName) {
2956
2689
  throw new Error("indexName is required");
2957
2690
  }
@@ -2961,22 +2694,38 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2961
2694
  } catch (err) {
2962
2695
  throw new error.MastraError(
2963
2696
  {
2964
- id: "STORAGE_LANCE_VECTOR_CREATE_INDEX_FAILED_INVALID_ARGS",
2697
+ id: storage.createVectorErrorId("LANCE", "CREATE_INDEX", "INVALID_ARGS"),
2965
2698
  domain: error.ErrorDomain.STORAGE,
2966
2699
  category: error.ErrorCategory.USER,
2967
- details: { tableName: tableName || "", indexName, dimension, metric }
2700
+ details: { tableName: resolvedTableName, indexName, dimension, metric }
2968
2701
  },
2969
2702
  err
2970
2703
  );
2971
2704
  }
2972
2705
  try {
2973
2706
  const tables = await this.lanceClient.tableNames();
2974
- if (!tables.includes(tableName)) {
2975
- throw new Error(
2976
- `Table ${tableName} does not exist. Please create the table first by calling createTable() method.`
2707
+ let table;
2708
+ if (!tables.includes(resolvedTableName)) {
2709
+ this.logger.debug(
2710
+ `Table ${resolvedTableName} does not exist. Creating empty table with dimension ${dimension}.`
2977
2711
  );
2712
+ const initVector = new Array(dimension).fill(0);
2713
+ table = await this.lanceClient.createTable(resolvedTableName, [
2714
+ { id: "__init__", vector: initVector, _metadata_json: "" }
2715
+ ]);
2716
+ try {
2717
+ await table.delete("id = '__init__'");
2718
+ } catch (deleteError) {
2719
+ this.logger.warn(
2720
+ `Failed to delete initialization row from ${resolvedTableName}. Subsequent queries may include '__init__' row.`,
2721
+ deleteError
2722
+ );
2723
+ }
2724
+ this.logger.debug(`Table ${resolvedTableName} created. Index creation deferred until data is available.`);
2725
+ return;
2726
+ } else {
2727
+ table = await this.lanceClient.openTable(resolvedTableName);
2978
2728
  }
2979
- const table = await this.lanceClient.openTable(tableName);
2980
2729
  let metricType;
2981
2730
  if (metric === "euclidean") {
2982
2731
  metricType = "l2";
@@ -2985,8 +2734,15 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2985
2734
  } else if (metric === "cosine") {
2986
2735
  metricType = "cosine";
2987
2736
  }
2737
+ const rowCount = await table.countRows();
2738
+ if (rowCount < 256) {
2739
+ this.logger.warn(
2740
+ `Table ${resolvedTableName} has ${rowCount} rows, which is below the 256 row minimum for index creation. Skipping index creation.`
2741
+ );
2742
+ return;
2743
+ }
2988
2744
  if (indexConfig.type === "ivfflat") {
2989
- await table.createIndex(indexName, {
2745
+ await table.createIndex(columnToIndex, {
2990
2746
  config: lancedb.Index.ivfPq({
2991
2747
  numPartitions: indexConfig.numPartitions || 128,
2992
2748
  numSubVectors: indexConfig.numSubVectors || 16,
@@ -2995,7 +2751,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2995
2751
  });
2996
2752
  } else {
2997
2753
  this.logger.debug("Creating HNSW PQ index with config:", indexConfig);
2998
- await table.createIndex(indexName, {
2754
+ await table.createIndex(columnToIndex, {
2999
2755
  config: lancedb.Index.hnswPq({
3000
2756
  m: indexConfig?.hnsw?.m || 16,
3001
2757
  efConstruction: indexConfig?.hnsw?.efConstruction || 100,
@@ -3006,10 +2762,10 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3006
2762
  } catch (error$1) {
3007
2763
  throw new error.MastraError(
3008
2764
  {
3009
- id: "STORAGE_LANCE_VECTOR_CREATE_INDEX_FAILED",
2765
+ id: storage.createVectorErrorId("LANCE", "CREATE_INDEX", "FAILED"),
3010
2766
  domain: error.ErrorDomain.STORAGE,
3011
2767
  category: error.ErrorCategory.THIRD_PARTY,
3012
- details: { tableName: tableName || "", indexName, dimension }
2768
+ details: { tableName: resolvedTableName, indexName, dimension }
3013
2769
  },
3014
2770
  error$1
3015
2771
  );
@@ -3018,7 +2774,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3018
2774
  async listIndexes() {
3019
2775
  if (!this.lanceClient) {
3020
2776
  throw new error.MastraError({
3021
- id: "STORAGE_LANCE_VECTOR_LIST_INDEXES_FAILED_INVALID_ARGS",
2777
+ id: storage.createVectorErrorId("LANCE", "LIST_INDEXES", "INVALID_ARGS"),
3022
2778
  domain: error.ErrorDomain.STORAGE,
3023
2779
  category: error.ErrorCategory.USER,
3024
2780
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -3037,7 +2793,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3037
2793
  } catch (error$1) {
3038
2794
  throw new error.MastraError(
3039
2795
  {
3040
- id: "STORAGE_LANCE_VECTOR_LIST_INDEXES_FAILED",
2796
+ id: storage.createVectorErrorId("LANCE", "LIST_INDEXES", "FAILED"),
3041
2797
  domain: error.ErrorDomain.STORAGE,
3042
2798
  category: error.ErrorCategory.THIRD_PARTY
3043
2799
  },
@@ -3056,7 +2812,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3056
2812
  } catch (err) {
3057
2813
  throw new error.MastraError(
3058
2814
  {
3059
- id: "STORAGE_LANCE_VECTOR_DESCRIBE_INDEX_FAILED_INVALID_ARGS",
2815
+ id: storage.createVectorErrorId("LANCE", "DESCRIBE_INDEX", "INVALID_ARGS"),
3060
2816
  domain: error.ErrorDomain.STORAGE,
3061
2817
  category: error.ErrorCategory.USER,
3062
2818
  details: { indexName }
@@ -3091,7 +2847,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3091
2847
  } catch (error$1) {
3092
2848
  throw new error.MastraError(
3093
2849
  {
3094
- id: "STORAGE_LANCE_VECTOR_DESCRIBE_INDEX_FAILED",
2850
+ id: storage.createVectorErrorId("LANCE", "DESCRIBE_INDEX", "FAILED"),
3095
2851
  domain: error.ErrorDomain.STORAGE,
3096
2852
  category: error.ErrorCategory.THIRD_PARTY,
3097
2853
  details: { indexName }
@@ -3111,7 +2867,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3111
2867
  } catch (err) {
3112
2868
  throw new error.MastraError(
3113
2869
  {
3114
- id: "STORAGE_LANCE_VECTOR_DELETE_INDEX_FAILED_INVALID_ARGS",
2870
+ id: storage.createVectorErrorId("LANCE", "DELETE_INDEX", "INVALID_ARGS"),
3115
2871
  domain: error.ErrorDomain.STORAGE,
3116
2872
  category: error.ErrorCategory.USER,
3117
2873
  details: { indexName }
@@ -3134,7 +2890,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3134
2890
  } catch (error$1) {
3135
2891
  throw new error.MastraError(
3136
2892
  {
3137
- id: "STORAGE_LANCE_VECTOR_DELETE_INDEX_FAILED",
2893
+ id: storage.createVectorErrorId("LANCE", "DELETE_INDEX", "FAILED"),
3138
2894
  domain: error.ErrorDomain.STORAGE,
3139
2895
  category: error.ErrorCategory.THIRD_PARTY,
3140
2896
  details: { indexName }
@@ -3149,7 +2905,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3149
2905
  async deleteAllTables() {
3150
2906
  if (!this.lanceClient) {
3151
2907
  throw new error.MastraError({
3152
- id: "STORAGE_LANCE_VECTOR_DELETE_ALL_TABLES_FAILED_INVALID_ARGS",
2908
+ id: storage.createVectorErrorId("LANCE", "DELETE_ALL_TABLES", "INVALID_ARGS"),
3153
2909
  domain: error.ErrorDomain.STORAGE,
3154
2910
  category: error.ErrorCategory.USER,
3155
2911
  details: { methodName: "deleteAllTables" },
@@ -3161,7 +2917,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3161
2917
  } catch (error$1) {
3162
2918
  throw new error.MastraError(
3163
2919
  {
3164
- id: "STORAGE_LANCE_VECTOR_DELETE_ALL_TABLES_FAILED",
2920
+ id: storage.createVectorErrorId("LANCE", "DELETE_ALL_TABLES", "FAILED"),
3165
2921
  domain: error.ErrorDomain.STORAGE,
3166
2922
  category: error.ErrorCategory.THIRD_PARTY,
3167
2923
  details: { methodName: "deleteAllTables" }
@@ -3173,7 +2929,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3173
2929
  async deleteTable(tableName) {
3174
2930
  if (!this.lanceClient) {
3175
2931
  throw new error.MastraError({
3176
- id: "STORAGE_LANCE_VECTOR_DELETE_TABLE_FAILED_INVALID_ARGS",
2932
+ id: storage.createVectorErrorId("LANCE", "DELETE_TABLE", "INVALID_ARGS"),
3177
2933
  domain: error.ErrorDomain.STORAGE,
3178
2934
  category: error.ErrorCategory.USER,
3179
2935
  details: { tableName },
@@ -3185,7 +2941,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3185
2941
  } catch (error$1) {
3186
2942
  throw new error.MastraError(
3187
2943
  {
3188
- id: "STORAGE_LANCE_VECTOR_DELETE_TABLE_FAILED",
2944
+ id: storage.createVectorErrorId("LANCE", "DELETE_TABLE", "FAILED"),
3189
2945
  domain: error.ErrorDomain.STORAGE,
3190
2946
  category: error.ErrorCategory.THIRD_PARTY,
3191
2947
  details: { tableName }
@@ -3194,7 +2950,44 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3194
2950
  );
3195
2951
  }
3196
2952
  }
3197
- async updateVector({ indexName, id, update }) {
2953
+ async updateVector(params) {
2954
+ const { indexName, update } = params;
2955
+ if ("id" in params && "filter" in params && params.id && params.filter) {
2956
+ throw new error.MastraError({
2957
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "MUTUALLY_EXCLUSIVE"),
2958
+ domain: error.ErrorDomain.STORAGE,
2959
+ category: error.ErrorCategory.USER,
2960
+ text: "id and filter are mutually exclusive",
2961
+ details: { indexName }
2962
+ });
2963
+ }
2964
+ if (!("id" in params || "filter" in params) || !params.id && !params.filter) {
2965
+ throw new error.MastraError({
2966
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "NO_TARGET"),
2967
+ domain: error.ErrorDomain.STORAGE,
2968
+ category: error.ErrorCategory.USER,
2969
+ text: "Either id or filter must be provided",
2970
+ details: { indexName }
2971
+ });
2972
+ }
2973
+ if ("filter" in params && params.filter && Object.keys(params.filter).length === 0) {
2974
+ throw new error.MastraError({
2975
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "EMPTY_FILTER"),
2976
+ domain: error.ErrorDomain.STORAGE,
2977
+ category: error.ErrorCategory.USER,
2978
+ text: "Cannot update with empty filter",
2979
+ details: { indexName }
2980
+ });
2981
+ }
2982
+ if (!update.vector && !update.metadata) {
2983
+ throw new error.MastraError({
2984
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "NO_PAYLOAD"),
2985
+ domain: error.ErrorDomain.STORAGE,
2986
+ category: error.ErrorCategory.USER,
2987
+ text: "No updates provided",
2988
+ details: { indexName }
2989
+ });
2990
+ }
3198
2991
  try {
3199
2992
  if (!this.lanceClient) {
3200
2993
  throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
@@ -3202,21 +2995,6 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3202
2995
  if (!indexName) {
3203
2996
  throw new Error("indexName is required");
3204
2997
  }
3205
- if (!id) {
3206
- throw new Error("id is required");
3207
- }
3208
- } catch (err) {
3209
- throw new error.MastraError(
3210
- {
3211
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED_INVALID_ARGS",
3212
- domain: error.ErrorDomain.STORAGE,
3213
- category: error.ErrorCategory.USER,
3214
- details: { indexName, id }
3215
- },
3216
- err
3217
- );
3218
- }
3219
- try {
3220
2998
  const tables = await this.lanceClient.tableNames();
3221
2999
  for (const tableName of tables) {
3222
3000
  this.logger.debug("Checking table:" + tableName);
@@ -3226,39 +3004,77 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3226
3004
  const hasColumn = schema.fields.some((field) => field.name === indexName);
3227
3005
  if (hasColumn) {
3228
3006
  this.logger.debug(`Found column ${indexName} in table ${tableName}`);
3229
- const existingRecord = await table.query().where(`id = '${id}'`).select(schema.fields.map((field) => field.name)).limit(1).toArray();
3230
- if (existingRecord.length === 0) {
3231
- throw new Error(`Record with id '${id}' not found in table ${tableName}`);
3007
+ let whereClause;
3008
+ if ("id" in params && params.id) {
3009
+ whereClause = `id = '${params.id}'`;
3010
+ } else if ("filter" in params && params.filter) {
3011
+ const translator = new LanceFilterTranslator();
3012
+ const processFilterKeys = (filter) => {
3013
+ const processedFilter = {};
3014
+ Object.entries(filter).forEach(([key, value]) => {
3015
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3016
+ Object.entries(value).forEach(([nestedKey, nestedValue]) => {
3017
+ processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
3018
+ });
3019
+ } else {
3020
+ processedFilter[`metadata_${key}`] = value;
3021
+ }
3022
+ });
3023
+ return processedFilter;
3024
+ };
3025
+ const prefixedFilter = processFilterKeys(params.filter);
3026
+ whereClause = translator.translate(prefixedFilter) || "";
3027
+ if (!whereClause) {
3028
+ throw new Error("Failed to translate filter to SQL");
3029
+ }
3030
+ } else {
3031
+ throw new Error("Either id or filter must be provided");
3232
3032
  }
3233
- const rowData = {
3234
- id
3235
- };
3236
- Object.entries(existingRecord[0]).forEach(([key, value]) => {
3237
- if (key !== "id" && key !== "_distance") {
3238
- if (key === indexName) {
3239
- if (!update.vector) {
3240
- if (Array.isArray(value)) {
3241
- rowData[key] = [...value];
3242
- } else if (typeof value === "object" && value !== null) {
3243
- rowData[key] = Array.from(value);
3033
+ const existingRecords = await table.query().where(whereClause).select(schema.fields.map((field) => field.name)).toArray();
3034
+ if (existingRecords.length === 0) {
3035
+ this.logger.info(`No records found matching criteria in table ${tableName}`);
3036
+ return;
3037
+ }
3038
+ const updatedRecords = existingRecords.map((record) => {
3039
+ const rowData = {};
3040
+ Object.entries(record).forEach(([key, value]) => {
3041
+ if (key !== "_distance") {
3042
+ if (key === indexName) {
3043
+ if (update.vector) {
3044
+ rowData[key] = update.vector;
3244
3045
  } else {
3245
- rowData[key] = value;
3046
+ if (Array.isArray(value)) {
3047
+ rowData[key] = [...value];
3048
+ } else if (typeof value === "object" && value !== null) {
3049
+ rowData[key] = Array.from(value);
3050
+ } else {
3051
+ rowData[key] = value;
3052
+ }
3053
+ }
3054
+ } else {
3055
+ rowData[key] = value;
3056
+ }
3057
+ }
3058
+ });
3059
+ if (update.metadata) {
3060
+ Object.entries(update.metadata).forEach(([key, value]) => {
3061
+ rowData[`metadata_${key}`] = value;
3062
+ });
3063
+ const hasMetadataJson = schema.fields.some((f) => f.name === "_metadata_json");
3064
+ if (hasMetadataJson) {
3065
+ let existingMetadata = {};
3066
+ if (record._metadata_json) {
3067
+ try {
3068
+ existingMetadata = JSON.parse(record._metadata_json);
3069
+ } catch {
3246
3070
  }
3247
3071
  }
3248
- } else {
3249
- rowData[key] = value;
3072
+ rowData["_metadata_json"] = JSON.stringify({ ...existingMetadata, ...update.metadata });
3250
3073
  }
3251
3074
  }
3075
+ return rowData;
3252
3076
  });
3253
- if (update.vector) {
3254
- rowData[indexName] = update.vector;
3255
- }
3256
- if (update.metadata) {
3257
- Object.entries(update.metadata).forEach(([key, value]) => {
3258
- rowData[`metadata_${key}`] = value;
3259
- });
3260
- }
3261
- await table.add([rowData], { mode: "overwrite" });
3077
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(updatedRecords);
3262
3078
  return;
3263
3079
  }
3264
3080
  } catch (err) {
@@ -3268,12 +3084,19 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3268
3084
  }
3269
3085
  throw new Error(`No table found with column/index '${indexName}'`);
3270
3086
  } catch (error$1) {
3087
+ if (error$1 instanceof error.MastraError) throw error$1;
3271
3088
  throw new error.MastraError(
3272
3089
  {
3273
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED",
3090
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "FAILED"),
3274
3091
  domain: error.ErrorDomain.STORAGE,
3275
3092
  category: error.ErrorCategory.THIRD_PARTY,
3276
- details: { indexName, id, hasVector: !!update.vector, hasMetadata: !!update.metadata }
3093
+ details: {
3094
+ indexName,
3095
+ ..."id" in params && params.id && { id: params.id },
3096
+ ..."filter" in params && params.filter && { filter: JSON.stringify(params.filter) },
3097
+ hasVector: !!update.vector,
3098
+ hasMetadata: !!update.metadata
3099
+ }
3277
3100
  },
3278
3101
  error$1
3279
3102
  );
@@ -3293,10 +3116,13 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3293
3116
  } catch (err) {
3294
3117
  throw new error.MastraError(
3295
3118
  {
3296
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED_INVALID_ARGS",
3119
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTOR", "INVALID_ARGS"),
3297
3120
  domain: error.ErrorDomain.STORAGE,
3298
3121
  category: error.ErrorCategory.USER,
3299
- details: { indexName, id }
3122
+ details: {
3123
+ indexName,
3124
+ ...id && { id }
3125
+ }
3300
3126
  },
3301
3127
  err
3302
3128
  );
@@ -3323,43 +3149,150 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3323
3149
  } catch (error$1) {
3324
3150
  throw new error.MastraError(
3325
3151
  {
3326
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED",
3152
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTOR", "FAILED"),
3327
3153
  domain: error.ErrorDomain.STORAGE,
3328
3154
  category: error.ErrorCategory.THIRD_PARTY,
3329
- details: { indexName, id }
3155
+ details: {
3156
+ indexName,
3157
+ ...id && { id }
3158
+ }
3330
3159
  },
3331
3160
  error$1
3332
3161
  );
3333
3162
  }
3334
3163
  }
3335
3164
  /**
3336
- * Converts a flattened object with keys using underscore notation back to a nested object.
3337
- * Example: { name: 'test', details_text: 'test' } → { name: 'test', details: { text: 'test' } }
3165
+ * Extracts column names referenced in a SQL WHERE clause.
3166
+ * Identifies metadata_* prefixed identifiers used in filter conditions.
3338
3167
  */
3339
- unflattenObject(obj) {
3340
- const result = {};
3341
- Object.keys(obj).forEach((key) => {
3342
- const value = obj[key];
3343
- const parts = key.split("_");
3344
- let current = result;
3345
- for (let i = 0; i < parts.length - 1; i++) {
3346
- const part = parts[i];
3347
- if (!part) continue;
3348
- if (!current[part] || typeof current[part] !== "object") {
3349
- current[part] = {};
3168
+ extractFilterColumns(whereClause) {
3169
+ const matches = whereClause.match(/metadata_\w+/g);
3170
+ return matches ? [...new Set(matches)] : [];
3171
+ }
3172
+ /**
3173
+ * Extracts metadata from flattened column names (legacy data without _metadata_json).
3174
+ * Returns keys as-is after stripping the 'metadata_' prefix, without any unflattening.
3175
+ */
3176
+ extractFlatMetadata(result) {
3177
+ const metadata = {};
3178
+ Object.keys(result).forEach((key) => {
3179
+ if (key !== "id" && key !== "score" && key !== "vector" && key !== "_distance" && key !== "_metadata_json") {
3180
+ if (key.startsWith("metadata_")) {
3181
+ metadata[key.substring("metadata_".length)] = result[key];
3350
3182
  }
3351
- current = current[part];
3352
- }
3353
- const lastPart = parts[parts.length - 1];
3354
- if (lastPart) {
3355
- current[lastPart] = value;
3356
3183
  }
3357
3184
  });
3358
- return result;
3185
+ return metadata;
3186
+ }
3187
+ async deleteVectors({ indexName, filter, ids }) {
3188
+ if (ids && filter) {
3189
+ throw new error.MastraError({
3190
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "MUTUALLY_EXCLUSIVE"),
3191
+ domain: error.ErrorDomain.STORAGE,
3192
+ category: error.ErrorCategory.USER,
3193
+ text: "ids and filter are mutually exclusive",
3194
+ details: { indexName }
3195
+ });
3196
+ }
3197
+ if (!ids && !filter) {
3198
+ throw new error.MastraError({
3199
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "NO_TARGET"),
3200
+ domain: error.ErrorDomain.STORAGE,
3201
+ category: error.ErrorCategory.USER,
3202
+ text: "Either filter or ids must be provided",
3203
+ details: { indexName }
3204
+ });
3205
+ }
3206
+ if (ids && ids.length === 0) {
3207
+ throw new error.MastraError({
3208
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "EMPTY_IDS"),
3209
+ domain: error.ErrorDomain.STORAGE,
3210
+ category: error.ErrorCategory.USER,
3211
+ text: "Cannot delete with empty ids array",
3212
+ details: { indexName }
3213
+ });
3214
+ }
3215
+ if (filter && Object.keys(filter).length === 0) {
3216
+ throw new error.MastraError({
3217
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "EMPTY_FILTER"),
3218
+ domain: error.ErrorDomain.STORAGE,
3219
+ category: error.ErrorCategory.USER,
3220
+ text: "Cannot delete with empty filter",
3221
+ details: { indexName }
3222
+ });
3223
+ }
3224
+ try {
3225
+ if (!this.lanceClient) {
3226
+ throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
3227
+ }
3228
+ if (!indexName) {
3229
+ throw new Error("indexName is required");
3230
+ }
3231
+ const tables = await this.lanceClient.tableNames();
3232
+ for (const tableName of tables) {
3233
+ this.logger.debug("Checking table:" + tableName);
3234
+ const table = await this.lanceClient.openTable(tableName);
3235
+ try {
3236
+ const schema = await table.schema();
3237
+ const hasColumn = schema.fields.some((field) => field.name === indexName);
3238
+ if (hasColumn) {
3239
+ this.logger.debug(`Found column ${indexName} in table ${tableName}`);
3240
+ if (ids) {
3241
+ const idsConditions = ids.map((id) => `id = '${id}'`).join(" OR ");
3242
+ await table.delete(idsConditions);
3243
+ } else if (filter) {
3244
+ const translator = new LanceFilterTranslator();
3245
+ const processFilterKeys = (filter2) => {
3246
+ const processedFilter = {};
3247
+ Object.entries(filter2).forEach(([key, value]) => {
3248
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3249
+ Object.entries(value).forEach(([nestedKey, nestedValue]) => {
3250
+ processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
3251
+ });
3252
+ } else {
3253
+ processedFilter[`metadata_${key}`] = value;
3254
+ }
3255
+ });
3256
+ return processedFilter;
3257
+ };
3258
+ const prefixedFilter = processFilterKeys(filter);
3259
+ const whereClause = translator.translate(prefixedFilter);
3260
+ if (!whereClause) {
3261
+ throw new Error("Failed to translate filter to SQL");
3262
+ }
3263
+ await table.delete(whereClause);
3264
+ }
3265
+ return;
3266
+ }
3267
+ } catch (err) {
3268
+ this.logger.error(`Error checking schema for table ${tableName}:` + err);
3269
+ continue;
3270
+ }
3271
+ }
3272
+ throw new Error(`No table found with column/index '${indexName}'`);
3273
+ } catch (error$1) {
3274
+ if (error$1 instanceof error.MastraError) throw error$1;
3275
+ throw new error.MastraError(
3276
+ {
3277
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "FAILED"),
3278
+ domain: error.ErrorDomain.STORAGE,
3279
+ category: error.ErrorCategory.THIRD_PARTY,
3280
+ details: {
3281
+ indexName,
3282
+ ...filter && { filter: JSON.stringify(filter) },
3283
+ ...ids && { idsCount: ids.length }
3284
+ }
3285
+ },
3286
+ error$1
3287
+ );
3288
+ }
3359
3289
  }
3360
3290
  };
3361
3291
 
3362
3292
  exports.LanceStorage = LanceStorage;
3363
3293
  exports.LanceVectorStore = LanceVectorStore;
3294
+ exports.StoreMemoryLance = StoreMemoryLance;
3295
+ exports.StoreScoresLance = StoreScoresLance;
3296
+ exports.StoreWorkflowsLance = StoreWorkflowsLance;
3364
3297
  //# sourceMappingURL=index.cjs.map
3365
3298
  //# sourceMappingURL=index.cjs.map