@mastra/lance 0.0.0-iterate-traces-ui-again-20250912091900 → 0.0.0-jail-fs-20260105160110

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 +1285 -3
  2. package/README.md +61 -4
  3. package/dist/docs/README.md +33 -0
  4. package/dist/docs/SKILL.md +34 -0
  5. package/dist/docs/SOURCE_MAP.json +6 -0
  6. package/dist/docs/rag/01-vector-databases.md +638 -0
  7. package/dist/docs/storage/01-reference.md +113 -0
  8. package/dist/docs/vectors/01-reference.md +149 -0
  9. package/dist/index.cjs +1466 -1586
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.js +1465 -1588
  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 +27 -22
  19. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  20. package/dist/storage/domains/workflows/index.d.ts +20 -24
  21. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  22. package/dist/storage/index.d.ts +101 -234
  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 +6 -3
  26. package/dist/vector/index.d.ts.map +1 -1
  27. package/package.json +16 -11
  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,131 +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');
9
+ var evals = require('@mastra/core/evals');
8
10
  var vector = require('@mastra/core/vector');
9
11
  var filter = require('@mastra/core/vector/filter');
10
12
 
11
13
  // src/storage/index.ts
12
- var StoreLegacyEvalsLance = class extends storage.LegacyEvalsStorage {
13
- client;
14
- constructor({ client }) {
15
- super();
16
- this.client = client;
17
- }
18
- async getEvalsByAgentName(agentName, type) {
19
- try {
20
- const table = await this.client.openTable(storage.TABLE_EVALS);
21
- const query = table.query().where(`agent_name = '${agentName}'`);
22
- const records = await query.toArray();
23
- let filteredRecords = records;
24
- if (type === "live") {
25
- filteredRecords = records.filter((record) => record.test_info === null);
26
- } else if (type === "test") {
27
- filteredRecords = records.filter((record) => record.test_info !== null);
28
- }
29
- return filteredRecords.map((record) => {
30
- return {
31
- id: record.id,
32
- input: record.input,
33
- output: record.output,
34
- agentName: record.agent_name,
35
- metricName: record.metric_name,
36
- result: JSON.parse(record.result),
37
- instructions: record.instructions,
38
- testInfo: record.test_info ? JSON.parse(record.test_info) : null,
39
- globalRunId: record.global_run_id,
40
- runId: record.run_id,
41
- createdAt: new Date(record.created_at).toString()
42
- };
43
- });
44
- } catch (error$1) {
45
- throw new error.MastraError(
46
- {
47
- id: "LANCE_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
48
- domain: error.ErrorDomain.STORAGE,
49
- category: error.ErrorCategory.THIRD_PARTY,
50
- details: { agentName }
51
- },
52
- error$1
53
- );
54
- }
55
- }
56
- async getEvals(options) {
57
- try {
58
- const table = await this.client.openTable(storage.TABLE_EVALS);
59
- const conditions = [];
60
- if (options.agentName) {
61
- conditions.push(`agent_name = '${options.agentName}'`);
62
- }
63
- if (options.type === "live") {
64
- conditions.push("length(test_info) = 0");
65
- } else if (options.type === "test") {
66
- conditions.push("length(test_info) > 0");
67
- }
68
- const startDate = options.dateRange?.start || options.fromDate;
69
- const endDate = options.dateRange?.end || options.toDate;
70
- if (startDate) {
71
- conditions.push(`\`created_at\` >= ${startDate.getTime()}`);
72
- }
73
- if (endDate) {
74
- conditions.push(`\`created_at\` <= ${endDate.getTime()}`);
75
- }
76
- let total = 0;
77
- if (conditions.length > 0) {
78
- total = await table.countRows(conditions.join(" AND "));
79
- } else {
80
- total = await table.countRows();
81
- }
82
- const query = table.query();
83
- if (conditions.length > 0) {
84
- const whereClause = conditions.join(" AND ");
85
- query.where(whereClause);
86
- }
87
- const records = await query.toArray();
88
- const evals = records.sort((a, b) => b.created_at - a.created_at).map((record) => {
89
- return {
90
- id: record.id,
91
- input: record.input,
92
- output: record.output,
93
- agentName: record.agent_name,
94
- metricName: record.metric_name,
95
- result: JSON.parse(record.result),
96
- instructions: record.instructions,
97
- testInfo: record.test_info ? JSON.parse(record.test_info) : null,
98
- globalRunId: record.global_run_id,
99
- runId: record.run_id,
100
- createdAt: new Date(record.created_at).toISOString()
101
- };
102
- });
103
- const page = options.page || 0;
104
- const perPage = options.perPage || 10;
105
- const pagedEvals = evals.slice(page * perPage, (page + 1) * perPage);
106
- return {
107
- evals: pagedEvals,
108
- total,
109
- page,
110
- perPage,
111
- hasMore: total > (page + 1) * perPage
112
- };
113
- } catch (error$1) {
114
- throw new error.MastraError(
115
- {
116
- id: "LANCE_STORE_GET_EVALS_FAILED",
117
- domain: error.ErrorDomain.STORAGE,
118
- category: error.ErrorCategory.THIRD_PARTY,
119
- details: { agentName: options.agentName ?? "" }
120
- },
121
- error$1
122
- );
123
- }
124
- }
125
- };
126
14
  function getPrimaryKeys(tableName) {
127
15
  let primaryId = ["id"];
128
16
  if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
129
17
  primaryId = ["workflow_name", "run_id"];
130
- } else if (tableName === storage.TABLE_EVALS) {
131
- primaryId = ["agent_name", "metric_name", "run_id"];
132
18
  }
133
19
  return primaryId;
134
20
  }
@@ -189,7 +75,6 @@ function processResultWithTypeConversion(rawResult, tableSchema) {
189
75
  } else if (fieldTypeStr.includes("float64") && ["createdAt", "updatedAt"].includes(key)) {
190
76
  processedResult[key] = new Date(processedResult[key]);
191
77
  }
192
- console.log(key, "processedResult", processedResult);
193
78
  }
194
79
  return processedResult;
195
80
  }
@@ -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,665 +125,503 @@ 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 })
296
+ } catch (validationError) {
297
+ throw new error.MastraError(
298
+ {
299
+ id: storage.createStorageErrorId("LANCE", "ALTER_TABLE", "INVALID_ARGS"),
300
+ domain: error.ErrorDomain.STORAGE,
301
+ category: error.ErrorCategory.USER,
302
+ text: validationError.message,
303
+ details: { tableName }
304
+ },
305
+ validationError
438
306
  );
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();
307
+ }
308
+ try {
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
+ }
442
331
  } catch (error$1) {
443
332
  throw new error.MastraError(
444
333
  {
445
- id: "LANCE_STORE_GET_MESSAGES_FAILED",
334
+ id: storage.createStorageErrorId("LANCE", "ALTER_TABLE", "FAILED"),
446
335
  domain: error.ErrorDomain.STORAGE,
447
336
  category: error.ErrorCategory.THIRD_PARTY,
448
- details: {
449
- threadId,
450
- resourceId: resourceId ?? ""
451
- }
337
+ details: { tableName }
452
338
  },
453
339
  error$1
454
340
  );
455
341
  }
456
342
  }
457
- async getMessagesById({
458
- messageIds,
459
- format
460
- }) {
461
- if (messageIds.length === 0) return [];
343
+ async clearTable({ tableName }) {
462
344
  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 })
345
+ if (!this.client) {
346
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
347
+ }
348
+ if (!tableName) {
349
+ throw new Error("tableName is required for clearTable.");
350
+ }
351
+ } catch (validationError) {
352
+ throw new error.MastraError(
353
+ {
354
+ id: storage.createStorageErrorId("LANCE", "CLEAR_TABLE", "INVALID_ARGS"),
355
+ domain: error.ErrorDomain.STORAGE,
356
+ category: error.ErrorCategory.USER,
357
+ text: validationError.message,
358
+ details: { tableName }
359
+ },
360
+ validationError
469
361
  );
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();
362
+ }
363
+ try {
364
+ const table = await this.client.openTable(tableName);
365
+ await table.delete("1=1");
473
366
  } catch (error$1) {
474
367
  throw new error.MastraError(
475
368
  {
476
- id: "LANCE_STORE_GET_MESSAGES_BY_ID_FAILED",
369
+ id: storage.createStorageErrorId("LANCE", "CLEAR_TABLE", "FAILED"),
477
370
  domain: error.ErrorDomain.STORAGE,
478
371
  category: error.ErrorCategory.THIRD_PARTY,
479
- details: {
480
- messageIds: JSON.stringify(messageIds)
481
- }
372
+ details: { tableName }
482
373
  },
483
374
  error$1
484
375
  );
485
376
  }
486
377
  }
487
- async saveMessages(args) {
378
+ async insert({ tableName, record }) {
488
379
  try {
489
- const { messages, format = "v1" } = args;
490
- if (messages.length === 0) {
491
- return [];
380
+ if (!this.client) {
381
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
492
382
  }
493
- const threadId = messages[0]?.threadId;
494
- if (!threadId) {
495
- throw new Error("Thread ID is required");
383
+ if (!tableName) {
384
+ throw new Error("tableName is required for insert.");
496
385
  }
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
- }
386
+ if (!record || Object.keys(record).length === 0) {
387
+ throw new Error("record is required and cannot be empty for insert.");
510
388
  }
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) {
389
+ } catch (validationError) {
530
390
  throw new error.MastraError(
531
391
  {
532
- id: "LANCE_STORE_SAVE_MESSAGES_FAILED",
392
+ id: storage.createStorageErrorId("LANCE", "INSERT", "INVALID_ARGS"),
533
393
  domain: error.ErrorDomain.STORAGE,
534
- category: error.ErrorCategory.THIRD_PARTY
394
+ category: error.ErrorCategory.USER,
395
+ text: validationError.message,
396
+ details: { tableName }
535
397
  },
536
- error$1
398
+ validationError
537
399
  );
538
400
  }
539
- }
540
- async getThreadsByResourceIdPaginated(args) {
541
401
  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);
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]);
409
+ }
550
410
  }
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
- };
411
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
562
412
  } catch (error$1) {
563
413
  throw new error.MastraError(
564
414
  {
565
- id: "LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
415
+ id: storage.createStorageErrorId("LANCE", "INSERT", "FAILED"),
566
416
  domain: error.ErrorDomain.STORAGE,
567
- category: error.ErrorCategory.THIRD_PARTY
417
+ category: error.ErrorCategory.THIRD_PARTY,
418
+ details: { tableName }
568
419
  },
569
420
  error$1
570
421
  );
571
422
  }
572
423
  }
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;
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
+ );
583
446
  }
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);
447
+ try {
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]);
602
456
  }
603
457
  }
604
- }
605
- }
606
- if (additionalIndices.size === 0) {
607
- return records;
458
+ return processedRecord;
459
+ });
460
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
461
+ } catch (error$1) {
462
+ throw new error.MastraError(
463
+ {
464
+ id: storage.createStorageErrorId("LANCE", "BATCH_INSERT", "FAILED"),
465
+ domain: error.ErrorDomain.STORAGE,
466
+ category: error.ErrorCategory.THIRD_PARTY,
467
+ details: { tableName }
468
+ },
469
+ error$1
470
+ );
608
471
  }
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
472
  }
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;
473
+ async load({ tableName, keys }) {
625
474
  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);
475
+ if (!this.client) {
476
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
645
477
  }
646
- const conditions = [`thread_id = '${threadId}'`];
647
- if (resourceId) {
648
- conditions.push(`\`resourceId\` = '${resourceId}'`);
649
- }
650
- if (fromDate) {
651
- conditions.push(`\`createdAt\` >= ${fromDate.getTime()}`);
478
+ if (!tableName) {
479
+ throw new Error("tableName is required for load.");
652
480
  }
653
- if (toDate) {
654
- conditions.push(`\`createdAt\` <= ${toDate.getTime()}`);
481
+ if (!keys || Object.keys(keys).length === 0) {
482
+ throw new Error("keys are required and cannot be empty for load.");
655
483
  }
656
- let total = 0;
657
- if (conditions.length > 0) {
658
- total = await table.countRows(conditions.join(" AND "));
659
- } else {
660
- total = await table.countRows();
484
+ } catch (validationError) {
485
+ throw new error.MastraError(
486
+ {
487
+ id: storage.createStorageErrorId("LANCE", "LOAD", "INVALID_ARGS"),
488
+ domain: error.ErrorDomain.STORAGE,
489
+ category: error.ErrorCategory.USER,
490
+ text: validationError.message,
491
+ details: { tableName }
492
+ },
493
+ validationError
494
+ );
495
+ }
496
+ try {
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);
661
515
  }
662
- if (total === 0 && messages.length === 0) {
663
- return {
664
- messages: [],
665
- total: 0,
666
- page,
667
- perPage,
668
- hasMore: false
669
- };
516
+ const result = await query.limit(1).toArray();
517
+ if (result.length === 0) {
518
+ this.logger.debug("No record found");
519
+ return null;
670
520
  }
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));
693
- }
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
- };
521
+ return processResultWithTypeConversion(result[0], tableSchema);
727
522
  } catch (error$1) {
728
- const mastraError = new error.MastraError(
523
+ if (error$1 instanceof error.MastraError) throw error$1;
524
+ throw new error.MastraError(
729
525
  {
730
- id: "LANCE_STORE_GET_MESSAGES_PAGINATED_FAILED",
526
+ id: storage.createStorageErrorId("LANCE", "LOAD", "FAILED"),
731
527
  domain: error.ErrorDomain.STORAGE,
732
528
  category: error.ErrorCategory.THIRD_PARTY,
733
- details: {
734
- threadId,
735
- resourceId: resourceId ?? ""
736
- }
529
+ details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? "" }
737
530
  },
738
531
  error$1
739
532
  );
740
- this.logger?.trackException?.(mastraError);
741
- this.logger?.error?.(mastraError.toString());
742
- return { messages: [], total: 0, page, perPage, hasMore: false };
743
533
  }
744
534
  }
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
- };
535
+ };
536
+
537
+ // src/storage/domains/memory/index.ts
538
+ var StoreMemoryLance = class extends storage.MemoryStorage {
539
+ client;
540
+ #db;
541
+ constructor(config) {
542
+ super();
543
+ const client = resolveLanceConfig(config);
544
+ this.client = client;
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"]
555
+ });
763
556
  }
764
- async updateMessages(args) {
765
- const { messages } = args;
766
- this.logger.debug("Updating messages", { count: messages.length });
767
- if (!messages.length) {
768
- return [];
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;
769
565
  }
770
- const updatedMessages = [];
771
- const affectedThreadIds = /* @__PURE__ */ new Set();
566
+ this.logger.debug("Deleting messages", { count: messageIds.length });
772
567
  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;
805
- }
806
- updatePayload.content = JSON.stringify(newContent);
807
- }
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));
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);
812
573
  }
