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