813
574
  }
814
- for (const threadId of affectedThreadIds) {
815
- await this.operations.insert({
816
- tableName: storage.TABLE_THREADS,
817
- record: { id: threadId, updatedAt: Date.now() }
818
- });
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
+ }
819
593
  }
820
- return updatedMessages;
821
594
  } catch (error$1) {
822
595
  throw new error.MastraError(
823
596
  {
824
- id: "LANCE_STORE_UPDATE_MESSAGES_FAILED",
597
+ id: storage.createStorageErrorId("LANCE", "DELETE_MESSAGES", "FAILED"),
825
598
  domain: error.ErrorDomain.STORAGE,
826
599
  category: error.ErrorCategory.THIRD_PARTY,
827
- details: { count: messages.length }
600
+ details: { count: messageIds.length }
828
601
  },
829
602
  error$1
830
603
  );
831
604
  }
832
605
  }
833
- async getResourceById({ resourceId }) {
606
+ // Utility to escape single quotes in SQL strings
607
+ escapeSql(str) {
608
+ return str.replace(/'/g, "''");
609
+ }
610
+ async getThreadById({ threadId }) {
834
611
  try {
835
- const resource = await this.operations.load({ tableName: storage.TABLE_RESOURCES, keys: { id: resourceId } });
836
- if (!resource) {
612
+ const thread = await this.#db.load({ tableName: storage.TABLE_THREADS, keys: { id: threadId } });
613
+ if (!thread) {
837
614
  return null;
838
615
  }
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();
872
- }
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);
880
- }
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
- }
890
- }
891
616
  return {
892
- ...resource,
893
- createdAt,
894
- updatedAt,
895
- workingMemory,
896
- metadata
617
+ ...thread,
618
+ createdAt: new Date(thread.createdAt),
619
+ updatedAt: new Date(thread.updatedAt)
897
620
  };
898
621
  } catch (error$1) {
899
622
  throw new error.MastraError(
900
623
  {
901
- id: "LANCE_STORE_GET_RESOURCE_BY_ID_FAILED",
624
+ id: storage.createStorageErrorId("LANCE", "GET_THREAD_BY_ID", "FAILED"),
902
625
  domain: error.ErrorDomain.STORAGE,
903
626
  category: error.ErrorCategory.THIRD_PARTY
904
627
  },
@@ -906,23 +629,21 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
906
629
  );
907
630
  }
908
631
  }
909
- async saveResource({ resource }) {
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 }) {
910
638
  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);
639
+ const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
640
+ const table = await this.client.openTable(storage.TABLE_THREADS);
920
641
  await table.add([record], { mode: "append" });
921
- return resource;
642
+ return thread;
922
643
  } catch (error$1) {
923
644
  throw new error.MastraError(
924
645
  {
925
- id: "LANCE_STORE_SAVE_RESOURCE_FAILED",
646
+ id: storage.createStorageErrorId("LANCE", "SAVE_THREAD", "FAILED"),
926
647
  domain: error.ErrorDomain.STORAGE,
927
648
  category: error.ErrorCategory.THIRD_PARTY
928
649
  },
@@ -930,44 +651,32 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
930
651
  );
931
652
  }
932
653
  }
933
- async updateResource({
934
- resourceId,
935
- workingMemory,
654
+ async updateThread({
655
+ id,
656
+ title,
936
657
  metadata
937
658
  }) {
938
- const maxRetries = 3;
659
+ const maxRetries = 5;
939
660
  for (let attempt = 0; attempt < maxRetries; attempt++) {
940
661
  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 });
662
+ const current = await this.getThreadById({ threadId: id });
663
+ if (!current) {
664
+ throw new Error(`Thread with id ${id} not found`);
951
665
  }
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
- };
666
+ const mergedMetadata = { ...current.metadata, ...metadata };
961
667
  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)
668
+ id,
669
+ title,
670
+ metadata: JSON.stringify(mergedMetadata),
671
+ updatedAt: (/* @__PURE__ */ new Date()).getTime()
967
672
  };
968
- const table = await this.client.openTable(storage.TABLE_RESOURCES);
673
+ const table = await this.client.openTable(storage.TABLE_THREADS);
969
674
  await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
970
- return updatedResource;
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;
971
680
  } catch (error$1) {
972
681
  if (error$1.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
973
682
  const delay = Math.pow(2, attempt) * 10;
@@ -976,7 +685,7 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
976
685
  }
977
686
  throw new error.MastraError(
978
687
  {
979
- id: "LANCE_STORE_UPDATE_RESOURCE_FAILED",
688
+ id: storage.createStorageErrorId("LANCE", "UPDATE_THREAD", "FAILED"),
980
689
  domain: error.ErrorDomain.STORAGE,
981
690
  category: error.ErrorCategory.THIRD_PARTY
982
691
  },
@@ -984,445 +693,681 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
984
693
  );
985
694
  }
986
695
  }
987
- throw new Error("Unexpected end of retry loop");
988
- }
989
- };
990
- var StoreOperationsLance = class extends storage.StoreOperations {
991
- client;
992
- constructor({ client }) {
993
- super();
994
- 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);
1050
- });
1051
- return new apacheArrow.Schema(fields);
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
+ );
1052
704
  }
1053
- async createTable({
1054
- tableName,
1055
- schema
1056
- }) {
705
+ async deleteThread({ threadId }) {
1057
706
  try {
1058
- if (!this.client) {
1059
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1060
- }
1061
- if (!tableName) {
1062
- throw new Error("tableName is required for createTable.");
1063
- }
1064
- if (!schema) {
1065
- throw new Error("schema is required for createTable.");
1066
- }
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}'`);
1067
711
  } catch (error$1) {
1068
712
  throw new error.MastraError(
1069
713
  {
1070
- id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_INVALID_ARGS",
714
+ id: storage.createStorageErrorId("LANCE", "DELETE_THREAD", "FAILED"),
1071
715
  domain: error.ErrorDomain.STORAGE,
1072
- category: error.ErrorCategory.USER,
1073
- details: { tableName }
716
+ category: error.ErrorCategory.THIRD_PARTY
1074
717
  },
1075
718
  error$1
1076
719
  );
1077
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: [] };
1078
738
  try {
1079
- const arrowSchema = this.translateSchema(schema);
1080
- await this.client.createEmptyTable(tableName, arrowSchema);
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() };
1081
751
  } 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
752
  throw new error.MastraError(
1087
753
  {
1088
- id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_FAILED",
754
+ id: storage.createStorageErrorId("LANCE", "LIST_MESSAGES_BY_ID", "FAILED"),
1089
755
  domain: error.ErrorDomain.STORAGE,
1090
756
  category: error.ErrorCategory.THIRD_PARTY,
1091
- details: { tableName }
757
+ details: {
758
+ messageIds: JSON.stringify(messageIds)
759
+ }
1092
760
  },
1093
761
  error$1
1094
762
  );
1095
763
  }
1096
764
  }
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.");
1104
- }
1105
- } 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())) {
1106
769
  throw new error.MastraError(
1107
770
  {
1108
- id: "STORAGE_LANCE_STORAGE_DROP_TABLE_INVALID_ARGS",
771
+ id: storage.createStorageErrorId("LANCE", "LIST_MESSAGES", "INVALID_THREAD_ID"),
1109
772
  domain: error.ErrorDomain.STORAGE,
1110
- category: error.ErrorCategory.USER,
1111
- text: validationError.message,
1112
- details: { tableName }
773
+ category: error.ErrorCategory.THIRD_PARTY,
774
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
1113
775
  },
1114
- validationError
776
+ new Error("threadId must be a non-empty string or array of non-empty strings")
1115
777
  );
1116
778
  }
779
+ const perPage = storage.normalizePerPage(perPageInput, 40);
780
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1117
781
  try {
1118
- await this.client.dropTable(tableName);
1119
- } 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;
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
+ );
1123
792
  }
1124
- throw new error.MastraError(
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)) {
828
+ return {
829
+ messages: [],
830
+ total: 0,
831
+ page,
832
+ perPage: perPageForResponse,
833
+ hasMore: false
834
+ };
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
+ };
879
+ } catch (error$1) {
880
+ const mastraError = new error.MastraError(
1125
881
  {
1126
- id: "STORAGE_LANCE_STORAGE_DROP_TABLE_FAILED",
882
+ id: storage.createStorageErrorId("LANCE", "LIST_MESSAGES", "FAILED"),
1127
883
  domain: error.ErrorDomain.STORAGE,
1128
884
  category: error.ErrorCategory.THIRD_PARTY,
1129
- details: { tableName }
885
+ details: {
886
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
887
+ resourceId: resourceId ?? ""
888
+ }
1130
889
  },
1131
890
  error$1
1132
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
+ };
1133
901
  }
1134
902
  }
1135
- async alterTable({
1136
- tableName,
1137
- schema,
1138
- ifNotExists
1139
- }) {
903
+ async saveMessages(args) {
1140
904
  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.");
905
+ const { messages } = args;
906
+ if (messages.length === 0) {
907
+ return { messages: [] };
1146
908
  }
1147
- if (!schema) {
1148
- throw new Error("schema is required for alterTable.");
909
+ const threadId = messages[0]?.threadId;
910
+ if (!threadId) {
911
+ throw new Error("Thread ID is required");
1149
912
  }
1150
- if (!ifNotExists || ifNotExists.length === 0) {
1151
- this.logger.debug("No columns specified to add in alterTable, skipping.");
1152
- return;
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
+ }
1153
926
  }
1154
- } catch (validationError) {
1155
- throw new error.MastraError(
1156
- {
1157
- id: "STORAGE_LANCE_STORAGE_ALTER_TABLE_INVALID_ARGS",
1158
- domain: error.ErrorDomain.STORAGE,
1159
- category: error.ErrorCategory.USER,
1160
- text: validationError.message,
1161
- details: { tableName }
1162
- },
1163
- validationError
1164
- );
1165
- }
1166
- 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];
927
+ const transformedMessages = messages.map((message) => {
928
+ const { threadId: threadId2, type, ...rest } = message;
1180
929
  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"]})`
930
+ ...rest,
931
+ thread_id: threadId2,
932
+ type: type ?? "v2",
933
+ content: JSON.stringify(message.content)
1183
934
  };
1184
935
  });
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
- }
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() };
1189
944
  } catch (error$1) {
1190
945
  throw new error.MastraError(
1191
946
  {
1192
- id: "STORAGE_LANCE_STORAGE_ALTER_TABLE_FAILED",
947
+ id: storage.createStorageErrorId("LANCE", "SAVE_MESSAGES", "FAILED"),
1193
948
  domain: error.ErrorDomain.STORAGE,
1194
- category: error.ErrorCategory.THIRD_PARTY,
1195
- details: { tableName }
949
+ category: error.ErrorCategory.THIRD_PARTY
1196
950
  },
1197
951
  error$1
1198
952
  );
1199
953
  }
1200
954
  }
1201
- async clearTable({ tableName }) {
955
+ async listThreadsByResourceId(args) {
1202
956
  try {
1203
- if (!this.client) {
1204
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1205
- }
1206
- if (!tableName) {
1207
- throw new Error("tableName is required for clearTable.");
957
+ const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
958
+ const perPage = storage.normalizePerPage(perPageInput, 100);
959
+ if (page < 0) {
960
+ throw new error.MastraError(
961
+ {
962
+ id: storage.createStorageErrorId("LANCE", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
963
+ domain: error.ErrorDomain.STORAGE,
964
+ category: error.ErrorCategory.USER,
965
+ details: { page }
966
+ },
967
+ new Error("page must be >= 0")
968
+ );
1208
969
  }
1209
- } catch (validationError) {
1210
- throw new error.MastraError(
1211
- {
1212
- id: "STORAGE_LANCE_STORAGE_CLEAR_TABLE_INVALID_ARGS",
1213
- domain: error.ErrorDomain.STORAGE,
1214
- category: error.ErrorCategory.USER,
1215
- text: validationError.message,
1216
- details: { tableName }
1217
- },
1218
- validationError
970
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
971
+ const { field, direction } = this.parseOrderBy(orderBy);
972
+ const table = await this.client.openTable(storage.TABLE_THREADS);
973
+ const total = await table.countRows(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
974
+ const query = table.query().where(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
975
+ const records = await query.toArray();
976
+ records.sort((a, b) => {
977
+ const aValue = ["createdAt", "updatedAt"].includes(field) ? new Date(a[field]).getTime() : a[field];
978
+ const bValue = ["createdAt", "updatedAt"].includes(field) ? new Date(b[field]).getTime() : b[field];
979
+ if (aValue == null && bValue == null) return 0;
980
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
981
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
982
+ if (typeof aValue === "string" && typeof bValue === "string") {
983
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
984
+ }
985
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
986
+ });
987
+ const paginatedRecords = records.slice(offset, offset + perPage);
988
+ const schema = await getTableSchema({ tableName: storage.TABLE_THREADS, client: this.client });
989
+ const threads = paginatedRecords.map(
990
+ (record) => processResultWithTypeConversion(record, schema)
1219
991
  );
1220
- }
1221
- try {
1222
- const table = await this.client.openTable(tableName);
1223
- await table.delete("1=1");
992
+ return {
993
+ threads,
994
+ total,
995
+ page,
996
+ perPage: perPageForResponse,
997
+ hasMore: offset + perPage < total
998
+ };
1224
999
  } catch (error$1) {
1225
1000
  throw new error.MastraError(
1226
1001
  {
1227
- id: "STORAGE_LANCE_STORAGE_CLEAR_TABLE_FAILED",
1002
+ id: storage.createStorageErrorId("LANCE", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
1228
1003
  domain: error.ErrorDomain.STORAGE,
1229
- category: error.ErrorCategory.THIRD_PARTY,
1230
- details: { tableName }
1004
+ category: error.ErrorCategory.THIRD_PARTY
1231
1005
  },
1232
1006
  error$1
1233
1007
  );
1234
1008
  }
1235
1009
  }
1236
- async insert({ tableName, record }) {
1237
- 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.");
1010
+ /**
1011
+ * Processes messages to include context messages based on withPreviousMessages and withNextMessages
1012
+ * @param records - The sorted array of records to process
1013
+ * @param include - The array of include specifications with context parameters
1014
+ * @returns The processed array with context messages included
1015
+ */
1016
+ processMessagesWithContext(records, include) {
1017
+ const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
1018
+ if (messagesWithContext.length === 0) {
1019
+ return records;
1020
+ }
1021
+ const messageIndexMap = /* @__PURE__ */ new Map();
1022
+ records.forEach((message, index) => {
1023
+ messageIndexMap.set(message.id, index);
1024
+ });
1025
+ const additionalIndices = /* @__PURE__ */ new Set();
1026
+ for (const item of messagesWithContext) {
1027
+ const messageIndex = messageIndexMap.get(item.id);
1028
+ if (messageIndex !== void 0) {
1029
+ if (item.withPreviousMessages) {
1030
+ const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
1031
+ for (let i = startIdx; i < messageIndex; i++) {
1032
+ additionalIndices.add(i);
1033
+ }
1034
+ }
1035
+ if (item.withNextMessages) {
1036
+ const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
1037
+ for (let i = messageIndex + 1; i <= endIdx; i++) {
1038
+ additionalIndices.add(i);
1039
+ }
1040
+ }
1243
1041
  }
1244
- if (!record || Object.keys(record).length === 0) {
1245
- throw new Error("record is required and cannot be empty for insert.");
1042
+ }
1043
+ if (additionalIndices.size === 0) {
1044
+ return records;
1045
+ }
1046
+ const originalMatchIds = new Set(include.map((item) => item.id));
1047
+ const allIndices = /* @__PURE__ */ new Set();
1048
+ records.forEach((record, index) => {
1049
+ if (originalMatchIds.has(record.id)) {
1050
+ allIndices.add(index);
1246
1051
  }
1247
- } catch (validationError) {
1248
- throw new error.MastraError(
1249
- {
1250
- id: "STORAGE_LANCE_STORAGE_INSERT_INVALID_ARGS",
1251
- domain: error.ErrorDomain.STORAGE,
1252
- category: error.ErrorCategory.USER,
1253
- text: validationError.message,
1254
- details: { tableName }
1255
- },
1256
- validationError
1257
- );
1052
+ });
1053
+ additionalIndices.forEach((index) => {
1054
+ allIndices.add(index);
1055
+ });
1056
+ return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
1057
+ }
1058
+ /**
1059
+ * Parse message data from LanceDB record format to MastraDBMessage format
1060
+ */
1061
+ parseMessageData(data) {
1062
+ const { thread_id, ...rest } = data;
1063
+ return {
1064
+ ...rest,
1065
+ threadId: thread_id,
1066
+ content: typeof data.content === "string" ? (() => {
1067
+ try {
1068
+ return JSON.parse(data.content);
1069
+ } catch {
1070
+ return data.content;
1071
+ }
1072
+ })() : data.content,
1073
+ createdAt: new Date(data.createdAt),
1074
+ updatedAt: new Date(data.updatedAt)
1075
+ };
1076
+ }
1077
+ async updateMessages(args) {
1078
+ const { messages } = args;
1079
+ this.logger.debug("Updating messages", { count: messages.length });
1080
+ if (!messages.length) {
1081
+ return [];
1258
1082
  }
1083
+ const updatedMessages = [];
1084
+ const affectedThreadIds = /* @__PURE__ */ new Set();
1259
1085
  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]);
1086
+ for (const updateData of messages) {
1087
+ const { id, ...updates } = updateData;
1088
+ const existingMessage = await this.#db.load({ tableName: storage.TABLE_MESSAGES, keys: { id } });
1089
+ if (!existingMessage) {
1090
+ this.logger.warn("Message not found for update", { id });
1091
+ continue;
1092
+ }
1093
+ const existingMsg = this.parseMessageData(existingMessage);
1094
+ const originalThreadId = existingMsg.threadId;
1095
+ affectedThreadIds.add(originalThreadId);
1096
+ const updatePayload = {};
1097
+ if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
1098
+ if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
1099
+ if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
1100
+ if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
1101
+ updatePayload.thread_id = updates.threadId;
1102
+ affectedThreadIds.add(updates.threadId);
1103
+ }
1104
+ if (updates.content) {
1105
+ const existingContent = existingMsg.content;
1106
+ let newContent = { ...existingContent };
1107
+ if (updates.content.metadata !== void 0) {
1108
+ newContent.metadata = {
1109
+ ...existingContent.metadata || {},
1110
+ ...updates.content.metadata || {}
1111
+ };
1112
+ }
1113
+ if (updates.content.content !== void 0) {
1114
+ newContent.content = updates.content.content;
1115
+ }
1116
+ if ("parts" in updates.content && updates.content.parts !== void 0) {
1117
+ newContent.parts = updates.content.parts;
1118
+ }
1119
+ updatePayload.content = JSON.stringify(newContent);
1120
+ }
1121
+ await this.#db.insert({ tableName: storage.TABLE_MESSAGES, record: { id, ...updatePayload } });
1122
+ const updatedMessage = await this.#db.load({ tableName: storage.TABLE_MESSAGES, keys: { id } });
1123
+ if (updatedMessage) {
1124
+ updatedMessages.push(this.parseMessageData(updatedMessage));
1267
1125
  }
1268
1126
  }
1269
- console.log(await table.schema());
1270
- await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
1127
+ for (const threadId of affectedThreadIds) {
1128
+ await this.#db.insert({
1129
+ tableName: storage.TABLE_THREADS,
1130
+ record: { id: threadId, updatedAt: Date.now() }
1131
+ });
1132
+ }
1133
+ return updatedMessages;
1271
1134
  } catch (error$1) {
1272
1135
  throw new error.MastraError(
1273
1136
  {
1274
- id: "STORAGE_LANCE_STORAGE_INSERT_FAILED",
1137
+ id: storage.createStorageErrorId("LANCE", "UPDATE_MESSAGES", "FAILED"),
1275
1138
  domain: error.ErrorDomain.STORAGE,
1276
1139
  category: error.ErrorCategory.THIRD_PARTY,
1277
- details: { tableName }
1140
+ details: { count: messages.length }
1278
1141
  },
1279
1142
  error$1
1280
1143
  );
1281
1144
  }
1282
1145
  }
1283
- async batchInsert({ tableName, records }) {
1146
+ async getResourceById({ resourceId }) {
1284
1147
  try {
1285
- if (!this.client) {
1286
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1148
+ const resource = await this.#db.load({ tableName: storage.TABLE_RESOURCES, keys: { id: resourceId } });
1149
+ if (!resource) {
1150
+ return null;
1287
1151
  }
1288
- if (!tableName) {
1289
- throw new Error("tableName is required for batchInsert.");
1152
+ let createdAt;
1153
+ let updatedAt;
1154
+ try {
1155
+ if (resource.createdAt instanceof Date) {
1156
+ createdAt = resource.createdAt;
1157
+ } else if (typeof resource.createdAt === "string") {
1158
+ createdAt = new Date(resource.createdAt);
1159
+ } else if (typeof resource.createdAt === "number") {
1160
+ createdAt = new Date(resource.createdAt);
1161
+ } else {
1162
+ createdAt = /* @__PURE__ */ new Date();
1163
+ }
1164
+ if (isNaN(createdAt.getTime())) {
1165
+ createdAt = /* @__PURE__ */ new Date();
1166
+ }
1167
+ } catch {
1168
+ createdAt = /* @__PURE__ */ new Date();
1290
1169
  }
1291
- if (!records || records.length === 0) {
1292
- throw new Error("records array is required and cannot be empty for batchInsert.");
1170
+ try {
1171
+ if (resource.updatedAt instanceof Date) {
1172
+ updatedAt = resource.updatedAt;
1173
+ } else if (typeof resource.updatedAt === "string") {
1174
+ updatedAt = new Date(resource.updatedAt);
1175
+ } else if (typeof resource.updatedAt === "number") {
1176
+ updatedAt = new Date(resource.updatedAt);
1177
+ } else {
1178
+ updatedAt = /* @__PURE__ */ new Date();
1179
+ }
1180
+ if (isNaN(updatedAt.getTime())) {
1181
+ updatedAt = /* @__PURE__ */ new Date();
1182
+ }
1183
+ } catch {
1184
+ updatedAt = /* @__PURE__ */ new Date();
1293
1185
  }
1294
- } catch (validationError) {
1295
- throw new error.MastraError(
1296
- {
1297
- id: "STORAGE_LANCE_STORAGE_BATCH_INSERT_INVALID_ARGS",
1298
- domain: error.ErrorDomain.STORAGE,
1299
- category: error.ErrorCategory.USER,
1300
- text: validationError.message,
1301
- details: { tableName }
1302
- },
1303
- validationError
1304
- );
1305
- }
1306
- 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
- }
1186
+ let workingMemory = resource.workingMemory;
1187
+ if (workingMemory === null || workingMemory === void 0) {
1188
+ workingMemory = void 0;
1189
+ } else if (workingMemory === "") {
1190
+ workingMemory = "";
1191
+ } else if (typeof workingMemory === "object") {
1192
+ workingMemory = JSON.stringify(workingMemory);
1193
+ }
1194
+ let metadata = resource.metadata;
1195
+ if (metadata === "" || metadata === null || metadata === void 0) {
1196
+ metadata = void 0;
1197
+ } else if (typeof metadata === "string") {
1198
+ try {
1199
+ metadata = JSON.parse(metadata);
1200
+ } catch {
1201
+ metadata = metadata;
1316
1202
  }
1317
- return processedRecord;
1318
- });
1319
- console.log(processedRecords);
1320
- await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
1203
+ }
1204
+ return {
1205
+ ...resource,
1206
+ createdAt,
1207
+ updatedAt,
1208
+ workingMemory,
1209
+ metadata
1210
+ };
1321
1211
  } catch (error$1) {
1322
1212
  throw new error.MastraError(
1323
1213
  {
1324
- id: "STORAGE_LANCE_STORAGE_BATCH_INSERT_FAILED",
1214
+ id: storage.createStorageErrorId("LANCE", "GET_RESOURCE_BY_ID", "FAILED"),
1325
1215
  domain: error.ErrorDomain.STORAGE,
1326
- category: error.ErrorCategory.THIRD_PARTY,
1327
- details: { tableName }
1216
+ category: error.ErrorCategory.THIRD_PARTY
1328
1217
  },
1329
1218
  error$1
1330
1219
  );
1331
1220
  }
1332
1221
  }
1333
- async load({ tableName, keys }) {
1222
+ async saveResource({ resource }) {
1334
1223
  try {
1335
- if (!this.client) {
1336
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1337
- }
1338
- if (!tableName) {
1339
- throw new Error("tableName is required for load.");
1340
- }
1341
- if (!keys || Object.keys(keys).length === 0) {
1342
- throw new Error("keys are required and cannot be empty for load.");
1343
- }
1344
- } catch (validationError) {
1224
+ const record = {
1225
+ ...resource,
1226
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : "",
1227
+ createdAt: resource.createdAt.getTime(),
1228
+ // Store as timestamp (milliseconds)
1229
+ updatedAt: resource.updatedAt.getTime()
1230
+ // Store as timestamp (milliseconds)
1231
+ };
1232
+ const table = await this.client.openTable(storage.TABLE_RESOURCES);
1233
+ await table.add([record], { mode: "append" });
1234
+ return resource;
1235
+ } catch (error$1) {
1345
1236
  throw new error.MastraError(
1346
1237
  {
1347
- id: "STORAGE_LANCE_STORAGE_LOAD_INVALID_ARGS",
1238
+ id: storage.createStorageErrorId("LANCE", "SAVE_RESOURCE", "FAILED"),
1348
1239
  domain: error.ErrorDomain.STORAGE,
1349
- category: error.ErrorCategory.USER,
1350
- text: validationError.message,
1351
- details: { tableName }
1240
+ category: error.ErrorCategory.THIRD_PARTY
1352
1241
  },
1353
- validationError
1242
+ error$1
1354
1243
  );
1355
1244
  }
1356
- try {
1357
- const table = await this.client.openTable(tableName);
1358
- const tableSchema = await getTableSchema({ tableName, client: this.client });
1359
- const query = table.query();
1360
- if (Object.keys(keys).length > 0) {
1361
- validateKeyTypes(keys, tableSchema);
1362
- const filterConditions = Object.entries(keys).map(([key, value]) => {
1363
- const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
1364
- const quotedKey = isCamelCase ? `\`${key}\`` : key;
1365
- if (typeof value === "string") {
1366
- return `${quotedKey} = '${value}'`;
1367
- } else if (value === null) {
1368
- return `${quotedKey} IS NULL`;
1369
- } else {
1370
- return `${quotedKey} = ${value}`;
1371
- }
1372
- }).join(" AND ");
1373
- this.logger.debug("where clause generated: " + filterConditions);
1374
- query.where(filterConditions);
1375
- }
1376
- const result = await query.limit(1).toArray();
1377
- if (result.length === 0) {
1378
- this.logger.debug("No record found");
1379
- return null;
1245
+ }
1246
+ async updateResource({
1247
+ resourceId,
1248
+ workingMemory,
1249
+ metadata
1250
+ }) {
1251
+ const maxRetries = 3;
1252
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1253
+ try {
1254
+ const existingResource = await this.getResourceById({ resourceId });
1255
+ if (!existingResource) {
1256
+ const newResource = {
1257
+ id: resourceId,
1258
+ workingMemory,
1259
+ metadata: metadata || {},
1260
+ createdAt: /* @__PURE__ */ new Date(),
1261
+ updatedAt: /* @__PURE__ */ new Date()
1262
+ };
1263
+ return this.saveResource({ resource: newResource });
1264
+ }
1265
+ const updatedResource = {
1266
+ ...existingResource,
1267
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1268
+ metadata: {
1269
+ ...existingResource.metadata,
1270
+ ...metadata
1271
+ },
1272
+ updatedAt: /* @__PURE__ */ new Date()
1273
+ };
1274
+ const record = {
1275
+ id: resourceId,
1276
+ workingMemory: updatedResource.workingMemory || "",
1277
+ metadata: updatedResource.metadata ? JSON.stringify(updatedResource.metadata) : "",
1278
+ updatedAt: updatedResource.updatedAt.getTime()
1279
+ // Store as timestamp (milliseconds)
1280
+ };
1281
+ const table = await this.client.openTable(storage.TABLE_RESOURCES);
1282
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
1283
+ return updatedResource;
1284
+ } catch (error$1) {
1285
+ if (error$1.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
1286
+ const delay = Math.pow(2, attempt) * 10;
1287
+ await new Promise((resolve) => setTimeout(resolve, delay));
1288
+ continue;
1289
+ }
1290
+ throw new error.MastraError(
1291
+ {
1292
+ id: storage.createStorageErrorId("LANCE", "UPDATE_RESOURCE", "FAILED"),
1293
+ domain: error.ErrorDomain.STORAGE,
1294
+ category: error.ErrorCategory.THIRD_PARTY
1295
+ },
1296
+ error$1
1297
+ );
1380
1298
  }
1381
- return processResultWithTypeConversion(result[0], tableSchema);
1382
- } catch (error$1) {
1383
- if (error$1 instanceof error.MastraError) throw error$1;
1384
- throw new error.MastraError(
1385
- {
1386
- id: "STORAGE_LANCE_STORAGE_LOAD_FAILED",
1387
- domain: error.ErrorDomain.STORAGE,
1388
- category: error.ErrorCategory.THIRD_PARTY,
1389
- details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? "" }
1390
- },
1391
- error$1
1392
- );
1393
1299
  }
1300
+ throw new Error("Unexpected end of retry loop");
1394
1301
  }
1395
1302
  };
1396
1303
  var StoreScoresLance = class extends storage.ScoresStorage {
1397
1304
  client;
1398
- constructor({ client }) {
1305
+ #db;
1306
+ constructor(config) {
1399
1307
  super();
1308
+ const client = resolveLanceConfig(config);
1400
1309
  this.client = client;
1310
+ this.#db = new LanceDB({ client });
1311
+ }
1312
+ async init() {
1313
+ await this.#db.createTable({ tableName: storage.TABLE_SCORERS, schema: storage.SCORERS_SCHEMA });
1314
+ await this.#db.alterTable({
1315
+ tableName: storage.TABLE_SCORERS,
1316
+ schema: storage.SCORERS_SCHEMA,
1317
+ ifNotExists: ["spanId", "requestContext"]
1318
+ });
1319
+ }
1320
+ async dangerouslyClearAll() {
1321
+ await this.#db.clearTable({ tableName: storage.TABLE_SCORERS });
1401
1322
  }
1402
1323
  async saveScore(score) {
1324
+ let validatedScore;
1325
+ try {
1326
+ validatedScore = evals.saveScorePayloadSchema.parse(score);
1327
+ } catch (error$1) {
1328
+ throw new error.MastraError(
1329
+ {
1330
+ id: storage.createStorageErrorId("LANCE", "SAVE_SCORE", "VALIDATION_FAILED"),
1331
+ text: "Failed to save score in LanceStorage",
1332
+ domain: error.ErrorDomain.STORAGE,
1333
+ category: error.ErrorCategory.USER,
1334
+ details: {
1335
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
1336
+ entityId: score.entityId ?? "unknown",
1337
+ entityType: score.entityType ?? "unknown",
1338
+ traceId: score.traceId ?? "",
1339
+ spanId: score.spanId ?? ""
1340
+ }
1341
+ },
1342
+ error$1
1343
+ );
1344
+ }
1345
+ const id = crypto.randomUUID();
1346
+ const now = /* @__PURE__ */ new Date();
1403
1347
  try {
1404
- const id = crypto.randomUUID();
1405
1348
  const table = await this.client.openTable(storage.TABLE_SCORERS);
1406
1349
  const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1407
1350
  const allowedFields = new Set(schema.fields.map((f) => f.name));
1408
1351
  const filteredScore = {};
1409
- Object.keys(score).forEach((key) => {
1352
+ for (const key of Object.keys(validatedScore)) {
1410
1353
  if (allowedFields.has(key)) {
1411
- filteredScore[key] = score[key];
1354
+ filteredScore[key] = validatedScore[key];
1412
1355
  }
1413
- });
1356
+ }
1414
1357
  for (const key in filteredScore) {
1415
1358
  if (filteredScore[key] !== null && typeof filteredScore[key] === "object" && !(filteredScore[key] instanceof Date)) {
1416
1359
  filteredScore[key] = JSON.stringify(filteredScore[key]);
1417
1360
  }
1418
1361
  }
1419
1362
  filteredScore.id = id;
1363
+ filteredScore.createdAt = now;
1364
+ filteredScore.updatedAt = now;
1420
1365
  await table.add([filteredScore], { mode: "append" });
1421
- return { score };
1366
+ return { score: { ...validatedScore, id, createdAt: now, updatedAt: now } };
1422
1367
  } catch (error$1) {
1423
1368
  throw new error.MastraError(
1424
1369
  {
1425
- id: "LANCE_STORAGE_SAVE_SCORE_FAILED",
1370
+ id: storage.createStorageErrorId("LANCE", "SAVE_SCORE", "FAILED"),
1426
1371
  text: "Failed to save score in LanceStorage",
1427
1372
  domain: error.ErrorDomain.STORAGE,
1428
1373
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1438,12 +1383,11 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1438
1383
  const query = table.query().where(`id = '${id}'`).limit(1);
1439
1384
  const records = await query.toArray();
1440
1385
  if (records.length === 0) return null;
1441
- const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1442
- return processResultWithTypeConversion(records[0], schema);
1386
+ return await this.transformScoreRow(records[0]);
1443
1387
  } catch (error$1) {
1444
1388
  throw new error.MastraError(
1445
1389
  {
1446
- id: "LANCE_STORAGE_GET_SCORE_BY_ID_FAILED",
1390
+ id: storage.createStorageErrorId("LANCE", "GET_SCORE_BY_ID", "FAILED"),
1447
1391
  text: "Failed to get score by id in LanceStorage",
1448
1392
  domain: error.ErrorDomain.STORAGE,
1449
1393
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1453,7 +1397,23 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1453
1397
  );
1454
1398
  }
1455
1399
  }
1456
- async getScoresByScorerId({
1400
+ /**
1401
+ * LanceDB-specific score row transformation.
1402
+ *
1403
+ * Note: This implementation does NOT use coreTransformScoreRow because:
1404
+ * 1. LanceDB stores schema information in the table itself (requires async fetch)
1405
+ * 2. Uses processResultWithTypeConversion utility for LanceDB-specific type handling
1406
+ */
1407
+ async transformScoreRow(row) {
1408
+ const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1409
+ const transformed = processResultWithTypeConversion(row, schema);
1410
+ return {
1411
+ ...transformed,
1412
+ createdAt: row.createdAt,
1413
+ updatedAt: row.updatedAt
1414
+ };
1415
+ }
1416
+ async listScoresByScorerId({
1457
1417
  scorerId,
1458
1418
  pagination,
1459
1419
  entityId,
@@ -1461,9 +1421,10 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1461
1421
  source
1462
1422
  }) {
1463
1423
  try {
1424
+ const { page, perPage: perPageInput } = pagination;
1425
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1426
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1464
1427
  const table = await this.client.openTable(storage.TABLE_SCORERS);
1465
- const { page = 0, perPage = 10 } = pagination || {};
1466
- const offset = page * perPage;
1467
1428
  let query = table.query().where(`\`scorerId\` = '${scorerId}'`);
1468
1429
  if (source) {
1469
1430
  query = query.where(`\`source\` = '${source}'`);
@@ -1474,30 +1435,38 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1474
1435
  if (entityType) {
1475
1436
  query = query.where(`\`entityType\` = '${entityType}'`);
1476
1437
  }
1477
- query = query.limit(perPage);
1478
- if (offset > 0) query.offset(offset);
1479
- const records = await query.toArray();
1480
- const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1481
- const scores = processResultWithTypeConversion(records, schema);
1482
1438
  let totalQuery = table.query().where(`\`scorerId\` = '${scorerId}'`);
1483
1439
  if (source) {
1484
1440
  totalQuery = totalQuery.where(`\`source\` = '${source}'`);
1485
1441
  }
1442
+ if (entityId) {
1443
+ totalQuery = totalQuery.where(`\`entityId\` = '${entityId}'`);
1444
+ }
1445
+ if (entityType) {
1446
+ totalQuery = totalQuery.where(`\`entityType\` = '${entityType}'`);
1447
+ }
1486
1448
  const allRecords = await totalQuery.toArray();
1487
1449
  const total = allRecords.length;
1450
+ const end = perPageInput === false ? total : start + perPage;
1451
+ if (perPageInput !== false) {
1452
+ query = query.limit(perPage);
1453
+ if (start > 0) query = query.offset(start);
1454
+ }
1455
+ const records = await query.toArray();
1456
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1488
1457
  return {
1489
1458
  pagination: {
1490
1459
  page,
1491
- perPage,
1460
+ perPage: perPageForResponse,
1492
1461
  total,
1493
- hasMore: offset + scores.length < total
1462
+ hasMore: end < total
1494
1463
  },
1495
1464
  scores
1496
1465
  };
1497
1466
  } catch (error$1) {
1498
1467
  throw new error.MastraError(
1499
1468
  {
1500
- id: "LANCE_STORAGE_GET_SCORES_BY_SCORER_ID_FAILED",
1469
+ id: storage.createStorageErrorId("LANCE", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
1501
1470
  text: "Failed to get scores by scorerId in LanceStorage",
1502
1471
  domain: error.ErrorDomain.STORAGE,
1503
1472
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1507,34 +1476,38 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1507
1476
  );
1508
1477
  }
1509
1478
  }
1510
- async getScoresByRunId({
1479
+ async listScoresByRunId({
1511
1480
  runId,
1512
1481
  pagination
1513
1482
  }) {
1514
1483
  try {
1484
+ const { page, perPage: perPageInput } = pagination;
1485
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1486
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1515
1487
  const table = await this.client.openTable(storage.TABLE_SCORERS);
1516
- const { page = 0, perPage = 10 } = pagination || {};
1517
- const offset = page * perPage;
1518
- const query = table.query().where(`\`runId\` = '${runId}'`).limit(perPage);
1519
- if (offset > 0) query.offset(offset);
1520
- const records = await query.toArray();
1521
- const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1522
- const scores = processResultWithTypeConversion(records, schema);
1523
1488
  const allRecords = await table.query().where(`\`runId\` = '${runId}'`).toArray();
1524
1489
  const total = allRecords.length;
1490
+ const end = perPageInput === false ? total : start + perPage;
1491
+ let query = table.query().where(`\`runId\` = '${runId}'`);
1492
+ if (perPageInput !== false) {
1493
+ query = query.limit(perPage);
1494
+ if (start > 0) query = query.offset(start);
1495
+ }
1496
+ const records = await query.toArray();
1497
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1525
1498
  return {
1526
1499
  pagination: {
1527
1500
  page,
1528
- perPage,
1501
+ perPage: perPageForResponse,
1529
1502
  total,
1530
- hasMore: offset + scores.length < total
1503
+ hasMore: end < total
1531
1504
  },
1532
1505
  scores
1533
1506
  };
1534
1507
  } catch (error$1) {
1535
1508
  throw new error.MastraError(
1536
1509
  {
1537
- id: "LANCE_STORAGE_GET_SCORES_BY_RUN_ID_FAILED",
1510
+ id: storage.createStorageErrorId("LANCE", "LIST_SCORES_BY_RUN_ID", "FAILED"),
1538
1511
  text: "Failed to get scores by runId in LanceStorage",
1539
1512
  domain: error.ErrorDomain.STORAGE,
1540
1513
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1544,35 +1517,39 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1544
1517
  );
1545
1518
  }
1546
1519
  }
1547
- async getScoresByEntityId({
1520
+ async listScoresByEntityId({
1548
1521
  entityId,
1549
1522
  entityType,
1550
1523
  pagination
1551
1524
  }) {
1552
1525
  try {
1526
+ const { page, perPage: perPageInput } = pagination;
1527
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1528
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1553
1529
  const table = await this.client.openTable(storage.TABLE_SCORERS);
1554
- const { page = 0, perPage = 10 } = pagination || {};
1555
- const offset = page * perPage;
1556
- const query = table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).limit(perPage);
1557
- if (offset > 0) query.offset(offset);
1558
- const records = await query.toArray();
1559
- const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1560
- const scores = processResultWithTypeConversion(records, schema);
1561
1530
  const allRecords = await table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).toArray();
1562
1531
  const total = allRecords.length;
1532
+ const end = perPageInput === false ? total : start + perPage;
1533
+ let query = table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`);
1534
+ if (perPageInput !== false) {
1535
+ query = query.limit(perPage);
1536
+ if (start > 0) query = query.offset(start);
1537
+ }
1538
+ const records = await query.toArray();
1539
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1563
1540
  return {
1564
1541
  pagination: {
1565
1542
  page,
1566
- perPage,
1543
+ perPage: perPageForResponse,
1567
1544
  total,
1568
- hasMore: offset + scores.length < total
1545
+ hasMore: end < total
1569
1546
  },
1570
1547
  scores
1571
1548
  };
1572
1549
  } catch (error$1) {
1573
1550
  throw new error.MastraError(
1574
1551
  {
1575
- id: "LANCE_STORAGE_GET_SCORES_BY_ENTITY_ID_FAILED",
1552
+ id: storage.createStorageErrorId("LANCE", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
1576
1553
  text: "Failed to get scores by entityId and entityType in LanceStorage",
1577
1554
  domain: error.ErrorDomain.STORAGE,
1578
1555
  category: error.ErrorCategory.THIRD_PARTY,
@@ -1582,199 +1559,52 @@ var StoreScoresLance = class extends storage.ScoresStorage {
1582
1559
  );
1583
1560
  }
1584
1561
  }
1585
- };
1586
- var StoreTracesLance = class extends storage.TracesStorage {
1587
- client;
1588
- operations;
1589
- constructor({ client, operations }) {
1590
- super();
1591
- this.client = client;
1592
- this.operations = operations;
1593
- }
1594
- async saveTrace({ trace }) {
1595
- try {
1596
- const table = await this.client.openTable(storage.TABLE_TRACES);
1597
- const record = {
1598
- ...trace,
1599
- attributes: JSON.stringify(trace.attributes),
1600
- status: JSON.stringify(trace.status),
1601
- events: JSON.stringify(trace.events),
1602
- links: JSON.stringify(trace.links),
1603
- other: JSON.stringify(trace.other)
1604
- };
1605
- await table.add([record], { mode: "append" });
1606
- return trace;
1607
- } catch (error$1) {
1608
- throw new error.MastraError(
1609
- {
1610
- id: "LANCE_STORE_SAVE_TRACE_FAILED",
1611
- domain: error.ErrorDomain.STORAGE,
1612
- category: error.ErrorCategory.THIRD_PARTY
1613
- },
1614
- error$1
1615
- );
1616
- }
1617
- }
1618
- async getTraceById({ traceId }) {
1619
- try {
1620
- const table = await this.client.openTable(storage.TABLE_TRACES);
1621
- const query = table.query().where(`id = '${traceId}'`);
1622
- const records = await query.toArray();
1623
- return records[0];
1624
- } catch (error$1) {
1625
- throw new error.MastraError(
1626
- {
1627
- id: "LANCE_STORE_GET_TRACE_BY_ID_FAILED",
1628
- domain: error.ErrorDomain.STORAGE,
1629
- category: error.ErrorCategory.THIRD_PARTY
1630
- },
1631
- error$1
1632
- );
1633
- }
1634
- }
1635
- async getTraces({
1636
- name,
1637
- scope,
1638
- page = 1,
1639
- perPage = 10,
1640
- attributes
1562
+ async listScoresBySpan({
1563
+ traceId,
1564
+ spanId,
1565
+ pagination
1641
1566
  }) {
1642
1567
  try {
1643
- const table = await this.client.openTable(storage.TABLE_TRACES);
1644
- const query = table.query();
1645
- if (name) {
1646
- query.where(`name = '${name}'`);
1647
- }
1648
- if (scope) {
1649
- query.where(`scope = '${scope}'`);
1650
- }
1651
- if (attributes) {
1652
- query.where(`attributes = '${JSON.stringify(attributes)}'`);
1653
- }
1654
- const offset = (page - 1) * perPage;
1655
- query.limit(perPage);
1656
- if (offset > 0) {
1657
- query.offset(offset);
1658
- }
1659
- const records = await query.toArray();
1660
- return records.map((record) => {
1661
- const processed = {
1662
- ...record,
1663
- attributes: record.attributes ? JSON.parse(record.attributes) : {},
1664
- status: record.status ? JSON.parse(record.status) : {},
1665
- events: record.events ? JSON.parse(record.events) : [],
1666
- links: record.links ? JSON.parse(record.links) : [],
1667
- other: record.other ? JSON.parse(record.other) : {},
1668
- startTime: new Date(record.startTime),
1669
- endTime: new Date(record.endTime),
1670
- createdAt: new Date(record.createdAt)
1671
- };
1672
- if (processed.parentSpanId === null || processed.parentSpanId === void 0) {
1673
- processed.parentSpanId = "";
1674
- } else {
1675
- processed.parentSpanId = String(processed.parentSpanId);
1676
- }
1677
- return processed;
1678
- });
1679
- } catch (error$1) {
1680
- throw new error.MastraError(
1681
- {
1682
- id: "LANCE_STORE_GET_TRACES_FAILED",
1683
- domain: error.ErrorDomain.STORAGE,
1684
- category: error.ErrorCategory.THIRD_PARTY,
1685
- details: { name: name ?? "", scope: scope ?? "" }
1686
- },
1687
- error$1
1688
- );
1689
- }
1690
- }
1691
- async getTracesPaginated(args) {
1692
- try {
1693
- const table = await this.client.openTable(storage.TABLE_TRACES);
1694
- const query = table.query();
1695
- const conditions = [];
1696
- if (args.name) {
1697
- conditions.push(`name = '${args.name}'`);
1698
- }
1699
- if (args.scope) {
1700
- conditions.push(`scope = '${args.scope}'`);
1701
- }
1702
- if (args.attributes) {
1703
- const attributesStr = JSON.stringify(args.attributes);
1704
- conditions.push(`attributes LIKE '%${attributesStr.replace(/"/g, '\\"')}%'`);
1705
- }
1706
- if (args.dateRange?.start) {
1707
- conditions.push(`\`createdAt\` >= ${args.dateRange.start.getTime()}`);
1708
- }
1709
- if (args.dateRange?.end) {
1710
- conditions.push(`\`createdAt\` <= ${args.dateRange.end.getTime()}`);
1711
- }
1712
- if (conditions.length > 0) {
1713
- const whereClause = conditions.join(" AND ");
1714
- query.where(whereClause);
1715
- }
1716
- let total = 0;
1717
- if (conditions.length > 0) {
1718
- const countQuery = table.query().where(conditions.join(" AND "));
1719
- const allRecords = await countQuery.toArray();
1720
- total = allRecords.length;
1721
- } else {
1722
- total = await table.countRows();
1723
- }
1724
- const page = args.page || 0;
1725
- const perPage = args.perPage || 10;
1726
- const offset = page * perPage;
1727
- query.limit(perPage);
1728
- if (offset > 0) {
1729
- query.offset(offset);
1568
+ const { page, perPage: perPageInput } = pagination;
1569
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1570
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1571
+ const table = await this.client.openTable(storage.TABLE_SCORERS);
1572
+ const allRecords = await table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`).toArray();
1573
+ const total = allRecords.length;
1574
+ const end = perPageInput === false ? total : start + perPage;
1575
+ let query = table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`);
1576
+ if (perPageInput !== false) {
1577
+ query = query.limit(perPage);
1578
+ if (start > 0) query = query.offset(start);
1730
1579
  }
1731
1580
  const records = await query.toArray();
1732
- const traces = records.map((record) => {
1733
- const processed = {
1734
- ...record,
1735
- attributes: record.attributes ? JSON.parse(record.attributes) : {},
1736
- status: record.status ? JSON.parse(record.status) : {},
1737
- events: record.events ? JSON.parse(record.events) : [],
1738
- links: record.links ? JSON.parse(record.links) : [],
1739
- other: record.other ? JSON.parse(record.other) : {},
1740
- startTime: new Date(record.startTime),
1741
- endTime: new Date(record.endTime),
1742
- createdAt: new Date(record.createdAt)
1743
- };
1744
- if (processed.parentSpanId === null || processed.parentSpanId === void 0) {
1745
- processed.parentSpanId = "";
1746
- } else {
1747
- processed.parentSpanId = String(processed.parentSpanId);
1748
- }
1749
- return processed;
1750
- });
1581
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1751
1582
  return {
1752
- traces,
1753
- total,
1754
- page,
1755
- perPage,
1756
- hasMore: total > (page + 1) * perPage
1583
+ pagination: {
1584
+ page,
1585
+ perPage: perPageForResponse,
1586
+ total,
1587
+ hasMore: end < total
1588
+ },
1589
+ scores
1757
1590
  };
1758
1591
  } catch (error$1) {
1759
1592
  throw new error.MastraError(
1760
1593
  {
1761
- id: "LANCE_STORE_GET_TRACES_PAGINATED_FAILED",
1594
+ id: storage.createStorageErrorId("LANCE", "LIST_SCORES_BY_SPAN", "FAILED"),
1595
+ text: "Failed to get scores by traceId and spanId in LanceStorage",
1762
1596
  domain: error.ErrorDomain.STORAGE,
1763
1597
  category: error.ErrorCategory.THIRD_PARTY,
1764
- details: { name: args.name ?? "", scope: args.scope ?? "" }
1598
+ details: { error: error$1?.message }
1765
1599
  },
1766
1600
  error$1
1767
1601
  );
1768
1602
  }
1769
1603
  }
1770
- async batchTraceInsert({ records }) {
1771
- this.logger.debug("Batch inserting traces", { count: records.length });
1772
- await this.operations.batchInsert({
1773
- tableName: storage.TABLE_TRACES,
1774
- records
1775
- });
1776
- }
1777
1604
  };
1605
+ function escapeSql(str) {
1606
+ return str.replace(/'/g, "''");
1607
+ }
1778
1608
  function parseWorkflowRun(row) {
1779
1609
  let parsedSnapshot = row.snapshot;
1780
1610
  if (typeof parsedSnapshot === "string") {
@@ -1795,54 +1625,104 @@ function parseWorkflowRun(row) {
1795
1625
  }
1796
1626
  var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1797
1627
  client;
1798
- constructor({ client }) {
1628
+ #db;
1629
+ constructor(config) {
1799
1630
  super();
1631
+ const client = resolveLanceConfig(config);
1800
1632
  this.client = client;
1633
+ this.#db = new LanceDB({ client });
1634
+ }
1635
+ async init() {
1636
+ const schema = storage.TABLE_SCHEMAS[storage.TABLE_WORKFLOW_SNAPSHOT];
1637
+ await this.#db.createTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT, schema });
1638
+ await this.#db.alterTable({
1639
+ tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
1640
+ schema,
1641
+ ifNotExists: ["resourceId"]
1642
+ });
1801
1643
  }
1802
- updateWorkflowResults({
1803
- // workflowName,
1804
- // runId,
1805
- // stepId,
1806
- // result,
1807
- // runtimeContext,
1644
+ async dangerouslyClearAll() {
1645
+ await this.#db.clearTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
1646
+ }
1647
+ async updateWorkflowResults({
1648
+ workflowName,
1649
+ runId,
1650
+ stepId,
1651
+ result,
1652
+ requestContext
1808
1653
  }) {
1809
- throw new Error("Method not implemented.");
1654
+ let snapshot = await this.loadWorkflowSnapshot({ workflowName, runId });
1655
+ if (!snapshot) {
1656
+ snapshot = {
1657
+ context: {},
1658
+ activePaths: [],
1659
+ timestamp: Date.now(),
1660
+ suspendedPaths: {},
1661
+ activeStepsPath: {},
1662
+ resumeLabels: {},
1663
+ serializedStepGraph: [],
1664
+ status: "pending",
1665
+ value: {},
1666
+ waitingPaths: {},
1667
+ runId,
1668
+ requestContext: {}
1669
+ };
1670
+ }
1671
+ snapshot.context[stepId] = result;
1672
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
1673
+ await this.persistWorkflowSnapshot({ workflowName, runId, snapshot });
1674
+ return snapshot.context;
1810
1675
  }
1811
- updateWorkflowState({
1812
- // workflowName,
1813
- // runId,
1814
- // opts,
1676
+ async updateWorkflowState({
1677
+ workflowName,
1678
+ runId,
1679
+ opts
1815
1680
  }) {
1816
- throw new Error("Method not implemented.");
1681
+ const snapshot = await this.loadWorkflowSnapshot({ workflowName, runId });
1682
+ if (!snapshot) {
1683
+ return void 0;
1684
+ }
1685
+ if (!snapshot.context) {
1686
+ throw new Error(`Snapshot not found for runId ${runId}`);
1687
+ }
1688
+ const updatedSnapshot = { ...snapshot, ...opts };
1689
+ await this.persistWorkflowSnapshot({ workflowName, runId, snapshot: updatedSnapshot });
1690
+ return updatedSnapshot;
1817
1691
  }
1818
1692
  async persistWorkflowSnapshot({
1819
1693
  workflowName,
1820
1694
  runId,
1821
- snapshot
1695
+ resourceId,
1696
+ snapshot,
1697
+ createdAt,
1698
+ updatedAt
1822
1699
  }) {
1823
1700
  try {
1824
1701
  const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1825
- const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
1702
+ const query = table.query().where(`workflow_name = '${escapeSql(workflowName)}' AND run_id = '${escapeSql(runId)}'`);
1826
1703
  const records = await query.toArray();
1827
- let createdAt;
1828
- const now = Date.now();
1704
+ let createdAtValue;
1705
+ const now = createdAt?.getTime() ?? Date.now();
1829
1706
  if (records.length > 0) {
1830
- createdAt = records[0].createdAt ?? now;
1707
+ createdAtValue = records[0].createdAt ?? now;
1831
1708
  } else {
1832
- createdAt = now;
1709
+ createdAtValue = now;
1833
1710
  }
1711
+ const { status, value, ...rest } = snapshot;
1834
1712
  const record = {
1835
1713
  workflow_name: workflowName,
1836
1714
  run_id: runId,
1837
- snapshot: JSON.stringify(snapshot),
1838
- createdAt,
1839
- updatedAt: now
1715
+ resourceId,
1716
+ snapshot: JSON.stringify({ status, value, ...rest }),
1717
+ // this is to ensure status is always just before value, for when querying the db by status
1718
+ createdAt: createdAtValue,
1719
+ updatedAt: updatedAt ?? now
1840
1720
  };
1841
1721
  await table.mergeInsert(["workflow_name", "run_id"]).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
1842
1722
  } catch (error$1) {
1843
1723
  throw new error.MastraError(
1844
1724
  {
1845
- id: "LANCE_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
1725
+ id: storage.createStorageErrorId("LANCE", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
1846
1726
  domain: error.ErrorDomain.STORAGE,
1847
1727
  category: error.ErrorCategory.THIRD_PARTY,
1848
1728
  details: { workflowName, runId }
@@ -1857,13 +1737,13 @@ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1857
1737
  }) {
1858
1738
  try {
1859
1739
  const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1860
- const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
1740
+ const query = table.query().where(`workflow_name = '${escapeSql(workflowName)}' AND run_id = '${escapeSql(runId)}'`);
1861
1741
  const records = await query.toArray();
1862
1742
  return records.length > 0 ? JSON.parse(records[0].snapshot) : null;
1863
1743
  } catch (error$1) {
1864
1744
  throw new error.MastraError(
1865
1745
  {
1866
- id: "LANCE_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
1746
+ id: storage.createStorageErrorId("LANCE", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
1867
1747
  domain: error.ErrorDomain.STORAGE,
1868
1748
  category: error.ErrorCategory.THIRD_PARTY,
1869
1749
  details: { workflowName, runId }
@@ -1875,9 +1755,9 @@ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1875
1755
  async getWorkflowRunById(args) {
1876
1756
  try {
1877
1757
  const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1878
- let whereClause = `run_id = '${args.runId}'`;
1758
+ let whereClause = `run_id = '${escapeSql(args.runId)}'`;
1879
1759
  if (args.workflowName) {
1880
- whereClause += ` AND workflow_name = '${args.workflowName}'`;
1760
+ whereClause += ` AND workflow_name = '${escapeSql(args.workflowName)}'`;
1881
1761
  }
1882
1762
  const query = table.query().where(whereClause);
1883
1763
  const records = await query.toArray();
@@ -1887,7 +1767,7 @@ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1887
1767
  } catch (error$1) {
1888
1768
  throw new error.MastraError(
1889
1769
  {
1890
- id: "LANCE_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
1770
+ id: storage.createStorageErrorId("LANCE", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
1891
1771
  domain: error.ErrorDomain.STORAGE,
1892
1772
  category: error.ErrorCategory.THIRD_PARTY,
1893
1773
  details: { runId: args.runId, workflowName: args.workflowName ?? "" }
@@ -1896,16 +1776,37 @@ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1896
1776
  );
1897
1777
  }
1898
1778
  }
1899
- async getWorkflowRuns(args) {
1779
+ async deleteWorkflowRunById({ runId, workflowName }) {
1780
+ try {
1781
+ const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1782
+ const whereClause = `run_id = '${escapeSql(runId)}' AND workflow_name = '${escapeSql(workflowName)}'`;
1783
+ await table.delete(whereClause);
1784
+ } catch (error$1) {
1785
+ throw new error.MastraError(
1786
+ {
1787
+ id: storage.createStorageErrorId("LANCE", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
1788
+ domain: error.ErrorDomain.STORAGE,
1789
+ category: error.ErrorCategory.THIRD_PARTY,
1790
+ details: { runId, workflowName }
1791
+ },
1792
+ error$1
1793
+ );
1794
+ }
1795
+ }
1796
+ async listWorkflowRuns(args) {
1900
1797
  try {
1901
1798
  const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1902
1799
  let query = table.query();
1903
1800
  const conditions = [];
1904
1801
  if (args?.workflowName) {
1905
- conditions.push(`workflow_name = '${args.workflowName.replace(/'/g, "''")}'`);
1802
+ conditions.push(`workflow_name = '${escapeSql(args.workflowName)}'`);
1803
+ }
1804
+ if (args?.status) {
1805
+ const escapedStatus = args.status.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/%/g, "\\%").replace(/_/g, "\\_");
1806
+ conditions.push(`\`snapshot\` LIKE '%"status":"${escapedStatus}","value"%'`);
1906
1807
  }
1907
1808
  if (args?.resourceId) {
1908
- conditions.push(`\`resourceId\` = '${args.resourceId}'`);
1809
+ conditions.push(`\`resourceId\` = '${escapeSql(args.resourceId)}'`);
1909
1810
  }
1910
1811
  if (args?.fromDate instanceof Date) {
1911
1812
  conditions.push(`\`createdAt\` >= ${args.fromDate.getTime()}`);
@@ -1920,11 +1821,22 @@ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1920
1821
  } else {
1921
1822
  total = await table.countRows();
1922
1823
  }
1923
- if (args?.limit) {
1924
- query.limit(args.limit);
1925
- }
1926
- if (args?.offset) {
1927
- query.offset(args.offset);
1824
+ if (args?.perPage !== void 0 && args?.page !== void 0) {
1825
+ const normalizedPerPage = storage.normalizePerPage(args.perPage, Number.MAX_SAFE_INTEGER);
1826
+ if (args.page < 0 || !Number.isInteger(args.page)) {
1827
+ throw new error.MastraError(
1828
+ {
1829
+ id: storage.createStorageErrorId("LANCE", "LIST_WORKFLOW_RUNS", "INVALID_PAGINATION"),
1830
+ domain: error.ErrorDomain.STORAGE,
1831
+ category: error.ErrorCategory.USER,
1832
+ details: { page: args.page, perPage: args.perPage }
1833
+ },
1834
+ new Error(`Invalid pagination parameters: page=${args.page}, perPage=${args.perPage}`)
1835
+ );
1836
+ }
1837
+ const offset = args.page * normalizedPerPage;
1838
+ query.limit(normalizedPerPage);
1839
+ query.offset(offset);
1928
1840
  }
1929
1841
  const records = await query.toArray();
1930
1842
  return {
@@ -1934,10 +1846,10 @@ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1934
1846
  } catch (error$1) {
1935
1847
  throw new error.MastraError(
1936
1848
  {
1937
- id: "LANCE_STORE_GET_WORKFLOW_RUNS_FAILED",
1849
+ id: storage.createStorageErrorId("LANCE", "LIST_WORKFLOW_RUNS", "FAILED"),
1938
1850
  domain: error.ErrorDomain.STORAGE,
1939
1851
  category: error.ErrorCategory.THIRD_PARTY,
1940
- details: { namespace: args?.namespace ?? "", workflowName: args?.workflowName ?? "" }
1852
+ details: { resourceId: args?.resourceId ?? "", workflowName: args?.workflowName ?? "" }
1941
1853
  },
1942
1854
  error$1
1943
1855
  );
@@ -1951,295 +1863,95 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
1951
1863
  lanceClient;
1952
1864
  /**
1953
1865
  * Creates a new instance of LanceStorage
1866
+ * @param id The unique identifier for this storage instance
1867
+ * @param name The name for this storage instance
1954
1868
  * @param uri The URI to connect to LanceDB
1955
- * @param options connection options
1869
+ * @param connectionOptions connection options for LanceDB
1870
+ * @param storageOptions storage options including disableInit
1956
1871
  *
1957
1872
  * Usage:
1958
1873
  *
1959
1874
  * Connect to a local database
1960
1875
  * ```ts
1961
- * const store = await LanceStorage.create('/path/to/db');
1876
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', '/path/to/db');
1962
1877
  * ```
1963
1878
  *
1964
1879
  * Connect to a LanceDB cloud database
1965
1880
  * ```ts
1966
- * const store = await LanceStorage.create('db://host:port');
1881
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', 'db://host:port');
1967
1882
  * ```
1968
1883
  *
1969
1884
  * Connect to a cloud database
1970
1885
  * ```ts
1971
- * const store = await LanceStorage.create('s3://bucket/db', { storageOptions: { timeout: '60s' } });
1886
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', 's3://bucket/db', { storageOptions: { timeout: '60s' } });
1887
+ * ```
1888
+ *
1889
+ * Disable auto-init for runtime (after CI/CD has run migrations)
1890
+ * ```ts
1891
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', '/path/to/db', undefined, { disableInit: true });
1972
1892
  * ```
1973
1893
  */
1974
- static async create(name, uri, options) {
1975
- const instance = new _LanceStorage(name);
1894
+ static async create(id, name, uri, connectionOptions, storageOptions) {
1895
+ const instance = new _LanceStorage(id, name, storageOptions?.disableInit);
1976
1896
  try {
1977
- instance.lanceClient = await lancedb.connect(uri, options);
1978
- const operations = new StoreOperationsLance({ client: instance.lanceClient });
1897
+ instance.lanceClient = await lancedb.connect(uri, connectionOptions);
1979
1898
  instance.stores = {
1980
- operations: new StoreOperationsLance({ client: instance.lanceClient }),
1981
- workflows: new StoreWorkflowsLance({ client: instance.lanceClient }),
1982
- traces: new StoreTracesLance({ client: instance.lanceClient, operations }),
1983
- scores: new StoreScoresLance({ client: instance.lanceClient }),
1984
- memory: new StoreMemoryLance({ client: instance.lanceClient, operations }),
1985
- legacyEvals: new StoreLegacyEvalsLance({ client: instance.lanceClient })
1986
- };
1987
- return instance;
1988
- } catch (e) {
1989
- throw new error.MastraError(
1990
- {
1991
- id: "STORAGE_LANCE_STORAGE_CONNECT_FAILED",
1992
- domain: error.ErrorDomain.STORAGE,
1993
- category: error.ErrorCategory.THIRD_PARTY,
1994
- text: `Failed to connect to LanceDB: ${e.message || e}`,
1995
- details: { uri, optionsProvided: !!options }
1996
- },
1997
- e
1998
- );
1999
- }
2000
- }
2001
- /**
2002
- * @internal
2003
- * Private constructor to enforce using the create factory method
2004
- */
2005
- constructor(name) {
2006
- super({ name });
2007
- const operations = new StoreOperationsLance({ client: this.lanceClient });
2008
- this.stores = {
2009
- operations: new StoreOperationsLance({ client: this.lanceClient }),
2010
- workflows: new StoreWorkflowsLance({ client: this.lanceClient }),
2011
- traces: new StoreTracesLance({ client: this.lanceClient, operations }),
2012
- scores: new StoreScoresLance({ client: this.lanceClient }),
2013
- legacyEvals: new StoreLegacyEvalsLance({ client: this.lanceClient }),
2014
- memory: new StoreMemoryLance({ client: this.lanceClient, operations })
2015
- };
2016
- }
2017
- async createTable({
2018
- tableName,
2019
- schema
2020
- }) {
2021
- return this.stores.operations.createTable({ tableName, schema });
2022
- }
2023
- async dropTable({ tableName }) {
2024
- return this.stores.operations.dropTable({ tableName });
2025
- }
2026
- async alterTable({
2027
- tableName,
2028
- schema,
2029
- ifNotExists
2030
- }) {
2031
- return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
2032
- }
2033
- async clearTable({ tableName }) {
2034
- return this.stores.operations.clearTable({ tableName });
2035
- }
2036
- async insert({ tableName, record }) {
2037
- return this.stores.operations.insert({ tableName, record });
2038
- }
2039
- async batchInsert({ tableName, records }) {
2040
- return this.stores.operations.batchInsert({ tableName, records });
2041
- }
2042
- async load({ tableName, keys }) {
2043
- return this.stores.operations.load({ tableName, keys });
2044
- }
2045
- async getThreadById({ threadId }) {
2046
- return this.stores.memory.getThreadById({ threadId });
2047
- }
2048
- async getThreadsByResourceId({ resourceId }) {
2049
- return this.stores.memory.getThreadsByResourceId({ resourceId });
1899
+ workflows: new StoreWorkflowsLance({ client: instance.lanceClient }),
1900
+ scores: new StoreScoresLance({ client: instance.lanceClient }),
1901
+ memory: new StoreMemoryLance({ client: instance.lanceClient })
1902
+ };
1903
+ return instance;
1904
+ } catch (e) {
1905
+ throw new error.MastraError(
1906
+ {
1907
+ id: storage.createStorageErrorId("LANCE", "CONNECT", "FAILED"),
1908
+ domain: error.ErrorDomain.STORAGE,
1909
+ category: error.ErrorCategory.THIRD_PARTY,
1910
+ text: `Failed to connect to LanceDB: ${e.message || e}`,
1911
+ details: { uri, optionsProvided: !!connectionOptions }
1912
+ },
1913
+ e
1914
+ );
1915
+ }
2050
1916
  }
2051
1917
  /**
2052
- * Saves a thread to the database. This function doesn't overwrite existing threads.
2053
- * @param thread - The thread to save
2054
- * @returns The saved thread
1918
+ * Creates a new instance of LanceStorage from a pre-configured LanceDB connection.
1919
+ * Use this when you need to configure the connection before initialization.
1920
+ *
1921
+ * @param id The unique identifier for this storage instance
1922
+ * @param name The name for this storage instance
1923
+ * @param client Pre-configured LanceDB connection
1924
+ * @param options Storage options including disableInit
1925
+ *
1926
+ * @example
1927
+ * ```typescript
1928
+ * import { connect } from '@lancedb/lancedb';
1929
+ *
1930
+ * const client = await connect('/path/to/db', {
1931
+ * // Custom connection options
1932
+ * });
1933
+ *
1934
+ * const store = LanceStorage.fromClient('my-id', 'MyStorage', client);
1935
+ * ```
2055
1936
  */
2056
- async saveThread({ thread }) {
2057
- return this.stores.memory.saveThread({ thread });
2058
- }
2059
- async updateThread({
2060
- id,
2061
- title,
2062
- metadata
2063
- }) {
2064
- return this.stores.memory.updateThread({ id, title, metadata });
2065
- }
2066
- async deleteThread({ threadId }) {
2067
- return this.stores.memory.deleteThread({ threadId });
2068
- }
2069
- get supports() {
2070
- return {
2071
- selectByIncludeResourceScope: true,
2072
- resourceWorkingMemory: true,
2073
- hasColumn: true,
2074
- createTable: true,
2075
- deleteMessages: false
1937
+ static fromClient(id, name, client, options) {
1938
+ const instance = new _LanceStorage(id, name, options?.disableInit);
1939
+ instance.lanceClient = client;
1940
+ instance.stores = {
1941
+ workflows: new StoreWorkflowsLance({ client }),
1942
+ scores: new StoreScoresLance({ client }),
1943
+ memory: new StoreMemoryLance({ client })
2076
1944
  };
2077
- }
2078
- async getResourceById({ resourceId }) {
2079
- return this.stores.memory.getResourceById({ resourceId });
2080
- }
2081
- async saveResource({ resource }) {
2082
- return this.stores.memory.saveResource({ resource });
2083
- }
2084
- async updateResource({
2085
- resourceId,
2086
- workingMemory,
2087
- metadata
2088
- }) {
2089
- return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
1945
+ return instance;
2090
1946
  }
2091
1947
  /**
2092
- * Processes messages to include context messages based on withPreviousMessages and withNextMessages
2093
- * @param records - The sorted array of records to process
2094
- * @param include - The array of include specifications with context parameters
2095
- * @returns The processed array with context messages included
1948
+ * @internal
1949
+ * Private constructor to enforce using the create factory method.
1950
+ * Note: stores is initialized in create() after the lanceClient is connected.
2096
1951
  */
2097
- processMessagesWithContext(records, include) {
2098
- const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
2099
- if (messagesWithContext.length === 0) {
2100
- return records;
2101
- }
2102
- const messageIndexMap = /* @__PURE__ */ new Map();
2103
- records.forEach((message, index) => {
2104
- messageIndexMap.set(message.id, index);
2105
- });
2106
- const additionalIndices = /* @__PURE__ */ new Set();
2107
- for (const item of messagesWithContext) {
2108
- const messageIndex = messageIndexMap.get(item.id);
2109
- if (messageIndex !== void 0) {
2110
- if (item.withPreviousMessages) {
2111
- const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
2112
- for (let i = startIdx; i < messageIndex; i++) {
2113
- additionalIndices.add(i);
2114
- }
2115
- }
2116
- if (item.withNextMessages) {
2117
- const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
2118
- for (let i = messageIndex + 1; i <= endIdx; i++) {
2119
- additionalIndices.add(i);
2120
- }
2121
- }
2122
- }
2123
- }
2124
- if (additionalIndices.size === 0) {
2125
- return records;
2126
- }
2127
- const originalMatchIds = new Set(include.map((item) => item.id));
2128
- const allIndices = /* @__PURE__ */ new Set();
2129
- records.forEach((record, index) => {
2130
- if (originalMatchIds.has(record.id)) {
2131
- allIndices.add(index);
2132
- }
2133
- });
2134
- additionalIndices.forEach((index) => {
2135
- allIndices.add(index);
2136
- });
2137
- return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
2138
- }
2139
- async getMessages({
2140
- threadId,
2141
- resourceId,
2142
- selectBy,
2143
- format,
2144
- threadConfig
2145
- }) {
2146
- return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format, threadConfig });
2147
- }
2148
- async getMessagesById({
2149
- messageIds,
2150
- format
2151
- }) {
2152
- return this.stores.memory.getMessagesById({ messageIds, format });
2153
- }
2154
- async saveMessages(args) {
2155
- return this.stores.memory.saveMessages(args);
2156
- }
2157
- async getThreadsByResourceIdPaginated(args) {
2158
- return this.stores.memory.getThreadsByResourceIdPaginated(args);
2159
- }
2160
- async getMessagesPaginated(args) {
2161
- return this.stores.memory.getMessagesPaginated(args);
2162
- }
2163
- async updateMessages(_args) {
2164
- return this.stores.memory.updateMessages(_args);
2165
- }
2166
- async getTraceById(args) {
2167
- return this.stores.traces.getTraceById(args);
2168
- }
2169
- async getTraces(args) {
2170
- return this.stores.traces.getTraces(args);
2171
- }
2172
- async getTracesPaginated(args) {
2173
- return this.stores.traces.getTracesPaginated(args);
2174
- }
2175
- async getEvalsByAgentName(agentName, type) {
2176
- return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
2177
- }
2178
- async getEvals(options) {
2179
- return this.stores.legacyEvals.getEvals(options);
2180
- }
2181
- async getWorkflowRuns(args) {
2182
- return this.stores.workflows.getWorkflowRuns(args);
2183
- }
2184
- async getWorkflowRunById(args) {
2185
- return this.stores.workflows.getWorkflowRunById(args);
2186
- }
2187
- async updateWorkflowResults({
2188
- workflowName,
2189
- runId,
2190
- stepId,
2191
- result,
2192
- runtimeContext
2193
- }) {
2194
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
2195
- }
2196
- async updateWorkflowState({
2197
- workflowName,
2198
- runId,
2199
- opts
2200
- }) {
2201
- return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
2202
- }
2203
- async persistWorkflowSnapshot({
2204
- workflowName,
2205
- runId,
2206
- snapshot
2207
- }) {
2208
- return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
2209
- }
2210
- async loadWorkflowSnapshot({
2211
- workflowName,
2212
- runId
2213
- }) {
2214
- return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
2215
- }
2216
- async getScoreById({ id: _id }) {
2217
- return this.stores.scores.getScoreById({ id: _id });
2218
- }
2219
- async getScoresByScorerId({
2220
- scorerId,
2221
- source,
2222
- entityId,
2223
- entityType,
2224
- pagination
2225
- }) {
2226
- return this.stores.scores.getScoresByScorerId({ scorerId, source, pagination, entityId, entityType });
2227
- }
2228
- async saveScore(_score) {
2229
- return this.stores.scores.saveScore(_score);
2230
- }
2231
- async getScoresByRunId({
2232
- runId,
2233
- pagination
2234
- }) {
2235
- return this.stores.scores.getScoresByRunId({ runId, pagination });
2236
- }
2237
- async getScoresByEntityId({
2238
- entityId,
2239
- entityType,
2240
- pagination
2241
- }) {
2242
- return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
1952
+ constructor(id, name, disableInit) {
1953
+ super({ id, name, disableInit });
1954
+ this.stores = {};
2243
1955
  }
2244
1956
  };
2245
1957
  var LanceFilterTranslator = class extends filter.BaseFilterTranslator {
@@ -2588,14 +2300,14 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2588
2300
  * ```
2589
2301
  */
2590
2302
  static async create(uri, options) {
2591
- const instance = new _LanceVectorStore();
2303
+ const instance = new _LanceVectorStore(options?.id || crypto.randomUUID());
2592
2304
  try {
2593
2305
  instance.lanceClient = await lancedb.connect(uri, options);
2594
2306
  return instance;
2595
2307
  } catch (e) {
2596
2308
  throw new error.MastraError(
2597
2309
  {
2598
- id: "STORAGE_LANCE_VECTOR_CONNECT_FAILED",
2310
+ id: storage.createVectorErrorId("LANCE", "CONNECT", "FAILED"),
2599
2311
  domain: error.ErrorDomain.STORAGE,
2600
2312
  category: error.ErrorCategory.THIRD_PARTY,
2601
2313
  details: { uri }
@@ -2608,8 +2320,8 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2608
2320
  * @internal
2609
2321
  * Private constructor to enforce using the create factory method
2610
2322
  */
2611
- constructor() {
2612
- super();
2323
+ constructor(id) {
2324
+ super({ id });
2613
2325
  }
2614
2326
  close() {
2615
2327
  if (this.lanceClient) {
@@ -2638,7 +2350,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2638
2350
  } catch (error$1) {
2639
2351
  throw new error.MastraError(
2640
2352
  {
2641
- id: "STORAGE_LANCE_VECTOR_QUERY_FAILED_INVALID_ARGS",
2353
+ id: storage.createVectorErrorId("LANCE", "QUERY", "INVALID_ARGS"),
2642
2354
  domain: error.ErrorDomain.STORAGE,
2643
2355
  category: error.ErrorCategory.USER,
2644
2356
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2686,7 +2398,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2686
2398
  } catch (error$1) {
2687
2399
  throw new error.MastraError(
2688
2400
  {
2689
- id: "STORAGE_LANCE_VECTOR_QUERY_FAILED",
2401
+ id: storage.createVectorErrorId("LANCE", "QUERY", "FAILED"),
2690
2402
  domain: error.ErrorDomain.STORAGE,
2691
2403
  category: error.ErrorCategory.THIRD_PARTY,
2692
2404
  details: { tableName, includeVector, columnsCount: columns?.length, includeAllColumns }
@@ -2738,7 +2450,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2738
2450
  } catch (error$1) {
2739
2451
  throw new error.MastraError(
2740
2452
  {
2741
- id: "STORAGE_LANCE_VECTOR_UPSERT_FAILED_INVALID_ARGS",
2453
+ id: storage.createVectorErrorId("LANCE", "UPSERT", "INVALID_ARGS"),
2742
2454
  domain: error.ErrorDomain.STORAGE,
2743
2455
  category: error.ErrorCategory.USER,
2744
2456
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2774,7 +2486,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2774
2486
  } catch (error$1) {
2775
2487
  throw new error.MastraError(
2776
2488
  {
2777
- id: "STORAGE_LANCE_VECTOR_UPSERT_FAILED",
2489
+ id: storage.createVectorErrorId("LANCE", "UPSERT", "FAILED"),
2778
2490
  domain: error.ErrorDomain.STORAGE,
2779
2491
  category: error.ErrorCategory.THIRD_PARTY,
2780
2492
  details: { tableName, vectorCount: vectors.length, metadataCount: metadata.length, idsCount: ids.length }
@@ -2801,7 +2513,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2801
2513
  async createTable(tableName, data, options) {
2802
2514
  if (!this.lanceClient) {
2803
2515
  throw new error.MastraError({
2804
- id: "STORAGE_LANCE_VECTOR_CREATE_TABLE_FAILED_INVALID_ARGS",
2516
+ id: storage.createVectorErrorId("LANCE", "CREATE_TABLE", "INVALID_ARGS"),
2805
2517
  domain: error.ErrorDomain.STORAGE,
2806
2518
  category: error.ErrorCategory.USER,
2807
2519
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2816,7 +2528,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2816
2528
  } catch (error$1) {
2817
2529
  throw new error.MastraError(
2818
2530
  {
2819
- id: "STORAGE_LANCE_VECTOR_CREATE_TABLE_FAILED",
2531
+ id: storage.createVectorErrorId("LANCE", "CREATE_TABLE", "FAILED"),
2820
2532
  domain: error.ErrorDomain.STORAGE,
2821
2533
  category: error.ErrorCategory.THIRD_PARTY,
2822
2534
  details: { tableName }
@@ -2828,7 +2540,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2828
2540
  async listTables() {
2829
2541
  if (!this.lanceClient) {
2830
2542
  throw new error.MastraError({
2831
- id: "STORAGE_LANCE_VECTOR_LIST_TABLES_FAILED_INVALID_ARGS",
2543
+ id: storage.createVectorErrorId("LANCE", "LIST_TABLES", "INVALID_ARGS"),
2832
2544
  domain: error.ErrorDomain.STORAGE,
2833
2545
  category: error.ErrorCategory.USER,
2834
2546
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2840,7 +2552,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2840
2552
  } catch (error$1) {
2841
2553
  throw new error.MastraError(
2842
2554
  {
2843
- id: "STORAGE_LANCE_VECTOR_LIST_TABLES_FAILED",
2555
+ id: storage.createVectorErrorId("LANCE", "LIST_TABLES", "FAILED"),
2844
2556
  domain: error.ErrorDomain.STORAGE,
2845
2557
  category: error.ErrorCategory.THIRD_PARTY
2846
2558
  },
@@ -2851,7 +2563,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2851
2563
  async getTableSchema(tableName) {
2852
2564
  if (!this.lanceClient) {
2853
2565
  throw new error.MastraError({
2854
- id: "STORAGE_LANCE_VECTOR_GET_TABLE_SCHEMA_FAILED_INVALID_ARGS",
2566
+ id: storage.createVectorErrorId("LANCE", "GET_TABLE_SCHEMA", "INVALID_ARGS"),
2855
2567
  domain: error.ErrorDomain.STORAGE,
2856
2568
  category: error.ErrorCategory.USER,
2857
2569
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2864,7 +2576,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2864
2576
  } catch (error$1) {
2865
2577
  throw new error.MastraError(
2866
2578
  {
2867
- id: "STORAGE_LANCE_VECTOR_GET_TABLE_SCHEMA_FAILED",
2579
+ id: storage.createVectorErrorId("LANCE", "GET_TABLE_SCHEMA", "FAILED"),
2868
2580
  domain: error.ErrorDomain.STORAGE,
2869
2581
  category: error.ErrorCategory.THIRD_PARTY,
2870
2582
  details: { tableName }
@@ -2899,7 +2611,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2899
2611
  } catch (err) {
2900
2612
  throw new error.MastraError(
2901
2613
  {
2902
- id: "STORAGE_LANCE_VECTOR_CREATE_INDEX_FAILED_INVALID_ARGS",
2614
+ id: storage.createVectorErrorId("LANCE", "CREATE_INDEX", "INVALID_ARGS"),
2903
2615
  domain: error.ErrorDomain.STORAGE,
2904
2616
  category: error.ErrorCategory.USER,
2905
2617
  details: { tableName: tableName || "", indexName, dimension, metric }
@@ -2944,7 +2656,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2944
2656
  } catch (error$1) {
2945
2657
  throw new error.MastraError(
2946
2658
  {
2947
- id: "STORAGE_LANCE_VECTOR_CREATE_INDEX_FAILED",
2659
+ id: storage.createVectorErrorId("LANCE", "CREATE_INDEX", "FAILED"),
2948
2660
  domain: error.ErrorDomain.STORAGE,
2949
2661
  category: error.ErrorCategory.THIRD_PARTY,
2950
2662
  details: { tableName: tableName || "", indexName, dimension }
@@ -2956,7 +2668,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2956
2668
  async listIndexes() {
2957
2669
  if (!this.lanceClient) {
2958
2670
  throw new error.MastraError({
2959
- id: "STORAGE_LANCE_VECTOR_LIST_INDEXES_FAILED_INVALID_ARGS",
2671
+ id: storage.createVectorErrorId("LANCE", "LIST_INDEXES", "INVALID_ARGS"),
2960
2672
  domain: error.ErrorDomain.STORAGE,
2961
2673
  category: error.ErrorCategory.USER,
2962
2674
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2975,7 +2687,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2975
2687
  } catch (error$1) {
2976
2688
  throw new error.MastraError(
2977
2689
  {
2978
- id: "STORAGE_LANCE_VECTOR_LIST_INDEXES_FAILED",
2690
+ id: storage.createVectorErrorId("LANCE", "LIST_INDEXES", "FAILED"),
2979
2691
  domain: error.ErrorDomain.STORAGE,
2980
2692
  category: error.ErrorCategory.THIRD_PARTY
2981
2693
  },
@@ -2994,7 +2706,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2994
2706
  } catch (err) {
2995
2707
  throw new error.MastraError(
2996
2708
  {
2997
- id: "STORAGE_LANCE_VECTOR_DESCRIBE_INDEX_FAILED_INVALID_ARGS",
2709
+ id: storage.createVectorErrorId("LANCE", "DESCRIBE_INDEX", "INVALID_ARGS"),
2998
2710
  domain: error.ErrorDomain.STORAGE,
2999
2711
  category: error.ErrorCategory.USER,
3000
2712
  details: { indexName }
@@ -3029,7 +2741,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3029
2741
  } catch (error$1) {
3030
2742
  throw new error.MastraError(
3031
2743
  {
3032
- id: "STORAGE_LANCE_VECTOR_DESCRIBE_INDEX_FAILED",
2744
+ id: storage.createVectorErrorId("LANCE", "DESCRIBE_INDEX", "FAILED"),
3033
2745
  domain: error.ErrorDomain.STORAGE,
3034
2746
  category: error.ErrorCategory.THIRD_PARTY,
3035
2747
  details: { indexName }
@@ -3049,7 +2761,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3049
2761
  } catch (err) {
3050
2762
  throw new error.MastraError(
3051
2763
  {
3052
- id: "STORAGE_LANCE_VECTOR_DELETE_INDEX_FAILED_INVALID_ARGS",
2764
+ id: storage.createVectorErrorId("LANCE", "DELETE_INDEX", "INVALID_ARGS"),
3053
2765
  domain: error.ErrorDomain.STORAGE,
3054
2766
  category: error.ErrorCategory.USER,
3055
2767
  details: { indexName }
@@ -3072,7 +2784,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3072
2784
  } catch (error$1) {
3073
2785
  throw new error.MastraError(
3074
2786
  {
3075
- id: "STORAGE_LANCE_VECTOR_DELETE_INDEX_FAILED",
2787
+ id: storage.createVectorErrorId("LANCE", "DELETE_INDEX", "FAILED"),
3076
2788
  domain: error.ErrorDomain.STORAGE,
3077
2789
  category: error.ErrorCategory.THIRD_PARTY,
3078
2790
  details: { indexName }
@@ -3087,7 +2799,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3087
2799
  async deleteAllTables() {
3088
2800
  if (!this.lanceClient) {
3089
2801
  throw new error.MastraError({
3090
- id: "STORAGE_LANCE_VECTOR_DELETE_ALL_TABLES_FAILED_INVALID_ARGS",
2802
+ id: storage.createVectorErrorId("LANCE", "DELETE_ALL_TABLES", "INVALID_ARGS"),
3091
2803
  domain: error.ErrorDomain.STORAGE,
3092
2804
  category: error.ErrorCategory.USER,
3093
2805
  details: { methodName: "deleteAllTables" },
@@ -3099,7 +2811,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3099
2811
  } catch (error$1) {
3100
2812
  throw new error.MastraError(
3101
2813
  {
3102
- id: "STORAGE_LANCE_VECTOR_DELETE_ALL_TABLES_FAILED",
2814
+ id: storage.createVectorErrorId("LANCE", "DELETE_ALL_TABLES", "FAILED"),
3103
2815
  domain: error.ErrorDomain.STORAGE,
3104
2816
  category: error.ErrorCategory.THIRD_PARTY,
3105
2817
  details: { methodName: "deleteAllTables" }
@@ -3111,7 +2823,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3111
2823
  async deleteTable(tableName) {
3112
2824
  if (!this.lanceClient) {
3113
2825
  throw new error.MastraError({
3114
- id: "STORAGE_LANCE_VECTOR_DELETE_TABLE_FAILED_INVALID_ARGS",
2826
+ id: storage.createVectorErrorId("LANCE", "DELETE_TABLE", "INVALID_ARGS"),
3115
2827
  domain: error.ErrorDomain.STORAGE,
3116
2828
  category: error.ErrorCategory.USER,
3117
2829
  details: { tableName },
@@ -3123,7 +2835,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3123
2835
  } catch (error$1) {
3124
2836
  throw new error.MastraError(
3125
2837
  {
3126
- id: "STORAGE_LANCE_VECTOR_DELETE_TABLE_FAILED",
2838
+ id: storage.createVectorErrorId("LANCE", "DELETE_TABLE", "FAILED"),
3127
2839
  domain: error.ErrorDomain.STORAGE,
3128
2840
  category: error.ErrorCategory.THIRD_PARTY,
3129
2841
  details: { tableName }
@@ -3132,7 +2844,44 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3132
2844
  );
3133
2845
  }
3134
2846
  }
3135
- async updateVector({ indexName, id, update }) {
2847
+ async updateVector(params) {
2848
+ const { indexName, update } = params;
2849
+ if ("id" in params && "filter" in params && params.id && params.filter) {
2850
+ throw new error.MastraError({
2851
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "MUTUALLY_EXCLUSIVE"),
2852
+ domain: error.ErrorDomain.STORAGE,
2853
+ category: error.ErrorCategory.USER,
2854
+ text: "id and filter are mutually exclusive",
2855
+ details: { indexName }
2856
+ });
2857
+ }
2858
+ if (!("id" in params || "filter" in params) || !params.id && !params.filter) {
2859
+ throw new error.MastraError({
2860
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "NO_TARGET"),
2861
+ domain: error.ErrorDomain.STORAGE,
2862
+ category: error.ErrorCategory.USER,
2863
+ text: "Either id or filter must be provided",
2864
+ details: { indexName }
2865
+ });
2866
+ }
2867
+ if ("filter" in params && params.filter && Object.keys(params.filter).length === 0) {
2868
+ throw new error.MastraError({
2869
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "EMPTY_FILTER"),
2870
+ domain: error.ErrorDomain.STORAGE,
2871
+ category: error.ErrorCategory.USER,
2872
+ text: "Cannot update with empty filter",
2873
+ details: { indexName }
2874
+ });
2875
+ }
2876
+ if (!update.vector && !update.metadata) {
2877
+ throw new error.MastraError({
2878
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "NO_PAYLOAD"),
2879
+ domain: error.ErrorDomain.STORAGE,
2880
+ category: error.ErrorCategory.USER,
2881
+ text: "No updates provided",
2882
+ details: { indexName }
2883
+ });
2884
+ }
3136
2885
  try {
3137
2886
  if (!this.lanceClient) {
3138
2887
  throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
@@ -3140,21 +2889,6 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3140
2889
  if (!indexName) {
3141
2890
  throw new Error("indexName is required");
3142
2891
  }
3143
- if (!id) {
3144
- throw new Error("id is required");
3145
- }
3146
- } catch (err) {
3147
- throw new error.MastraError(
3148
- {
3149
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED_INVALID_ARGS",
3150
- domain: error.ErrorDomain.STORAGE,
3151
- category: error.ErrorCategory.USER,
3152
- details: { indexName, id }
3153
- },
3154
- err
3155
- );
3156
- }
3157
- try {
3158
2892
  const tables = await this.lanceClient.tableNames();
3159
2893
  for (const tableName of tables) {
3160
2894
  this.logger.debug("Checking table:" + tableName);
@@ -3164,39 +2898,66 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3164
2898
  const hasColumn = schema.fields.some((field) => field.name === indexName);
3165
2899
  if (hasColumn) {
3166
2900
  this.logger.debug(`Found column ${indexName} in table ${tableName}`);
3167
- const existingRecord = await table.query().where(`id = '${id}'`).select(schema.fields.map((field) => field.name)).limit(1).toArray();
3168
- if (existingRecord.length === 0) {
3169
- throw new Error(`Record with id '${id}' not found in table ${tableName}`);
2901
+ let whereClause;
2902
+ if ("id" in params && params.id) {
2903
+ whereClause = `id = '${params.id}'`;
2904
+ } else if ("filter" in params && params.filter) {
2905
+ const translator = new LanceFilterTranslator();
2906
+ const processFilterKeys = (filter) => {
2907
+ const processedFilter = {};
2908
+ Object.entries(filter).forEach(([key, value]) => {
2909
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
2910
+ Object.entries(value).forEach(([nestedKey, nestedValue]) => {
2911
+ processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
2912
+ });
2913
+ } else {
2914
+ processedFilter[`metadata_${key}`] = value;
2915
+ }
2916
+ });
2917
+ return processedFilter;
2918
+ };
2919
+ const prefixedFilter = processFilterKeys(params.filter);
2920
+ whereClause = translator.translate(prefixedFilter) || "";
2921
+ if (!whereClause) {
2922
+ throw new Error("Failed to translate filter to SQL");
2923
+ }
2924
+ } else {
2925
+ throw new Error("Either id or filter must be provided");
3170
2926
  }
3171
- const rowData = {
3172
- id
3173
- };
3174
- Object.entries(existingRecord[0]).forEach(([key, value]) => {
3175
- if (key !== "id" && key !== "_distance") {
3176
- if (key === indexName) {
3177
- if (!update.vector) {
3178
- if (Array.isArray(value)) {
3179
- rowData[key] = [...value];
3180
- } else if (typeof value === "object" && value !== null) {
3181
- rowData[key] = Array.from(value);
2927
+ const existingRecords = await table.query().where(whereClause).select(schema.fields.map((field) => field.name)).toArray();
2928
+ if (existingRecords.length === 0) {
2929
+ this.logger.info(`No records found matching criteria in table ${tableName}`);
2930
+ return;
2931
+ }
2932
+ const updatedRecords = existingRecords.map((record) => {
2933
+ const rowData = {};
2934
+ Object.entries(record).forEach(([key, value]) => {
2935
+ if (key !== "_distance") {
2936
+ if (key === indexName) {
2937
+ if (update.vector) {
2938
+ rowData[key] = update.vector;
3182
2939
  } else {
3183
- rowData[key] = value;
2940
+ if (Array.isArray(value)) {
2941
+ rowData[key] = [...value];
2942
+ } else if (typeof value === "object" && value !== null) {
2943
+ rowData[key] = Array.from(value);
2944
+ } else {
2945
+ rowData[key] = value;
2946
+ }
3184
2947
  }
2948
+ } else {
2949
+ rowData[key] = value;
3185
2950
  }
3186
- } else {
3187
- rowData[key] = value;
3188
2951
  }
2952
+ });
2953
+ if (update.metadata) {
2954
+ Object.entries(update.metadata).forEach(([key, value]) => {
2955
+ rowData[`metadata_${key}`] = value;
2956
+ });
3189
2957
  }
2958
+ return rowData;
3190
2959
  });
3191
- if (update.vector) {
3192
- rowData[indexName] = update.vector;
3193
- }
3194
- if (update.metadata) {
3195
- Object.entries(update.metadata).forEach(([key, value]) => {
3196
- rowData[`metadata_${key}`] = value;
3197
- });
3198
- }
3199
- await table.add([rowData], { mode: "overwrite" });
2960
+ await table.add(updatedRecords, { mode: "overwrite" });
3200
2961
  return;
3201
2962
  }
3202
2963
  } catch (err) {
@@ -3206,12 +2967,19 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3206
2967
  }
3207
2968
  throw new Error(`No table found with column/index '${indexName}'`);
3208
2969
  } catch (error$1) {
2970
+ if (error$1 instanceof error.MastraError) throw error$1;
3209
2971
  throw new error.MastraError(
3210
2972
  {
3211
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED",
2973
+ id: storage.createVectorErrorId("LANCE", "UPDATE_VECTOR", "FAILED"),
3212
2974
  domain: error.ErrorDomain.STORAGE,
3213
2975
  category: error.ErrorCategory.THIRD_PARTY,
3214
- details: { indexName, id, hasVector: !!update.vector, hasMetadata: !!update.metadata }
2976
+ details: {
2977
+ indexName,
2978
+ ..."id" in params && params.id && { id: params.id },
2979
+ ..."filter" in params && params.filter && { filter: JSON.stringify(params.filter) },
2980
+ hasVector: !!update.vector,
2981
+ hasMetadata: !!update.metadata
2982
+ }
3215
2983
  },
3216
2984
  error$1
3217
2985
  );
@@ -3231,10 +2999,13 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3231
2999
  } catch (err) {
3232
3000
  throw new error.MastraError(
3233
3001
  {
3234
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED_INVALID_ARGS",
3002
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTOR", "INVALID_ARGS"),
3235
3003
  domain: error.ErrorDomain.STORAGE,
3236
3004
  category: error.ErrorCategory.USER,
3237
- details: { indexName, id }
3005
+ details: {
3006
+ indexName,
3007
+ ...id && { id }
3008
+ }
3238
3009
  },
3239
3010
  err
3240
3011
  );
@@ -3261,10 +3032,13 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3261
3032
  } catch (error$1) {
3262
3033
  throw new error.MastraError(
3263
3034
  {
3264
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED",
3035
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTOR", "FAILED"),
3265
3036
  domain: error.ErrorDomain.STORAGE,
3266
3037
  category: error.ErrorCategory.THIRD_PARTY,
3267
- details: { indexName, id }
3038
+ details: {
3039
+ indexName,
3040
+ ...id && { id }
3041
+ }
3268
3042
  },
3269
3043
  error$1
3270
3044
  );
@@ -3295,9 +3069,115 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
3295
3069
  });
3296
3070
  return result;
3297
3071
  }
3072
+ async deleteVectors({ indexName, filter, ids }) {
3073
+ if (ids && filter) {
3074
+ throw new error.MastraError({
3075
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "MUTUALLY_EXCLUSIVE"),
3076
+ domain: error.ErrorDomain.STORAGE,
3077
+ category: error.ErrorCategory.USER,
3078
+ text: "ids and filter are mutually exclusive",
3079
+ details: { indexName }
3080
+ });
3081
+ }
3082
+ if (!ids && !filter) {
3083
+ throw new error.MastraError({
3084
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "NO_TARGET"),
3085
+ domain: error.ErrorDomain.STORAGE,
3086
+ category: error.ErrorCategory.USER,
3087
+ text: "Either filter or ids must be provided",
3088
+ details: { indexName }
3089
+ });
3090
+ }
3091
+ if (ids && ids.length === 0) {
3092
+ throw new error.MastraError({
3093
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "EMPTY_IDS"),
3094
+ domain: error.ErrorDomain.STORAGE,
3095
+ category: error.ErrorCategory.USER,
3096
+ text: "Cannot delete with empty ids array",
3097
+ details: { indexName }
3098
+ });
3099
+ }
3100
+ if (filter && Object.keys(filter).length === 0) {
3101
+ throw new error.MastraError({
3102
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "EMPTY_FILTER"),
3103
+ domain: error.ErrorDomain.STORAGE,
3104
+ category: error.ErrorCategory.USER,
3105
+ text: "Cannot delete with empty filter",
3106
+ details: { indexName }
3107
+ });
3108
+ }
3109
+ try {
3110
+ if (!this.lanceClient) {
3111
+ throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
3112
+ }
3113
+ if (!indexName) {
3114
+ throw new Error("indexName is required");
3115
+ }
3116
+ const tables = await this.lanceClient.tableNames();
3117
+ for (const tableName of tables) {
3118
+ this.logger.debug("Checking table:" + tableName);
3119
+ const table = await this.lanceClient.openTable(tableName);
3120
+ try {
3121
+ const schema = await table.schema();
3122
+ const hasColumn = schema.fields.some((field) => field.name === indexName);
3123
+ if (hasColumn) {
3124
+ this.logger.debug(`Found column ${indexName} in table ${tableName}`);
3125
+ if (ids) {
3126
+ const idsConditions = ids.map((id) => `id = '${id}'`).join(" OR ");
3127
+ await table.delete(idsConditions);
3128
+ } else if (filter) {
3129
+ const translator = new LanceFilterTranslator();
3130
+ const processFilterKeys = (filter2) => {
3131
+ const processedFilter = {};
3132
+ Object.entries(filter2).forEach(([key, value]) => {
3133
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3134
+ Object.entries(value).forEach(([nestedKey, nestedValue]) => {
3135
+ processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
3136
+ });
3137
+ } else {
3138
+ processedFilter[`metadata_${key}`] = value;
3139
+ }
3140
+ });
3141
+ return processedFilter;
3142
+ };
3143
+ const prefixedFilter = processFilterKeys(filter);
3144
+ const whereClause = translator.translate(prefixedFilter);
3145
+ if (!whereClause) {
3146
+ throw new Error("Failed to translate filter to SQL");
3147
+ }
3148
+ await table.delete(whereClause);
3149
+ }
3150
+ return;
3151
+ }
3152
+ } catch (err) {
3153
+ this.logger.error(`Error checking schema for table ${tableName}:` + err);
3154
+ continue;
3155
+ }
3156
+ }
3157
+ throw new Error(`No table found with column/index '${indexName}'`);
3158
+ } catch (error$1) {
3159
+ if (error$1 instanceof error.MastraError) throw error$1;
3160
+ throw new error.MastraError(
3161
+ {
3162
+ id: storage.createVectorErrorId("LANCE", "DELETE_VECTORS", "FAILED"),
3163
+ domain: error.ErrorDomain.STORAGE,
3164
+ category: error.ErrorCategory.THIRD_PARTY,
3165
+ details: {
3166
+ indexName,
3167
+ ...filter && { filter: JSON.stringify(filter) },
3168
+ ...ids && { idsCount: ids.length }
3169
+ }
3170
+ },
3171
+ error$1
3172
+ );
3173
+ }
3174
+ }
3298
3175
  };
3299
3176
 
3300
3177
  exports.LanceStorage = LanceStorage;
3301
3178
  exports.LanceVectorStore = LanceVectorStore;
3179
+ exports.StoreMemoryLance = StoreMemoryLance;
3180
+ exports.StoreScoresLance = StoreScoresLance;
3181
+ exports.StoreWorkflowsLance = StoreWorkflowsLance;
3302
3182
  //# sourceMappingURL=index.cjs.map
3303
3183
  //# sourceMappingURL=index.cjs.map