@mastra/lance 0.0.0-share-agent-metadata-with-cloud-20250718123411 → 0.0.0-span-scorring-test-20251124132129

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 (41) hide show
  1. package/CHANGELOG.md +948 -2
  2. package/README.md +64 -7
  3. package/dist/index.cjs +1657 -695
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +3 -2
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +1658 -696
  8. package/dist/index.js.map +1 -0
  9. package/dist/storage/domains/memory/index.d.ts +79 -0
  10. package/dist/storage/domains/memory/index.d.ts.map +1 -0
  11. package/dist/storage/domains/operations/index.d.ts +40 -0
  12. package/dist/storage/domains/operations/index.d.ts.map +1 -0
  13. package/dist/storage/domains/scores/index.d.ts +50 -0
  14. package/dist/storage/domains/scores/index.d.ts.map +1 -0
  15. package/dist/storage/domains/utils.d.ts +10 -0
  16. package/dist/storage/domains/utils.d.ts.map +1 -0
  17. package/dist/storage/domains/workflows/index.d.ts +50 -0
  18. package/dist/storage/domains/workflows/index.d.ts.map +1 -0
  19. package/dist/storage/index.d.ts +211 -0
  20. package/dist/storage/index.d.ts.map +1 -0
  21. package/dist/vector/filter.d.ts +41 -0
  22. package/dist/vector/filter.d.ts.map +1 -0
  23. package/dist/vector/index.d.ts +88 -0
  24. package/dist/vector/index.d.ts.map +1 -0
  25. package/dist/vector/types.d.ts +15 -0
  26. package/dist/vector/types.d.ts.map +1 -0
  27. package/package.json +31 -12
  28. package/dist/_tsup-dts-rollup.d.cts +0 -409
  29. package/dist/_tsup-dts-rollup.d.ts +0 -409
  30. package/dist/index.d.cts +0 -2
  31. package/eslint.config.js +0 -6
  32. package/src/index.ts +0 -2
  33. package/src/storage/index.test.ts +0 -1336
  34. package/src/storage/index.ts +0 -1447
  35. package/src/vector/filter.test.ts +0 -295
  36. package/src/vector/filter.ts +0 -443
  37. package/src/vector/index.test.ts +0 -1493
  38. package/src/vector/index.ts +0 -941
  39. package/src/vector/types.ts +0 -16
  40. package/tsconfig.json +0 -5
  41. package/vitest.config.ts +0 -11
package/dist/index.cjs CHANGED
@@ -1,112 +1,859 @@
1
1
  'use strict';
2
2
 
3
3
  var lancedb = require('@lancedb/lancedb');
4
- var agent = require('@mastra/core/agent');
5
4
  var error = require('@mastra/core/error');
6
5
  var storage = require('@mastra/core/storage');
6
+ var agent = require('@mastra/core/agent');
7
7
  var apacheArrow = require('apache-arrow');
8
+ var evals = require('@mastra/core/evals');
8
9
  var vector = require('@mastra/core/vector');
9
10
  var filter = require('@mastra/core/vector/filter');
10
11
 
11
12
  // src/storage/index.ts
12
- var LanceStorage = class _LanceStorage extends storage.MastraStorage {
13
- lanceClient;
13
+ function getPrimaryKeys(tableName) {
14
+ let primaryId = ["id"];
15
+ if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
16
+ primaryId = ["workflow_name", "run_id"];
17
+ }
18
+ return primaryId;
19
+ }
20
+ function validateKeyTypes(keys, tableSchema) {
21
+ const fieldTypes = new Map(
22
+ tableSchema.fields.map((field) => [field.name, field.type?.toString().toLowerCase()])
23
+ );
24
+ for (const [key, value] of Object.entries(keys)) {
25
+ const fieldType = fieldTypes.get(key);
26
+ if (!fieldType) {
27
+ throw new Error(`Field '${key}' does not exist in table schema`);
28
+ }
29
+ if (value !== null) {
30
+ if ((fieldType.includes("int") || fieldType.includes("bigint")) && typeof value !== "number") {
31
+ throw new Error(`Expected numeric value for field '${key}', got ${typeof value}`);
32
+ }
33
+ if (fieldType.includes("utf8") && typeof value !== "string") {
34
+ throw new Error(`Expected string value for field '${key}', got ${typeof value}`);
35
+ }
36
+ if (fieldType.includes("timestamp") && !(value instanceof Date) && typeof value !== "string") {
37
+ throw new Error(`Expected Date or string value for field '${key}', got ${typeof value}`);
38
+ }
39
+ }
40
+ }
41
+ }
42
+ function processResultWithTypeConversion(rawResult, tableSchema) {
43
+ const fieldTypeMap = /* @__PURE__ */ new Map();
44
+ tableSchema.fields.forEach((field) => {
45
+ const fieldName = field.name;
46
+ const fieldTypeStr = field.type.toString().toLowerCase();
47
+ fieldTypeMap.set(fieldName, fieldTypeStr);
48
+ });
49
+ if (Array.isArray(rawResult)) {
50
+ return rawResult.map((item) => processResultWithTypeConversion(item, tableSchema));
51
+ }
52
+ const processedResult = { ...rawResult };
53
+ for (const key in processedResult) {
54
+ const fieldTypeStr = fieldTypeMap.get(key);
55
+ if (!fieldTypeStr) continue;
56
+ if (typeof processedResult[key] === "string") {
57
+ if (fieldTypeStr.includes("int32") || fieldTypeStr.includes("float32")) {
58
+ if (!isNaN(Number(processedResult[key]))) {
59
+ processedResult[key] = Number(processedResult[key]);
60
+ }
61
+ } else if (fieldTypeStr.includes("int64")) {
62
+ processedResult[key] = Number(processedResult[key]);
63
+ } else if (fieldTypeStr.includes("utf8") && key !== "id") {
64
+ try {
65
+ const parsed = JSON.parse(processedResult[key]);
66
+ if (typeof parsed === "object") {
67
+ processedResult[key] = JSON.parse(processedResult[key]);
68
+ }
69
+ } catch {
70
+ }
71
+ }
72
+ } else if (typeof processedResult[key] === "bigint") {
73
+ processedResult[key] = Number(processedResult[key]);
74
+ } else if (fieldTypeStr.includes("float64") && ["createdAt", "updatedAt"].includes(key)) {
75
+ processedResult[key] = new Date(processedResult[key]);
76
+ }
77
+ }
78
+ return processedResult;
79
+ }
80
+ async function getTableSchema({
81
+ tableName,
82
+ client
83
+ }) {
84
+ try {
85
+ if (!client) {
86
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
87
+ }
88
+ if (!tableName) {
89
+ throw new Error("tableName is required for getTableSchema.");
90
+ }
91
+ } catch (validationError) {
92
+ throw new error.MastraError(
93
+ {
94
+ id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_INVALID_ARGS",
95
+ domain: error.ErrorDomain.STORAGE,
96
+ category: error.ErrorCategory.USER,
97
+ text: validationError.message,
98
+ details: { tableName }
99
+ },
100
+ validationError
101
+ );
102
+ }
103
+ try {
104
+ const table = await client.openTable(tableName);
105
+ const rawSchema = await table.schema();
106
+ const fields = rawSchema.fields;
107
+ return {
108
+ fields,
109
+ metadata: /* @__PURE__ */ new Map(),
110
+ get names() {
111
+ return fields.map((field) => field.name);
112
+ }
113
+ };
114
+ } catch (error$1) {
115
+ throw new error.MastraError(
116
+ {
117
+ id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_FAILED",
118
+ domain: error.ErrorDomain.STORAGE,
119
+ category: error.ErrorCategory.THIRD_PARTY,
120
+ details: { tableName }
121
+ },
122
+ error$1
123
+ );
124
+ }
125
+ }
126
+
127
+ // src/storage/domains/memory/index.ts
128
+ var StoreMemoryLance = class extends storage.MemoryStorage {
129
+ client;
130
+ operations;
131
+ constructor({ client, operations }) {
132
+ super();
133
+ this.client = client;
134
+ this.operations = operations;
135
+ }
136
+ // Utility to escape single quotes in SQL strings
137
+ escapeSql(str) {
138
+ return str.replace(/'/g, "''");
139
+ }
140
+ async getThreadById({ threadId }) {
141
+ try {
142
+ const thread = await this.operations.load({ tableName: storage.TABLE_THREADS, keys: { id: threadId } });
143
+ if (!thread) {
144
+ return null;
145
+ }
146
+ return {
147
+ ...thread,
148
+ createdAt: new Date(thread.createdAt),
149
+ updatedAt: new Date(thread.updatedAt)
150
+ };
151
+ } catch (error$1) {
152
+ throw new error.MastraError(
153
+ {
154
+ id: "LANCE_STORE_GET_THREAD_BY_ID_FAILED",
155
+ domain: error.ErrorDomain.STORAGE,
156
+ category: error.ErrorCategory.THIRD_PARTY
157
+ },
158
+ error$1
159
+ );
160
+ }
161
+ }
14
162
  /**
15
- * Creates a new instance of LanceStorage
16
- * @param uri The URI to connect to LanceDB
17
- * @param options connection options
18
- *
19
- * Usage:
20
- *
21
- * Connect to a local database
22
- * ```ts
23
- * const store = await LanceStorage.create('/path/to/db');
24
- * ```
25
- *
26
- * Connect to a LanceDB cloud database
27
- * ```ts
28
- * const store = await LanceStorage.create('db://host:port');
29
- * ```
30
- *
31
- * Connect to a cloud database
32
- * ```ts
33
- * const store = await LanceStorage.create('s3://bucket/db', { storageOptions: { timeout: '60s' } });
34
- * ```
163
+ * Saves a thread to the database. This function doesn't overwrite existing threads.
164
+ * @param thread - The thread to save
165
+ * @returns The saved thread
35
166
  */
36
- static async create(name, uri, options) {
37
- const instance = new _LanceStorage(name);
167
+ async saveThread({ thread }) {
38
168
  try {
39
- instance.lanceClient = await lancedb.connect(uri, options);
40
- return instance;
41
- } catch (e) {
169
+ const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
170
+ const table = await this.client.openTable(storage.TABLE_THREADS);
171
+ await table.add([record], { mode: "append" });
172
+ return thread;
173
+ } catch (error$1) {
42
174
  throw new error.MastraError(
43
175
  {
44
- id: "STORAGE_LANCE_STORAGE_CONNECT_FAILED",
176
+ id: "LANCE_STORE_SAVE_THREAD_FAILED",
177
+ domain: error.ErrorDomain.STORAGE,
178
+ category: error.ErrorCategory.THIRD_PARTY
179
+ },
180
+ error$1
181
+ );
182
+ }
183
+ }
184
+ async updateThread({
185
+ id,
186
+ title,
187
+ metadata
188
+ }) {
189
+ const maxRetries = 5;
190
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
191
+ try {
192
+ const current = await this.getThreadById({ threadId: id });
193
+ if (!current) {
194
+ throw new Error(`Thread with id ${id} not found`);
195
+ }
196
+ const mergedMetadata = { ...current.metadata, ...metadata };
197
+ const record = {
198
+ id,
199
+ title,
200
+ metadata: JSON.stringify(mergedMetadata),
201
+ updatedAt: (/* @__PURE__ */ new Date()).getTime()
202
+ };
203
+ const table = await this.client.openTable(storage.TABLE_THREADS);
204
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
205
+ const updatedThread = await this.getThreadById({ threadId: id });
206
+ if (!updatedThread) {
207
+ throw new Error(`Failed to retrieve updated thread ${id}`);
208
+ }
209
+ return updatedThread;
210
+ } catch (error$1) {
211
+ if (error$1.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
212
+ const delay = Math.pow(2, attempt) * 10;
213
+ await new Promise((resolve) => setTimeout(resolve, delay));
214
+ continue;
215
+ }
216
+ throw new error.MastraError(
217
+ {
218
+ id: "LANCE_STORE_UPDATE_THREAD_FAILED",
219
+ domain: error.ErrorDomain.STORAGE,
220
+ category: error.ErrorCategory.THIRD_PARTY
221
+ },
222
+ error$1
223
+ );
224
+ }
225
+ }
226
+ throw new error.MastraError(
227
+ {
228
+ id: "LANCE_STORE_UPDATE_THREAD_FAILED",
229
+ domain: error.ErrorDomain.STORAGE,
230
+ category: error.ErrorCategory.THIRD_PARTY
231
+ },
232
+ new Error("All retries exhausted")
233
+ );
234
+ }
235
+ async deleteThread({ threadId }) {
236
+ try {
237
+ const table = await this.client.openTable(storage.TABLE_THREADS);
238
+ await table.delete(`id = '${threadId}'`);
239
+ const messagesTable = await this.client.openTable(storage.TABLE_MESSAGES);
240
+ await messagesTable.delete(`thread_id = '${threadId}'`);
241
+ } catch (error$1) {
242
+ throw new error.MastraError(
243
+ {
244
+ id: "LANCE_STORE_DELETE_THREAD_FAILED",
245
+ domain: error.ErrorDomain.STORAGE,
246
+ category: error.ErrorCategory.THIRD_PARTY
247
+ },
248
+ error$1
249
+ );
250
+ }
251
+ }
252
+ normalizeMessage(message) {
253
+ const { thread_id, ...rest } = message;
254
+ return {
255
+ ...rest,
256
+ threadId: thread_id,
257
+ content: typeof message.content === "string" ? (() => {
258
+ try {
259
+ return JSON.parse(message.content);
260
+ } catch {
261
+ return message.content;
262
+ }
263
+ })() : message.content
264
+ };
265
+ }
266
+ async listMessagesById({ messageIds }) {
267
+ if (messageIds.length === 0) return { messages: [] };
268
+ try {
269
+ const table = await this.client.openTable(storage.TABLE_MESSAGES);
270
+ const quotedIds = messageIds.map((id) => `'${id}'`).join(", ");
271
+ const allRecords = await table.query().where(`id IN (${quotedIds})`).toArray();
272
+ const messages = processResultWithTypeConversion(
273
+ allRecords,
274
+ await getTableSchema({ tableName: storage.TABLE_MESSAGES, client: this.client })
275
+ );
276
+ const list = new agent.MessageList().add(
277
+ messages.map(this.normalizeMessage),
278
+ "memory"
279
+ );
280
+ return { messages: list.get.all.db() };
281
+ } catch (error$1) {
282
+ throw new error.MastraError(
283
+ {
284
+ id: "LANCE_STORE_LIST_MESSAGES_BY_ID_FAILED",
45
285
  domain: error.ErrorDomain.STORAGE,
46
286
  category: error.ErrorCategory.THIRD_PARTY,
47
- text: `Failed to connect to LanceDB: ${e.message || e}`,
48
- details: { uri, optionsProvided: !!options }
287
+ details: {
288
+ messageIds: JSON.stringify(messageIds)
289
+ }
49
290
  },
50
- e
291
+ error$1
292
+ );
293
+ }
294
+ }
295
+ async listMessages(args) {
296
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
297
+ if (!threadId.trim()) {
298
+ throw new error.MastraError(
299
+ {
300
+ id: "STORAGE_LANCE_LIST_MESSAGES_INVALID_THREAD_ID",
301
+ domain: error.ErrorDomain.STORAGE,
302
+ category: error.ErrorCategory.THIRD_PARTY,
303
+ details: { threadId }
304
+ },
305
+ new Error("threadId must be a non-empty string")
306
+ );
307
+ }
308
+ const perPage = storage.normalizePerPage(perPageInput, 40);
309
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
310
+ try {
311
+ if (page < 0) {
312
+ throw new error.MastraError(
313
+ {
314
+ id: "STORAGE_LANCE_LIST_MESSAGES_INVALID_PAGE",
315
+ domain: error.ErrorDomain.STORAGE,
316
+ category: error.ErrorCategory.USER,
317
+ details: { page }
318
+ },
319
+ new Error("page must be >= 0")
320
+ );
321
+ }
322
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
323
+ const table = await this.client.openTable(storage.TABLE_MESSAGES);
324
+ const conditions = [`thread_id = '${this.escapeSql(threadId)}'`];
325
+ if (resourceId) {
326
+ conditions.push(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
327
+ }
328
+ if (filter?.dateRange?.start) {
329
+ const startTime = filter.dateRange.start instanceof Date ? filter.dateRange.start.getTime() : new Date(filter.dateRange.start).getTime();
330
+ conditions.push(`\`createdAt\` >= ${startTime}`);
331
+ }
332
+ if (filter?.dateRange?.end) {
333
+ const endTime = filter.dateRange.end instanceof Date ? filter.dateRange.end.getTime() : new Date(filter.dateRange.end).getTime();
334
+ conditions.push(`\`createdAt\` <= ${endTime}`);
335
+ }
336
+ const whereClause = conditions.join(" AND ");
337
+ const total = await table.countRows(whereClause);
338
+ const query = table.query().where(whereClause);
339
+ let allRecords = await query.toArray();
340
+ allRecords.sort((a, b) => {
341
+ const aValue = field === "createdAt" ? a.createdAt : a[field];
342
+ const bValue = field === "createdAt" ? b.createdAt : b[field];
343
+ if (aValue == null && bValue == null) return 0;
344
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
345
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
346
+ if (typeof aValue === "string" && typeof bValue === "string") {
347
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
348
+ }
349
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
350
+ });
351
+ const paginatedRecords = allRecords.slice(offset, offset + perPage);
352
+ const messages = paginatedRecords.map((row) => this.normalizeMessage(row));
353
+ if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
354
+ return {
355
+ messages: [],
356
+ total: 0,
357
+ page,
358
+ perPage: perPageForResponse,
359
+ hasMore: false
360
+ };
361
+ }
362
+ const messageIds = new Set(messages.map((m) => m.id));
363
+ if (include && include.length > 0) {
364
+ const threadIds = [...new Set(include.map((item) => item.threadId || threadId))];
365
+ const allThreadMessages = [];
366
+ for (const tid of threadIds) {
367
+ const threadQuery = table.query().where(`thread_id = '${tid}'`);
368
+ let threadRecords = await threadQuery.toArray();
369
+ allThreadMessages.push(...threadRecords);
370
+ }
371
+ allThreadMessages.sort((a, b) => a.createdAt - b.createdAt);
372
+ const contextMessages = this.processMessagesWithContext(allThreadMessages, include);
373
+ const includedMessages = contextMessages.map((row) => this.normalizeMessage(row));
374
+ for (const includeMsg of includedMessages) {
375
+ if (!messageIds.has(includeMsg.id)) {
376
+ messages.push(includeMsg);
377
+ messageIds.add(includeMsg.id);
378
+ }
379
+ }
380
+ }
381
+ const list = new agent.MessageList().add(messages, "memory");
382
+ let finalMessages = list.get.all.db();
383
+ finalMessages = finalMessages.sort((a, b) => {
384
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
385
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
386
+ if (aValue == null && bValue == null) return 0;
387
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
388
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
389
+ if (typeof aValue === "string" && typeof bValue === "string") {
390
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
391
+ }
392
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
393
+ });
394
+ const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
395
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
396
+ const fetchedAll = perPageInput === false || allThreadMessagesReturned;
397
+ const hasMore = !fetchedAll && offset + perPage < total;
398
+ return {
399
+ messages: finalMessages,
400
+ total,
401
+ page,
402
+ perPage: perPageForResponse,
403
+ hasMore
404
+ };
405
+ } catch (error$1) {
406
+ const mastraError = new error.MastraError(
407
+ {
408
+ id: "LANCE_STORE_LIST_MESSAGES_FAILED",
409
+ domain: error.ErrorDomain.STORAGE,
410
+ category: error.ErrorCategory.THIRD_PARTY,
411
+ details: {
412
+ threadId,
413
+ resourceId: resourceId ?? ""
414
+ }
415
+ },
416
+ error$1
417
+ );
418
+ this.logger?.error?.(mastraError.toString());
419
+ this.logger?.trackException?.(mastraError);
420
+ return {
421
+ messages: [],
422
+ total: 0,
423
+ page,
424
+ perPage: perPageForResponse,
425
+ hasMore: false
426
+ };
427
+ }
428
+ }
429
+ async saveMessages(args) {
430
+ try {
431
+ const { messages } = args;
432
+ if (messages.length === 0) {
433
+ return { messages: [] };
434
+ }
435
+ const threadId = messages[0]?.threadId;
436
+ if (!threadId) {
437
+ throw new Error("Thread ID is required");
438
+ }
439
+ for (const message of messages) {
440
+ if (!message.id) {
441
+ throw new Error("Message ID is required");
442
+ }
443
+ if (!message.threadId) {
444
+ throw new Error("Thread ID is required for all messages");
445
+ }
446
+ if (message.resourceId === null || message.resourceId === void 0) {
447
+ throw new Error("Resource ID cannot be null or undefined");
448
+ }
449
+ if (!message.content) {
450
+ throw new Error("Message content is required");
451
+ }
452
+ }
453
+ const transformedMessages = messages.map((message) => {
454
+ const { threadId: threadId2, type, ...rest } = message;
455
+ return {
456
+ ...rest,
457
+ thread_id: threadId2,
458
+ type: type ?? "v2",
459
+ content: JSON.stringify(message.content)
460
+ };
461
+ });
462
+ const table = await this.client.openTable(storage.TABLE_MESSAGES);
463
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
464
+ const threadsTable = await this.client.openTable(storage.TABLE_THREADS);
465
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
466
+ const updateRecord = { id: threadId, updatedAt: currentTime };
467
+ await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([updateRecord]);
468
+ const list = new agent.MessageList().add(messages, "memory");
469
+ return { messages: list.get.all.db() };
470
+ } catch (error$1) {
471
+ throw new error.MastraError(
472
+ {
473
+ id: "LANCE_STORE_SAVE_MESSAGES_FAILED",
474
+ domain: error.ErrorDomain.STORAGE,
475
+ category: error.ErrorCategory.THIRD_PARTY
476
+ },
477
+ error$1
51
478
  );
52
479
  }
53
480
  }
54
- getPrimaryKeys(tableName) {
55
- let primaryId = ["id"];
56
- if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
57
- primaryId = ["workflow_name", "run_id"];
58
- } else if (tableName === storage.TABLE_EVALS) {
59
- primaryId = ["agent_name", "metric_name", "run_id"];
481
+ async listThreadsByResourceId(args) {
482
+ try {
483
+ const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
484
+ const perPage = storage.normalizePerPage(perPageInput, 100);
485
+ if (page < 0) {
486
+ throw new error.MastraError(
487
+ {
488
+ id: "STORAGE_LANCE_LIST_THREADS_BY_RESOURCE_ID_INVALID_PAGE",
489
+ domain: error.ErrorDomain.STORAGE,
490
+ category: error.ErrorCategory.USER,
491
+ details: { page }
492
+ },
493
+ new Error("page must be >= 0")
494
+ );
495
+ }
496
+ const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
497
+ const { field, direction } = this.parseOrderBy(orderBy);
498
+ const table = await this.client.openTable(storage.TABLE_THREADS);
499
+ const total = await table.countRows(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
500
+ const query = table.query().where(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
501
+ const records = await query.toArray();
502
+ records.sort((a, b) => {
503
+ const aValue = ["createdAt", "updatedAt"].includes(field) ? new Date(a[field]).getTime() : a[field];
504
+ const bValue = ["createdAt", "updatedAt"].includes(field) ? new Date(b[field]).getTime() : b[field];
505
+ if (aValue == null && bValue == null) return 0;
506
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
507
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
508
+ if (typeof aValue === "string" && typeof bValue === "string") {
509
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
510
+ }
511
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
512
+ });
513
+ const paginatedRecords = records.slice(offset, offset + perPage);
514
+ const schema = await getTableSchema({ tableName: storage.TABLE_THREADS, client: this.client });
515
+ const threads = paginatedRecords.map(
516
+ (record) => processResultWithTypeConversion(record, schema)
517
+ );
518
+ return {
519
+ threads,
520
+ total,
521
+ page,
522
+ perPage: perPageForResponse,
523
+ hasMore: offset + perPage < total
524
+ };
525
+ } catch (error$1) {
526
+ throw new error.MastraError(
527
+ {
528
+ id: "LANCE_STORE_LIST_THREADS_BY_RESOURCE_ID_FAILED",
529
+ domain: error.ErrorDomain.STORAGE,
530
+ category: error.ErrorCategory.THIRD_PARTY
531
+ },
532
+ error$1
533
+ );
60
534
  }
61
- return primaryId;
62
535
  }
63
536
  /**
64
- * @internal
65
- * Private constructor to enforce using the create factory method
537
+ * Processes messages to include context messages based on withPreviousMessages and withNextMessages
538
+ * @param records - The sorted array of records to process
539
+ * @param include - The array of include specifications with context parameters
540
+ * @returns The processed array with context messages included
541
+ */
542
+ processMessagesWithContext(records, include) {
543
+ const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
544
+ if (messagesWithContext.length === 0) {
545
+ return records;
546
+ }
547
+ const messageIndexMap = /* @__PURE__ */ new Map();
548
+ records.forEach((message, index) => {
549
+ messageIndexMap.set(message.id, index);
550
+ });
551
+ const additionalIndices = /* @__PURE__ */ new Set();
552
+ for (const item of messagesWithContext) {
553
+ const messageIndex = messageIndexMap.get(item.id);
554
+ if (messageIndex !== void 0) {
555
+ if (item.withPreviousMessages) {
556
+ const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
557
+ for (let i = startIdx; i < messageIndex; i++) {
558
+ additionalIndices.add(i);
559
+ }
560
+ }
561
+ if (item.withNextMessages) {
562
+ const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
563
+ for (let i = messageIndex + 1; i <= endIdx; i++) {
564
+ additionalIndices.add(i);
565
+ }
566
+ }
567
+ }
568
+ }
569
+ if (additionalIndices.size === 0) {
570
+ return records;
571
+ }
572
+ const originalMatchIds = new Set(include.map((item) => item.id));
573
+ const allIndices = /* @__PURE__ */ new Set();
574
+ records.forEach((record, index) => {
575
+ if (originalMatchIds.has(record.id)) {
576
+ allIndices.add(index);
577
+ }
578
+ });
579
+ additionalIndices.forEach((index) => {
580
+ allIndices.add(index);
581
+ });
582
+ return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
583
+ }
584
+ /**
585
+ * Parse message data from LanceDB record format to MastraDBMessage format
66
586
  */
67
- constructor(name) {
68
- super({ name });
587
+ parseMessageData(data) {
588
+ const { thread_id, ...rest } = data;
589
+ return {
590
+ ...rest,
591
+ threadId: thread_id,
592
+ content: typeof data.content === "string" ? (() => {
593
+ try {
594
+ return JSON.parse(data.content);
595
+ } catch {
596
+ return data.content;
597
+ }
598
+ })() : data.content,
599
+ createdAt: new Date(data.createdAt),
600
+ updatedAt: new Date(data.updatedAt)
601
+ };
69
602
  }
70
- async createTable({
71
- tableName,
72
- schema
73
- }) {
603
+ async updateMessages(args) {
604
+ const { messages } = args;
605
+ this.logger.debug("Updating messages", { count: messages.length });
606
+ if (!messages.length) {
607
+ return [];
608
+ }
609
+ const updatedMessages = [];
610
+ const affectedThreadIds = /* @__PURE__ */ new Set();
74
611
  try {
75
- if (!this.lanceClient) {
76
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
612
+ for (const updateData of messages) {
613
+ const { id, ...updates } = updateData;
614
+ const existingMessage = await this.operations.load({ tableName: storage.TABLE_MESSAGES, keys: { id } });
615
+ if (!existingMessage) {
616
+ this.logger.warn("Message not found for update", { id });
617
+ continue;
618
+ }
619
+ const existingMsg = this.parseMessageData(existingMessage);
620
+ const originalThreadId = existingMsg.threadId;
621
+ affectedThreadIds.add(originalThreadId);
622
+ const updatePayload = {};
623
+ if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
624
+ if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
625
+ if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
626
+ if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
627
+ updatePayload.thread_id = updates.threadId;
628
+ affectedThreadIds.add(updates.threadId);
629
+ }
630
+ if (updates.content) {
631
+ const existingContent = existingMsg.content;
632
+ let newContent = { ...existingContent };
633
+ if (updates.content.metadata !== void 0) {
634
+ newContent.metadata = {
635
+ ...existingContent.metadata || {},
636
+ ...updates.content.metadata || {}
637
+ };
638
+ }
639
+ if (updates.content.content !== void 0) {
640
+ newContent.content = updates.content.content;
641
+ }
642
+ if ("parts" in updates.content && updates.content.parts !== void 0) {
643
+ newContent.parts = updates.content.parts;
644
+ }
645
+ updatePayload.content = JSON.stringify(newContent);
646
+ }
647
+ await this.operations.insert({ tableName: storage.TABLE_MESSAGES, record: { id, ...updatePayload } });
648
+ const updatedMessage = await this.operations.load({ tableName: storage.TABLE_MESSAGES, keys: { id } });
649
+ if (updatedMessage) {
650
+ updatedMessages.push(this.parseMessageData(updatedMessage));
651
+ }
77
652
  }
78
- if (!tableName) {
79
- throw new Error("tableName is required for createTable.");
653
+ for (const threadId of affectedThreadIds) {
654
+ await this.operations.insert({
655
+ tableName: storage.TABLE_THREADS,
656
+ record: { id: threadId, updatedAt: Date.now() }
657
+ });
658
+ }
659
+ return updatedMessages;
660
+ } catch (error$1) {
661
+ throw new error.MastraError(
662
+ {
663
+ id: "LANCE_STORE_UPDATE_MESSAGES_FAILED",
664
+ domain: error.ErrorDomain.STORAGE,
665
+ category: error.ErrorCategory.THIRD_PARTY,
666
+ details: { count: messages.length }
667
+ },
668
+ error$1
669
+ );
670
+ }
671
+ }
672
+ async getResourceById({ resourceId }) {
673
+ try {
674
+ const resource = await this.operations.load({ tableName: storage.TABLE_RESOURCES, keys: { id: resourceId } });
675
+ if (!resource) {
676
+ return null;
677
+ }
678
+ let createdAt;
679
+ let updatedAt;
680
+ try {
681
+ if (resource.createdAt instanceof Date) {
682
+ createdAt = resource.createdAt;
683
+ } else if (typeof resource.createdAt === "string") {
684
+ createdAt = new Date(resource.createdAt);
685
+ } else if (typeof resource.createdAt === "number") {
686
+ createdAt = new Date(resource.createdAt);
687
+ } else {
688
+ createdAt = /* @__PURE__ */ new Date();
689
+ }
690
+ if (isNaN(createdAt.getTime())) {
691
+ createdAt = /* @__PURE__ */ new Date();
692
+ }
693
+ } catch {
694
+ createdAt = /* @__PURE__ */ new Date();
80
695
  }
81
- if (!schema) {
82
- throw new Error("schema is required for createTable.");
696
+ try {
697
+ if (resource.updatedAt instanceof Date) {
698
+ updatedAt = resource.updatedAt;
699
+ } else if (typeof resource.updatedAt === "string") {
700
+ updatedAt = new Date(resource.updatedAt);
701
+ } else if (typeof resource.updatedAt === "number") {
702
+ updatedAt = new Date(resource.updatedAt);
703
+ } else {
704
+ updatedAt = /* @__PURE__ */ new Date();
705
+ }
706
+ if (isNaN(updatedAt.getTime())) {
707
+ updatedAt = /* @__PURE__ */ new Date();
708
+ }
709
+ } catch {
710
+ updatedAt = /* @__PURE__ */ new Date();
711
+ }
712
+ let workingMemory = resource.workingMemory;
713
+ if (workingMemory === null || workingMemory === void 0) {
714
+ workingMemory = void 0;
715
+ } else if (workingMemory === "") {
716
+ workingMemory = "";
717
+ } else if (typeof workingMemory === "object") {
718
+ workingMemory = JSON.stringify(workingMemory);
719
+ }
720
+ let metadata = resource.metadata;
721
+ if (metadata === "" || metadata === null || metadata === void 0) {
722
+ metadata = void 0;
723
+ } else if (typeof metadata === "string") {
724
+ try {
725
+ metadata = JSON.parse(metadata);
726
+ } catch {
727
+ metadata = metadata;
728
+ }
83
729
  }
730
+ return {
731
+ ...resource,
732
+ createdAt,
733
+ updatedAt,
734
+ workingMemory,
735
+ metadata
736
+ };
84
737
  } catch (error$1) {
85
738
  throw new error.MastraError(
86
739
  {
87
- id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_INVALID_ARGS",
740
+ id: "LANCE_STORE_GET_RESOURCE_BY_ID_FAILED",
88
741
  domain: error.ErrorDomain.STORAGE,
89
- category: error.ErrorCategory.USER,
90
- details: { tableName }
742
+ category: error.ErrorCategory.THIRD_PARTY
91
743
  },
92
744
  error$1
93
745
  );
94
746
  }
747
+ }
748
+ async saveResource({ resource }) {
95
749
  try {
96
- const arrowSchema = this.translateSchema(schema);
97
- await this.lanceClient.createEmptyTable(tableName, arrowSchema);
750
+ const record = {
751
+ ...resource,
752
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : "",
753
+ createdAt: resource.createdAt.getTime(),
754
+ // Store as timestamp (milliseconds)
755
+ updatedAt: resource.updatedAt.getTime()
756
+ // Store as timestamp (milliseconds)
757
+ };
758
+ const table = await this.client.openTable(storage.TABLE_RESOURCES);
759
+ await table.add([record], { mode: "append" });
760
+ return resource;
98
761
  } catch (error$1) {
99
762
  throw new error.MastraError(
100
763
  {
101
- id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_FAILED",
764
+ id: "LANCE_STORE_SAVE_RESOURCE_FAILED",
102
765
  domain: error.ErrorDomain.STORAGE,
103
- category: error.ErrorCategory.THIRD_PARTY,
104
- details: { tableName }
766
+ category: error.ErrorCategory.THIRD_PARTY
105
767
  },
106
768
  error$1
107
769
  );
108
770
  }
109
771
  }
772
+ async updateResource({
773
+ resourceId,
774
+ workingMemory,
775
+ metadata
776
+ }) {
777
+ const maxRetries = 3;
778
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
779
+ try {
780
+ const existingResource = await this.getResourceById({ resourceId });
781
+ if (!existingResource) {
782
+ const newResource = {
783
+ id: resourceId,
784
+ workingMemory,
785
+ metadata: metadata || {},
786
+ createdAt: /* @__PURE__ */ new Date(),
787
+ updatedAt: /* @__PURE__ */ new Date()
788
+ };
789
+ return this.saveResource({ resource: newResource });
790
+ }
791
+ const updatedResource = {
792
+ ...existingResource,
793
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
794
+ metadata: {
795
+ ...existingResource.metadata,
796
+ ...metadata
797
+ },
798
+ updatedAt: /* @__PURE__ */ new Date()
799
+ };
800
+ const record = {
801
+ id: resourceId,
802
+ workingMemory: updatedResource.workingMemory || "",
803
+ metadata: updatedResource.metadata ? JSON.stringify(updatedResource.metadata) : "",
804
+ updatedAt: updatedResource.updatedAt.getTime()
805
+ // Store as timestamp (milliseconds)
806
+ };
807
+ const table = await this.client.openTable(storage.TABLE_RESOURCES);
808
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
809
+ return updatedResource;
810
+ } catch (error$1) {
811
+ if (error$1.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
812
+ const delay = Math.pow(2, attempt) * 10;
813
+ await new Promise((resolve) => setTimeout(resolve, delay));
814
+ continue;
815
+ }
816
+ throw new error.MastraError(
817
+ {
818
+ id: "LANCE_STORE_UPDATE_RESOURCE_FAILED",
819
+ domain: error.ErrorDomain.STORAGE,
820
+ category: error.ErrorCategory.THIRD_PARTY
821
+ },
822
+ error$1
823
+ );
824
+ }
825
+ }
826
+ throw new Error("Unexpected end of retry loop");
827
+ }
828
+ };
829
+ var StoreOperationsLance = class extends storage.StoreOperations {
830
+ client;
831
+ constructor({ client }) {
832
+ super();
833
+ this.client = client;
834
+ }
835
+ getDefaultValue(type) {
836
+ switch (type) {
837
+ case "text":
838
+ return "''";
839
+ case "timestamp":
840
+ return "CURRENT_TIMESTAMP";
841
+ case "integer":
842
+ case "bigint":
843
+ return "0";
844
+ case "jsonb":
845
+ return "'{}'";
846
+ case "uuid":
847
+ return "''";
848
+ default:
849
+ return super.getDefaultValue(type);
850
+ }
851
+ }
852
+ async hasColumn(tableName, columnName) {
853
+ const table = await this.client.openTable(tableName);
854
+ const schema = await table.schema();
855
+ return schema.fields.some((field) => field.name === columnName);
856
+ }
110
857
  translateSchema(schema) {
111
858
  const fields = Object.entries(schema).map(([name, column]) => {
112
859
  let arrowType;
@@ -142,40 +889,42 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
142
889
  });
143
890
  return new apacheArrow.Schema(fields);
144
891
  }
145
- /**
146
- * Drop a table if it exists
147
- * @param tableName Name of the table to drop
148
- */
149
- async dropTable(tableName) {
892
+ async createTable({
893
+ tableName,
894
+ schema
895
+ }) {
150
896
  try {
151
- if (!this.lanceClient) {
897
+ if (!this.client) {
152
898
  throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
153
899
  }
154
900
  if (!tableName) {
155
- throw new Error("tableName is required for dropTable.");
901
+ throw new Error("tableName is required for createTable.");
156
902
  }
157
- } catch (validationError) {
903
+ if (!schema) {
904
+ throw new Error("schema is required for createTable.");
905
+ }
906
+ } catch (error$1) {
158
907
  throw new error.MastraError(
159
908
  {
160
- id: "STORAGE_LANCE_STORAGE_DROP_TABLE_INVALID_ARGS",
909
+ id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_INVALID_ARGS",
161
910
  domain: error.ErrorDomain.STORAGE,
162
911
  category: error.ErrorCategory.USER,
163
- text: validationError.message,
164
912
  details: { tableName }
165
913
  },
166
- validationError
914
+ error$1
167
915
  );
168
916
  }
169
917
  try {
170
- await this.lanceClient.dropTable(tableName);
918
+ const arrowSchema = this.translateSchema(schema);
919
+ await this.client.createEmptyTable(tableName, arrowSchema);
171
920
  } catch (error$1) {
172
- if (error$1.toString().includes("was not found") || error$1.message?.includes("Table not found")) {
173
- this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
921
+ if (error$1.message?.includes("already exists")) {
922
+ this.logger.debug(`Table '${tableName}' already exists, skipping create`);
174
923
  return;
175
924
  }
176
925
  throw new error.MastraError(
177
926
  {
178
- id: "STORAGE_LANCE_STORAGE_DROP_TABLE_FAILED",
927
+ id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_FAILED",
179
928
  domain: error.ErrorDomain.STORAGE,
180
929
  category: error.ErrorCategory.THIRD_PARTY,
181
930
  details: { tableName }
@@ -184,23 +933,18 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
184
933
  );
185
934
  }
186
935
  }
187
- /**
188
- * Get table schema
189
- * @param tableName Name of the table
190
- * @returns Table schema
191
- */
192
- async getTableSchema(tableName) {
936
+ async dropTable({ tableName }) {
193
937
  try {
194
- if (!this.lanceClient) {
938
+ if (!this.client) {
195
939
  throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
196
940
  }
197
941
  if (!tableName) {
198
- throw new Error("tableName is required for getTableSchema.");
942
+ throw new Error("tableName is required for dropTable.");
199
943
  }
200
944
  } catch (validationError) {
201
945
  throw new error.MastraError(
202
946
  {
203
- id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_INVALID_ARGS",
947
+ id: "STORAGE_LANCE_STORAGE_DROP_TABLE_INVALID_ARGS",
204
948
  domain: error.ErrorDomain.STORAGE,
205
949
  category: error.ErrorCategory.USER,
206
950
  text: validationError.message,
@@ -210,20 +954,15 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
210
954
  );
211
955
  }
212
956
  try {
213
- const table = await this.lanceClient.openTable(tableName);
214
- const rawSchema = await table.schema();
215
- const fields = rawSchema.fields;
216
- return {
217
- fields,
218
- metadata: /* @__PURE__ */ new Map(),
219
- get names() {
220
- return fields.map((field) => field.name);
221
- }
222
- };
957
+ await this.client.dropTable(tableName);
223
958
  } catch (error$1) {
959
+ if (error$1.toString().includes("was not found") || error$1.message?.includes("Table not found")) {
960
+ this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
961
+ return;
962
+ }
224
963
  throw new error.MastraError(
225
964
  {
226
- id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_FAILED",
965
+ id: "STORAGE_LANCE_STORAGE_DROP_TABLE_FAILED",
227
966
  domain: error.ErrorDomain.STORAGE,
228
967
  category: error.ErrorCategory.THIRD_PARTY,
229
968
  details: { tableName }
@@ -232,36 +971,13 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
232
971
  );
233
972
  }
234
973
  }
235
- getDefaultValue(type) {
236
- switch (type) {
237
- case "text":
238
- return "''";
239
- case "timestamp":
240
- return "CURRENT_TIMESTAMP";
241
- case "integer":
242
- case "bigint":
243
- return "0";
244
- case "jsonb":
245
- return "'{}'";
246
- case "uuid":
247
- return "''";
248
- default:
249
- return super.getDefaultValue(type);
250
- }
251
- }
252
- /**
253
- * Alters table schema to add columns if they don't exist
254
- * @param tableName Name of the table
255
- * @param schema Schema of the table
256
- * @param ifNotExists Array of column names to add if they don't exist
257
- */
258
974
  async alterTable({
259
975
  tableName,
260
976
  schema,
261
977
  ifNotExists
262
978
  }) {
263
979
  try {
264
- if (!this.lanceClient) {
980
+ if (!this.client) {
265
981
  throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
266
982
  }
267
983
  if (!tableName) {
@@ -287,7 +1003,7 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
287
1003
  );
288
1004
  }
289
1005
  try {
290
- const table = await this.lanceClient.openTable(tableName);
1006
+ const table = await this.client.openTable(tableName);
291
1007
  const currentSchema = await table.schema();
292
1008
  const existingFields = new Set(currentSchema.fields.map((f) => f.name));
293
1009
  const typeMap = {
@@ -323,7 +1039,7 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
323
1039
  }
324
1040
  async clearTable({ tableName }) {
325
1041
  try {
326
- if (!this.lanceClient) {
1042
+ if (!this.client) {
327
1043
  throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
328
1044
  }
329
1045
  if (!tableName) {
@@ -342,7 +1058,7 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
342
1058
  );
343
1059
  }
344
1060
  try {
345
- const table = await this.lanceClient.openTable(tableName);
1061
+ const table = await this.client.openTable(tableName);
346
1062
  await table.delete("1=1");
347
1063
  } catch (error$1) {
348
1064
  throw new error.MastraError(
@@ -356,14 +1072,9 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
356
1072
  );
357
1073
  }
358
1074
  }
359
- /**
360
- * Insert a single record into a table. This function overwrites the existing record if it exists. Use this function for inserting records into tables with custom schemas.
361
- * @param tableName The name of the table to insert into.
362
- * @param record The record to insert.
363
- */
364
1075
  async insert({ tableName, record }) {
365
1076
  try {
366
- if (!this.lanceClient) {
1077
+ if (!this.client) {
367
1078
  throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
368
1079
  }
369
1080
  if (!tableName) {
@@ -385,8 +1096,8 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
385
1096
  );
386
1097
  }
387
1098
  try {
388
- const table = await this.lanceClient.openTable(tableName);
389
- const primaryId = this.getPrimaryKeys(tableName);
1099
+ const table = await this.client.openTable(tableName);
1100
+ const primaryId = getPrimaryKeys(tableName);
390
1101
  const processedRecord = { ...record };
391
1102
  for (const key in processedRecord) {
392
1103
  if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
@@ -394,6 +1105,7 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
394
1105
  processedRecord[key] = JSON.stringify(processedRecord[key]);
395
1106
  }
396
1107
  }
1108
+ console.info(await table.schema());
397
1109
  await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
398
1110
  } catch (error$1) {
399
1111
  throw new error.MastraError(
@@ -407,14 +1119,9 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
407
1119
  );
408
1120
  }
409
1121
  }
410
- /**
411
- * Insert multiple records into a table. This function overwrites the existing records if they exist. Use this function for inserting records into tables with custom schemas.
412
- * @param tableName The name of the table to insert into.
413
- * @param records The records to insert.
414
- */
415
1122
  async batchInsert({ tableName, records }) {
416
1123
  try {
417
- if (!this.lanceClient) {
1124
+ if (!this.client) {
418
1125
  throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
419
1126
  }
420
1127
  if (!tableName) {
@@ -436,8 +1143,8 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
436
1143
  );
437
1144
  }
438
1145
  try {
439
- const table = await this.lanceClient.openTable(tableName);
440
- const primaryId = this.getPrimaryKeys(tableName);
1146
+ const table = await this.client.openTable(tableName);
1147
+ const primaryId = getPrimaryKeys(tableName);
441
1148
  const processedRecords = records.map((record) => {
442
1149
  const processedRecord = { ...record };
443
1150
  for (const key in processedRecord) {
@@ -461,16 +1168,9 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
461
1168
  );
462
1169
  }
463
1170
  }
464
- /**
465
- * Load a record from the database by its key(s)
466
- * @param tableName The name of the table to query
467
- * @param keys Record of key-value pairs to use for lookup
468
- * @throws Error if invalid types are provided for keys
469
- * @returns The loaded record with proper type conversions, or null if not found
470
- */
471
1171
  async load({ tableName, keys }) {
472
1172
  try {
473
- if (!this.lanceClient) {
1173
+ if (!this.client) {
474
1174
  throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
475
1175
  }
476
1176
  if (!tableName) {
@@ -492,11 +1192,11 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
492
1192
  );
493
1193
  }
494
1194
  try {
495
- const table = await this.lanceClient.openTable(tableName);
496
- const tableSchema = await this.getTableSchema(tableName);
1195
+ const table = await this.client.openTable(tableName);
1196
+ const tableSchema = await getTableSchema({ tableName, client: this.client });
497
1197
  const query = table.query();
498
1198
  if (Object.keys(keys).length > 0) {
499
- this.validateKeyTypes(keys, tableSchema);
1199
+ validateKeyTypes(keys, tableSchema);
500
1200
  const filterConditions = Object.entries(keys).map(([key, value]) => {
501
1201
  const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
502
1202
  const quotedKey = isCamelCase ? `\`${key}\`` : key;
@@ -516,7 +1216,7 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
516
1216
  this.logger.debug("No record found");
517
1217
  return null;
518
1218
  }
519
- return this.processResultWithTypeConversion(result[0], tableSchema);
1219
+ return processResultWithTypeConversion(result[0], tableSchema);
520
1220
  } catch (error$1) {
521
1221
  if (error$1 instanceof error.MastraError) throw error$1;
522
1222
  throw new error.MastraError(
@@ -530,634 +1230,729 @@ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
530
1230
  );
531
1231
  }
532
1232
  }
533
- /**
534
- * Validates that key types match the schema definition
535
- * @param keys The keys to validate
536
- * @param tableSchema The table schema to validate against
537
- * @throws Error if a key has an incompatible type
538
- */
539
- validateKeyTypes(keys, tableSchema) {
540
- const fieldTypes = new Map(
541
- tableSchema.fields.map((field) => [field.name, field.type?.toString().toLowerCase()])
542
- );
543
- for (const [key, value] of Object.entries(keys)) {
544
- const fieldType = fieldTypes.get(key);
545
- if (!fieldType) {
546
- throw new Error(`Field '${key}' does not exist in table schema`);
547
- }
548
- if (value !== null) {
549
- if ((fieldType.includes("int") || fieldType.includes("bigint")) && typeof value !== "number") {
550
- throw new Error(`Expected numeric value for field '${key}', got ${typeof value}`);
551
- }
552
- if (fieldType.includes("utf8") && typeof value !== "string") {
553
- throw new Error(`Expected string value for field '${key}', got ${typeof value}`);
554
- }
555
- if (fieldType.includes("timestamp") && !(value instanceof Date) && typeof value !== "string") {
556
- throw new Error(`Expected Date or string value for field '${key}', got ${typeof value}`);
557
- }
558
- }
559
- }
560
- }
561
- /**
562
- * Process a database result with appropriate type conversions based on the table schema
563
- * @param rawResult The raw result object from the database
564
- * @param tableSchema The schema of the table containing type information
565
- * @returns Processed result with correct data types
566
- */
567
- processResultWithTypeConversion(rawResult, tableSchema) {
568
- const fieldTypeMap = /* @__PURE__ */ new Map();
569
- tableSchema.fields.forEach((field) => {
570
- const fieldName = field.name;
571
- const fieldTypeStr = field.type.toString().toLowerCase();
572
- fieldTypeMap.set(fieldName, fieldTypeStr);
573
- });
574
- if (Array.isArray(rawResult)) {
575
- return rawResult.map((item) => this.processResultWithTypeConversion(item, tableSchema));
576
- }
577
- const processedResult = { ...rawResult };
578
- for (const key in processedResult) {
579
- const fieldTypeStr = fieldTypeMap.get(key);
580
- if (!fieldTypeStr) continue;
581
- if (typeof processedResult[key] === "string") {
582
- if (fieldTypeStr.includes("int32") || fieldTypeStr.includes("float32")) {
583
- if (!isNaN(Number(processedResult[key]))) {
584
- processedResult[key] = Number(processedResult[key]);
585
- }
586
- } else if (fieldTypeStr.includes("int64")) {
587
- processedResult[key] = Number(processedResult[key]);
588
- } else if (fieldTypeStr.includes("utf8")) {
589
- try {
590
- processedResult[key] = JSON.parse(processedResult[key]);
591
- } catch (e) {
592
- this.logger.debug(`Failed to parse JSON for key ${key}: ${e}`);
593
- }
594
- }
595
- } else if (typeof processedResult[key] === "bigint") {
596
- processedResult[key] = Number(processedResult[key]);
597
- }
598
- }
599
- return processedResult;
600
- }
601
- getThreadById({ threadId }) {
602
- try {
603
- return this.load({ tableName: storage.TABLE_THREADS, keys: { id: threadId } });
604
- } catch (error$1) {
605
- throw new error.MastraError(
606
- {
607
- id: "LANCE_STORE_GET_THREAD_BY_ID_FAILED",
608
- domain: error.ErrorDomain.STORAGE,
609
- category: error.ErrorCategory.THIRD_PARTY
610
- },
611
- error$1
612
- );
613
- }
614
- }
615
- async getThreadsByResourceId({ resourceId }) {
616
- try {
617
- const table = await this.lanceClient.openTable(storage.TABLE_THREADS);
618
- const query = table.query().where(`\`resourceId\` = '${resourceId}'`);
619
- const records = await query.toArray();
620
- return this.processResultWithTypeConversion(
621
- records,
622
- await this.getTableSchema(storage.TABLE_THREADS)
623
- );
624
- } catch (error$1) {
625
- throw new error.MastraError(
626
- {
627
- id: "LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
628
- domain: error.ErrorDomain.STORAGE,
629
- category: error.ErrorCategory.THIRD_PARTY
630
- },
631
- error$1
632
- );
633
- }
634
- }
635
- /**
636
- * Saves a thread to the database. This function doesn't overwrite existing threads.
637
- * @param thread - The thread to save
638
- * @returns The saved thread
639
- */
640
- async saveThread({ thread }) {
641
- try {
642
- const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
643
- const table = await this.lanceClient.openTable(storage.TABLE_THREADS);
644
- await table.add([record], { mode: "append" });
645
- return thread;
646
- } catch (error$1) {
647
- throw new error.MastraError(
648
- {
649
- id: "LANCE_STORE_SAVE_THREAD_FAILED",
650
- domain: error.ErrorDomain.STORAGE,
651
- category: error.ErrorCategory.THIRD_PARTY
652
- },
653
- error$1
654
- );
655
- }
656
- }
657
- async updateThread({
658
- id,
659
- title,
660
- metadata
661
- }) {
662
- try {
663
- const record = { id, title, metadata: JSON.stringify(metadata) };
664
- const table = await this.lanceClient.openTable(storage.TABLE_THREADS);
665
- await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
666
- const query = table.query().where(`id = '${id}'`);
667
- const records = await query.toArray();
668
- return this.processResultWithTypeConversion(
669
- records[0],
670
- await this.getTableSchema(storage.TABLE_THREADS)
671
- );
672
- } catch (error$1) {
673
- throw new error.MastraError(
674
- {
675
- id: "LANCE_STORE_UPDATE_THREAD_FAILED",
676
- domain: error.ErrorDomain.STORAGE,
677
- category: error.ErrorCategory.THIRD_PARTY
678
- },
679
- error$1
680
- );
681
- }
1233
+ };
1234
+ var StoreScoresLance = class extends storage.ScoresStorage {
1235
+ client;
1236
+ constructor({ client }) {
1237
+ super();
1238
+ this.client = client;
682
1239
  }
683
- async deleteThread({ threadId }) {
1240
+ async saveScore(score) {
1241
+ let validatedScore;
684
1242
  try {
685
- const table = await this.lanceClient.openTable(storage.TABLE_THREADS);
686
- await table.delete(`id = '${threadId}'`);
1243
+ validatedScore = evals.saveScorePayloadSchema.parse(score);
687
1244
  } catch (error$1) {
688
1245
  throw new error.MastraError(
689
1246
  {
690
- id: "LANCE_STORE_DELETE_THREAD_FAILED",
691
- domain: error.ErrorDomain.STORAGE,
692
- category: error.ErrorCategory.THIRD_PARTY
693
- },
694
- error$1
695
- );
696
- }
697
- }
698
- /**
699
- * Processes messages to include context messages based on withPreviousMessages and withNextMessages
700
- * @param records - The sorted array of records to process
701
- * @param include - The array of include specifications with context parameters
702
- * @returns The processed array with context messages included
703
- */
704
- processMessagesWithContext(records, include) {
705
- const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
706
- if (messagesWithContext.length === 0) {
707
- return records;
708
- }
709
- const messageIndexMap = /* @__PURE__ */ new Map();
710
- records.forEach((message, index) => {
711
- messageIndexMap.set(message.id, index);
712
- });
713
- const additionalIndices = /* @__PURE__ */ new Set();
714
- for (const item of messagesWithContext) {
715
- const messageIndex = messageIndexMap.get(item.id);
716
- if (messageIndex !== void 0) {
717
- if (item.withPreviousMessages) {
718
- const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
719
- for (let i = startIdx; i < messageIndex; i++) {
720
- additionalIndices.add(i);
721
- }
1247
+ id: "LANCE_STORAGE_SAVE_SCORE_FAILED",
1248
+ text: "Failed to save score in LanceStorage",
1249
+ domain: error.ErrorDomain.STORAGE,
1250
+ category: error.ErrorCategory.THIRD_PARTY
1251
+ },
1252
+ error$1
1253
+ );
1254
+ }
1255
+ try {
1256
+ const id = crypto.randomUUID();
1257
+ const table = await this.client.openTable(storage.TABLE_SCORERS);
1258
+ const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1259
+ const allowedFields = new Set(schema.fields.map((f) => f.name));
1260
+ const filteredScore = {};
1261
+ Object.keys(validatedScore).forEach((key) => {
1262
+ if (allowedFields.has(key)) {
1263
+ filteredScore[key] = score[key];
722
1264
  }
723
- if (item.withNextMessages) {
724
- const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
725
- for (let i = messageIndex + 1; i <= endIdx; i++) {
726
- additionalIndices.add(i);
727
- }
1265
+ });
1266
+ for (const key in filteredScore) {
1267
+ if (filteredScore[key] !== null && typeof filteredScore[key] === "object" && !(filteredScore[key] instanceof Date)) {
1268
+ filteredScore[key] = JSON.stringify(filteredScore[key]);
728
1269
  }
729
1270
  }
1271
+ filteredScore.id = id;
1272
+ await table.add([filteredScore], { mode: "append" });
1273
+ return { score };
1274
+ } catch (error$1) {
1275
+ throw new error.MastraError(
1276
+ {
1277
+ id: "LANCE_STORAGE_SAVE_SCORE_FAILED",
1278
+ text: "Failed to save score in LanceStorage",
1279
+ domain: error.ErrorDomain.STORAGE,
1280
+ category: error.ErrorCategory.THIRD_PARTY,
1281
+ details: { error: error$1?.message }
1282
+ },
1283
+ error$1
1284
+ );
730
1285
  }
731
- if (additionalIndices.size === 0) {
732
- return records;
733
- }
734
- const originalMatchIds = new Set(include.map((item) => item.id));
735
- const allIndices = /* @__PURE__ */ new Set();
736
- records.forEach((record, index) => {
737
- if (originalMatchIds.has(record.id)) {
738
- allIndices.add(index);
739
- }
740
- });
741
- additionalIndices.forEach((index) => {
742
- allIndices.add(index);
743
- });
744
- return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
745
1286
  }
746
- async getMessages({
747
- threadId,
748
- resourceId,
749
- selectBy,
750
- format,
751
- threadConfig
752
- }) {
1287
+ async getScoreById({ id }) {
753
1288
  try {
754
- if (threadConfig) {
755
- throw new Error("ThreadConfig is not supported by LanceDB storage");
756
- }
757
- const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
758
- const table = await this.lanceClient.openTable(storage.TABLE_MESSAGES);
759
- let query = table.query().where(`\`threadId\` = '${threadId}'`);
760
- if (selectBy) {
761
- if (selectBy.include && selectBy.include.length > 0) {
762
- const includeIds = selectBy.include.map((item) => item.id);
763
- const includeClause = includeIds.map((id) => `\`id\` = '${id}'`).join(" OR ");
764
- query = query.where(`(\`threadId\` = '${threadId}' OR (${includeClause}))`);
765
- }
766
- }
767
- let records = await query.toArray();
768
- records.sort((a, b) => {
769
- const dateA = new Date(a.createdAt).getTime();
770
- const dateB = new Date(b.createdAt).getTime();
771
- return dateA - dateB;
772
- });
773
- if (selectBy?.include && selectBy.include.length > 0) {
774
- records = this.processMessagesWithContext(records, selectBy.include);
775
- }
776
- if (limit !== Number.MAX_SAFE_INTEGER) {
777
- records = records.slice(-limit);
778
- }
779
- const messages = this.processResultWithTypeConversion(records, await this.getTableSchema(storage.TABLE_MESSAGES));
780
- const normalized = messages.map((msg) => ({
781
- ...msg,
782
- content: typeof msg.content === "string" ? (() => {
783
- try {
784
- return JSON.parse(msg.content);
785
- } catch {
786
- return msg.content;
787
- }
788
- })() : msg.content
789
- }));
790
- const list = new agent.MessageList({ threadId, resourceId }).add(normalized, "memory");
791
- if (format === "v2") return list.get.all.v2();
792
- return list.get.all.v1();
1289
+ const table = await this.client.openTable(storage.TABLE_SCORERS);
1290
+ const query = table.query().where(`id = '${id}'`).limit(1);
1291
+ const records = await query.toArray();
1292
+ if (records.length === 0) return null;
1293
+ const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1294
+ return processResultWithTypeConversion(records[0], schema);
793
1295
  } catch (error$1) {
794
1296
  throw new error.MastraError(
795
1297
  {
796
- id: "LANCE_STORE_GET_MESSAGES_FAILED",
1298
+ id: "LANCE_STORAGE_GET_SCORE_BY_ID_FAILED",
1299
+ text: "Failed to get score by id in LanceStorage",
797
1300
  domain: error.ErrorDomain.STORAGE,
798
- category: error.ErrorCategory.THIRD_PARTY
1301
+ category: error.ErrorCategory.THIRD_PARTY,
1302
+ details: { error: error$1?.message }
799
1303
  },
800
1304
  error$1
801
1305
  );
802
1306
  }
803
1307
  }
804
- async saveMessages(args) {
1308
+ async listScoresByScorerId({
1309
+ scorerId,
1310
+ pagination,
1311
+ entityId,
1312
+ entityType,
1313
+ source
1314
+ }) {
805
1315
  try {
806
- const { messages, format = "v1" } = args;
807
- if (messages.length === 0) {
808
- return [];
1316
+ const { page, perPage: perPageInput } = pagination;
1317
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1318
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1319
+ const table = await this.client.openTable(storage.TABLE_SCORERS);
1320
+ let query = table.query().where(`\`scorerId\` = '${scorerId}'`);
1321
+ if (source) {
1322
+ query = query.where(`\`source\` = '${source}'`);
809
1323
  }
810
- const threadId = messages[0]?.threadId;
811
- if (!threadId) {
812
- throw new Error("Thread ID is required");
1324
+ if (entityId) {
1325
+ query = query.where(`\`entityId\` = '${entityId}'`);
813
1326
  }
814
- const transformedMessages = messages.map((message) => ({
815
- ...message,
816
- content: JSON.stringify(message.content)
817
- }));
818
- const table = await this.lanceClient.openTable(storage.TABLE_MESSAGES);
819
- await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
820
- const list = new agent.MessageList().add(messages, "memory");
821
- if (format === `v2`) return list.get.all.v2();
822
- return list.get.all.v1();
1327
+ if (entityType) {
1328
+ query = query.where(`\`entityType\` = '${entityType}'`);
1329
+ }
1330
+ let totalQuery = table.query().where(`\`scorerId\` = '${scorerId}'`);
1331
+ if (source) {
1332
+ totalQuery = totalQuery.where(`\`source\` = '${source}'`);
1333
+ }
1334
+ if (entityId) {
1335
+ totalQuery = totalQuery.where(`\`entityId\` = '${entityId}'`);
1336
+ }
1337
+ if (entityType) {
1338
+ totalQuery = totalQuery.where(`\`entityType\` = '${entityType}'`);
1339
+ }
1340
+ const allRecords = await totalQuery.toArray();
1341
+ const total = allRecords.length;
1342
+ const end = perPageInput === false ? total : start + perPage;
1343
+ if (perPageInput !== false) {
1344
+ query = query.limit(perPage);
1345
+ if (start > 0) query = query.offset(start);
1346
+ }
1347
+ const records = await query.toArray();
1348
+ const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1349
+ const scores = processResultWithTypeConversion(records, schema);
1350
+ return {
1351
+ pagination: {
1352
+ page,
1353
+ perPage: perPageForResponse,
1354
+ total,
1355
+ hasMore: end < total
1356
+ },
1357
+ scores
1358
+ };
823
1359
  } catch (error$1) {
824
1360
  throw new error.MastraError(
825
1361
  {
826
- id: "LANCE_STORE_SAVE_MESSAGES_FAILED",
1362
+ id: "LANCE_STORAGE_GET_SCORES_BY_SCORER_ID_FAILED",
1363
+ text: "Failed to get scores by scorerId in LanceStorage",
827
1364
  domain: error.ErrorDomain.STORAGE,
828
- category: error.ErrorCategory.THIRD_PARTY
1365
+ category: error.ErrorCategory.THIRD_PARTY,
1366
+ details: { error: error$1?.message }
829
1367
  },
830
1368
  error$1
831
1369
  );
832
1370
  }
833
1371
  }
834
- async saveTrace({ trace }) {
1372
+ async listScoresByRunId({
1373
+ runId,
1374
+ pagination
1375
+ }) {
835
1376
  try {
836
- const table = await this.lanceClient.openTable(storage.TABLE_TRACES);
837
- const record = {
838
- ...trace,
839
- attributes: JSON.stringify(trace.attributes),
840
- status: JSON.stringify(trace.status),
841
- events: JSON.stringify(trace.events),
842
- links: JSON.stringify(trace.links),
843
- other: JSON.stringify(trace.other)
1377
+ const { page, perPage: perPageInput } = pagination;
1378
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1379
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1380
+ const table = await this.client.openTable(storage.TABLE_SCORERS);
1381
+ const allRecords = await table.query().where(`\`runId\` = '${runId}'`).toArray();
1382
+ const total = allRecords.length;
1383
+ const end = perPageInput === false ? total : start + perPage;
1384
+ let query = table.query().where(`\`runId\` = '${runId}'`);
1385
+ if (perPageInput !== false) {
1386
+ query = query.limit(perPage);
1387
+ if (start > 0) query = query.offset(start);
1388
+ }
1389
+ const records = await query.toArray();
1390
+ const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1391
+ const scores = processResultWithTypeConversion(records, schema);
1392
+ return {
1393
+ pagination: {
1394
+ page,
1395
+ perPage: perPageForResponse,
1396
+ total,
1397
+ hasMore: end < total
1398
+ },
1399
+ scores
844
1400
  };
845
- await table.add([record], { mode: "append" });
846
- return trace;
847
1401
  } catch (error$1) {
848
1402
  throw new error.MastraError(
849
1403
  {
850
- id: "LANCE_STORE_SAVE_TRACE_FAILED",
1404
+ id: "LANCE_STORAGE_GET_SCORES_BY_RUN_ID_FAILED",
1405
+ text: "Failed to get scores by runId in LanceStorage",
851
1406
  domain: error.ErrorDomain.STORAGE,
852
- category: error.ErrorCategory.THIRD_PARTY
1407
+ category: error.ErrorCategory.THIRD_PARTY,
1408
+ details: { error: error$1?.message }
853
1409
  },
854
1410
  error$1
855
1411
  );
856
1412
  }
857
1413
  }
858
- async getTraceById({ traceId }) {
1414
+ async listScoresByEntityId({
1415
+ entityId,
1416
+ entityType,
1417
+ pagination
1418
+ }) {
859
1419
  try {
860
- const table = await this.lanceClient.openTable(storage.TABLE_TRACES);
861
- const query = table.query().where(`id = '${traceId}'`);
1420
+ const { page, perPage: perPageInput } = pagination;
1421
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1422
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1423
+ const table = await this.client.openTable(storage.TABLE_SCORERS);
1424
+ const allRecords = await table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).toArray();
1425
+ const total = allRecords.length;
1426
+ const end = perPageInput === false ? total : start + perPage;
1427
+ let query = table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`);
1428
+ if (perPageInput !== false) {
1429
+ query = query.limit(perPage);
1430
+ if (start > 0) query = query.offset(start);
1431
+ }
862
1432
  const records = await query.toArray();
863
- return this.processResultWithTypeConversion(records[0], await this.getTableSchema(storage.TABLE_TRACES));
1433
+ const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1434
+ const scores = processResultWithTypeConversion(records, schema);
1435
+ return {
1436
+ pagination: {
1437
+ page,
1438
+ perPage: perPageForResponse,
1439
+ total,
1440
+ hasMore: end < total
1441
+ },
1442
+ scores
1443
+ };
864
1444
  } catch (error$1) {
865
1445
  throw new error.MastraError(
866
1446
  {
867
- id: "LANCE_STORE_GET_TRACE_BY_ID_FAILED",
1447
+ id: "LANCE_STORAGE_GET_SCORES_BY_ENTITY_ID_FAILED",
1448
+ text: "Failed to get scores by entityId and entityType in LanceStorage",
868
1449
  domain: error.ErrorDomain.STORAGE,
869
- category: error.ErrorCategory.THIRD_PARTY
1450
+ category: error.ErrorCategory.THIRD_PARTY,
1451
+ details: { error: error$1?.message }
870
1452
  },
871
1453
  error$1
872
1454
  );
873
1455
  }
874
1456
  }
875
- async getTraces({
876
- name,
877
- scope,
878
- page = 1,
879
- perPage = 10,
880
- attributes
1457
+ async listScoresBySpan({
1458
+ traceId,
1459
+ spanId,
1460
+ pagination
881
1461
  }) {
882
1462
  try {
883
- const table = await this.lanceClient.openTable(storage.TABLE_TRACES);
884
- const query = table.query();
885
- if (name) {
886
- query.where(`name = '${name}'`);
887
- }
888
- if (scope) {
889
- query.where(`scope = '${scope}'`);
890
- }
891
- if (attributes) {
892
- query.where(`attributes = '${JSON.stringify(attributes)}'`);
893
- }
894
- const offset = (page - 1) * perPage;
895
- query.limit(perPage);
896
- if (offset > 0) {
897
- query.offset(offset);
1463
+ const { page, perPage: perPageInput } = pagination;
1464
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1465
+ const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1466
+ const table = await this.client.openTable(storage.TABLE_SCORERS);
1467
+ const allRecords = await table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`).toArray();
1468
+ const total = allRecords.length;
1469
+ const end = perPageInput === false ? total : start + perPage;
1470
+ let query = table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`);
1471
+ if (perPageInput !== false) {
1472
+ query = query.limit(perPage);
1473
+ if (start > 0) query = query.offset(start);
898
1474
  }
899
1475
  const records = await query.toArray();
900
- return records.map((record) => {
901
- return {
902
- ...record,
903
- attributes: JSON.parse(record.attributes),
904
- status: JSON.parse(record.status),
905
- events: JSON.parse(record.events),
906
- links: JSON.parse(record.links),
907
- other: JSON.parse(record.other),
908
- startTime: new Date(record.startTime),
909
- endTime: new Date(record.endTime),
910
- createdAt: new Date(record.createdAt)
911
- };
912
- });
1476
+ const schema = await getTableSchema({ tableName: storage.TABLE_SCORERS, client: this.client });
1477
+ const scores = processResultWithTypeConversion(records, schema);
1478
+ return {
1479
+ pagination: {
1480
+ page,
1481
+ perPage: perPageForResponse,
1482
+ total,
1483
+ hasMore: end < total
1484
+ },
1485
+ scores
1486
+ };
913
1487
  } catch (error$1) {
914
1488
  throw new error.MastraError(
915
1489
  {
916
- id: "LANCE_STORE_GET_TRACES_FAILED",
1490
+ id: "LANCE_STORAGE_GET_SCORES_BY_SPAN_FAILED",
1491
+ text: "Failed to get scores by traceId and spanId in LanceStorage",
917
1492
  domain: error.ErrorDomain.STORAGE,
918
1493
  category: error.ErrorCategory.THIRD_PARTY,
919
- details: { name: name ?? "", scope: scope ?? "" }
1494
+ details: { error: error$1?.message }
920
1495
  },
921
1496
  error$1
922
1497
  );
923
1498
  }
924
1499
  }
925
- async saveEvals({ evals }) {
1500
+ };
1501
+ function parseWorkflowRun(row) {
1502
+ let parsedSnapshot = row.snapshot;
1503
+ if (typeof parsedSnapshot === "string") {
1504
+ try {
1505
+ parsedSnapshot = JSON.parse(row.snapshot);
1506
+ } catch (e) {
1507
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1508
+ }
1509
+ }
1510
+ return {
1511
+ workflowName: row.workflow_name,
1512
+ runId: row.run_id,
1513
+ snapshot: parsedSnapshot,
1514
+ createdAt: storage.ensureDate(row.createdAt),
1515
+ updatedAt: storage.ensureDate(row.updatedAt),
1516
+ resourceId: row.resourceId
1517
+ };
1518
+ }
1519
+ var StoreWorkflowsLance = class extends storage.WorkflowsStorage {
1520
+ client;
1521
+ constructor({ client }) {
1522
+ super();
1523
+ this.client = client;
1524
+ }
1525
+ updateWorkflowResults({
1526
+ // workflowName,
1527
+ // runId,
1528
+ // stepId,
1529
+ // result,
1530
+ // requestContext,
1531
+ }) {
1532
+ throw new Error("Method not implemented.");
1533
+ }
1534
+ updateWorkflowState({
1535
+ // workflowName,
1536
+ // runId,
1537
+ // opts,
1538
+ }) {
1539
+ throw new Error("Method not implemented.");
1540
+ }
1541
+ async persistWorkflowSnapshot({
1542
+ workflowName,
1543
+ runId,
1544
+ resourceId,
1545
+ snapshot
1546
+ }) {
926
1547
  try {
927
- const table = await this.lanceClient.openTable(storage.TABLE_EVALS);
928
- const transformedEvals = evals.map((evalRecord) => ({
929
- input: evalRecord.input,
930
- output: evalRecord.output,
931
- agent_name: evalRecord.agentName,
932
- metric_name: evalRecord.metricName,
933
- result: JSON.stringify(evalRecord.result),
934
- instructions: evalRecord.instructions,
935
- test_info: JSON.stringify(evalRecord.testInfo),
936
- global_run_id: evalRecord.globalRunId,
937
- run_id: evalRecord.runId,
938
- created_at: new Date(evalRecord.createdAt).getTime()
939
- }));
940
- await table.add(transformedEvals, { mode: "append" });
941
- return evals;
1548
+ const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1549
+ const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
1550
+ const records = await query.toArray();
1551
+ let createdAt;
1552
+ const now = Date.now();
1553
+ if (records.length > 0) {
1554
+ createdAt = records[0].createdAt ?? now;
1555
+ } else {
1556
+ createdAt = now;
1557
+ }
1558
+ const { status, value, ...rest } = snapshot;
1559
+ const record = {
1560
+ workflow_name: workflowName,
1561
+ run_id: runId,
1562
+ resourceId,
1563
+ snapshot: JSON.stringify({ status, value, ...rest }),
1564
+ // this is to ensure status is always just before value, for when querying the db by status
1565
+ createdAt,
1566
+ updatedAt: now
1567
+ };
1568
+ await table.mergeInsert(["workflow_name", "run_id"]).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
942
1569
  } catch (error$1) {
943
1570
  throw new error.MastraError(
944
1571
  {
945
- id: "LANCE_STORE_SAVE_EVALS_FAILED",
1572
+ id: "LANCE_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
946
1573
  domain: error.ErrorDomain.STORAGE,
947
- category: error.ErrorCategory.THIRD_PARTY
1574
+ category: error.ErrorCategory.THIRD_PARTY,
1575
+ details: { workflowName, runId }
948
1576
  },
949
1577
  error$1
950
1578
  );
951
1579
  }
952
1580
  }
953
- async getEvalsByAgentName(agentName, type) {
1581
+ async loadWorkflowSnapshot({
1582
+ workflowName,
1583
+ runId
1584
+ }) {
954
1585
  try {
955
- if (type) {
956
- this.logger.warn("Type is not implemented yet in LanceDB storage");
957
- }
958
- const table = await this.lanceClient.openTable(storage.TABLE_EVALS);
959
- const query = table.query().where(`agent_name = '${agentName}'`);
1586
+ const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1587
+ const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
960
1588
  const records = await query.toArray();
961
- return records.map((record) => {
962
- return {
963
- id: record.id,
964
- input: record.input,
965
- output: record.output,
966
- agentName: record.agent_name,
967
- metricName: record.metric_name,
968
- result: JSON.parse(record.result),
969
- instructions: record.instructions,
970
- testInfo: JSON.parse(record.test_info),
971
- globalRunId: record.global_run_id,
972
- runId: record.run_id,
973
- createdAt: new Date(record.created_at).toString()
974
- };
975
- });
1589
+ return records.length > 0 ? JSON.parse(records[0].snapshot) : null;
976
1590
  } catch (error$1) {
977
1591
  throw new error.MastraError(
978
1592
  {
979
- id: "LANCE_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
1593
+ id: "LANCE_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
980
1594
  domain: error.ErrorDomain.STORAGE,
981
1595
  category: error.ErrorCategory.THIRD_PARTY,
982
- details: { agentName }
1596
+ details: { workflowName, runId }
983
1597
  },
984
1598
  error$1
985
1599
  );
986
1600
  }
987
1601
  }
988
- parseWorkflowRun(row) {
989
- let parsedSnapshot = row.snapshot;
990
- if (typeof parsedSnapshot === "string") {
991
- try {
992
- parsedSnapshot = JSON.parse(row.snapshot);
993
- } catch (e) {
994
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1602
+ async getWorkflowRunById(args) {
1603
+ try {
1604
+ const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1605
+ let whereClause = `run_id = '${args.runId}'`;
1606
+ if (args.workflowName) {
1607
+ whereClause += ` AND workflow_name = '${args.workflowName}'`;
995
1608
  }
1609
+ const query = table.query().where(whereClause);
1610
+ const records = await query.toArray();
1611
+ if (records.length === 0) return null;
1612
+ const record = records[0];
1613
+ return parseWorkflowRun(record);
1614
+ } catch (error$1) {
1615
+ throw new error.MastraError(
1616
+ {
1617
+ id: "LANCE_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
1618
+ domain: error.ErrorDomain.STORAGE,
1619
+ category: error.ErrorCategory.THIRD_PARTY,
1620
+ details: { runId: args.runId, workflowName: args.workflowName ?? "" }
1621
+ },
1622
+ error$1
1623
+ );
996
1624
  }
997
- return {
998
- workflowName: row.workflow_name,
999
- runId: row.run_id,
1000
- snapshot: parsedSnapshot,
1001
- createdAt: this.ensureDate(row.createdAt),
1002
- updatedAt: this.ensureDate(row.updatedAt),
1003
- resourceId: row.resourceId
1004
- };
1005
1625
  }
1006
- async getWorkflowRuns(args) {
1626
+ async listWorkflowRuns(args) {
1007
1627
  try {
1008
- const table = await this.lanceClient.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1009
- const query = table.query();
1628
+ const table = await this.client.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1629
+ let query = table.query();
1630
+ const conditions = [];
1010
1631
  if (args?.workflowName) {
1011
- query.where(`workflow_name = '${args.workflowName}'`);
1632
+ conditions.push(`workflow_name = '${args.workflowName.replace(/'/g, "''")}'`);
1633
+ }
1634
+ if (args?.status) {
1635
+ const escapedStatus = args.status.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/%/g, "\\%").replace(/_/g, "\\_");
1636
+ conditions.push(`\`snapshot\` LIKE '%"status":"${escapedStatus}","value"%'`);
1012
1637
  }
1013
- if (args?.fromDate) {
1014
- query.where(`\`createdAt\` >= ${args.fromDate.getTime()}`);
1638
+ if (args?.resourceId) {
1639
+ conditions.push(`\`resourceId\` = '${args.resourceId}'`);
1015
1640
  }
1016
- if (args?.toDate) {
1017
- query.where(`\`createdAt\` <= ${args.toDate.getTime()}`);
1641
+ if (args?.fromDate instanceof Date) {
1642
+ conditions.push(`\`createdAt\` >= ${args.fromDate.getTime()}`);
1018
1643
  }
1019
- if (args?.limit) {
1020
- query.limit(args.limit);
1644
+ if (args?.toDate instanceof Date) {
1645
+ conditions.push(`\`createdAt\` <= ${args.toDate.getTime()}`);
1021
1646
  }
1022
- if (args?.offset) {
1023
- query.offset(args.offset);
1647
+ let total = 0;
1648
+ if (conditions.length > 0) {
1649
+ query = query.where(conditions.join(" AND "));
1650
+ total = await table.countRows(conditions.join(" AND "));
1651
+ } else {
1652
+ total = await table.countRows();
1653
+ }
1654
+ if (args?.perPage !== void 0 && args?.page !== void 0) {
1655
+ const normalizedPerPage = storage.normalizePerPage(args.perPage, Number.MAX_SAFE_INTEGER);
1656
+ if (args.page < 0 || !Number.isInteger(args.page)) {
1657
+ throw new error.MastraError(
1658
+ {
1659
+ id: "LANCE_STORE_INVALID_PAGINATION_PARAMS",
1660
+ domain: error.ErrorDomain.STORAGE,
1661
+ category: error.ErrorCategory.USER,
1662
+ details: { page: args.page, perPage: args.perPage }
1663
+ },
1664
+ new Error(`Invalid pagination parameters: page=${args.page}, perPage=${args.perPage}`)
1665
+ );
1666
+ }
1667
+ const offset = args.page * normalizedPerPage;
1668
+ query.limit(normalizedPerPage);
1669
+ query.offset(offset);
1024
1670
  }
1025
1671
  const records = await query.toArray();
1026
1672
  return {
1027
- runs: records.map((record) => this.parseWorkflowRun(record)),
1028
- total: records.length
1673
+ runs: records.map((record) => parseWorkflowRun(record)),
1674
+ total: total || records.length
1029
1675
  };
1030
1676
  } catch (error$1) {
1031
1677
  throw new error.MastraError(
1032
1678
  {
1033
- id: "LANCE_STORE_GET_WORKFLOW_RUNS_FAILED",
1679
+ id: "LANCE_STORE_LIST_WORKFLOW_RUNS_FAILED",
1034
1680
  domain: error.ErrorDomain.STORAGE,
1035
1681
  category: error.ErrorCategory.THIRD_PARTY,
1036
- details: { namespace: args?.namespace ?? "", workflowName: args?.workflowName ?? "" }
1682
+ details: { resourceId: args?.resourceId ?? "", workflowName: args?.workflowName ?? "" }
1037
1683
  },
1038
1684
  error$1
1039
1685
  );
1040
1686
  }
1041
1687
  }
1688
+ };
1689
+
1690
+ // src/storage/index.ts
1691
+ var LanceStorage = class _LanceStorage extends storage.MastraStorage {
1692
+ stores;
1693
+ lanceClient;
1694
+ /**
1695
+ * Creates a new instance of LanceStorage
1696
+ * @param id The unique identifier for this storage instance
1697
+ * @param name The name for this storage instance
1698
+ * @param uri The URI to connect to LanceDB
1699
+ * @param options connection options
1700
+ *
1701
+ * Usage:
1702
+ *
1703
+ * Connect to a local database
1704
+ * ```ts
1705
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', '/path/to/db');
1706
+ * ```
1707
+ *
1708
+ * Connect to a LanceDB cloud database
1709
+ * ```ts
1710
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', 'db://host:port');
1711
+ * ```
1712
+ *
1713
+ * Connect to a cloud database
1714
+ * ```ts
1715
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', 's3://bucket/db', { storageOptions: { timeout: '60s' } });
1716
+ * ```
1717
+ */
1718
+ static async create(id, name, uri, options) {
1719
+ const instance = new _LanceStorage(id, name);
1720
+ try {
1721
+ instance.lanceClient = await lancedb.connect(uri, options);
1722
+ const operations = new StoreOperationsLance({ client: instance.lanceClient });
1723
+ instance.stores = {
1724
+ operations: new StoreOperationsLance({ client: instance.lanceClient }),
1725
+ workflows: new StoreWorkflowsLance({ client: instance.lanceClient }),
1726
+ scores: new StoreScoresLance({ client: instance.lanceClient }),
1727
+ memory: new StoreMemoryLance({ client: instance.lanceClient, operations })
1728
+ };
1729
+ return instance;
1730
+ } catch (e) {
1731
+ throw new error.MastraError(
1732
+ {
1733
+ id: "STORAGE_LANCE_STORAGE_CONNECT_FAILED",
1734
+ domain: error.ErrorDomain.STORAGE,
1735
+ category: error.ErrorCategory.THIRD_PARTY,
1736
+ text: `Failed to connect to LanceDB: ${e.message || e}`,
1737
+ details: { uri, optionsProvided: !!options }
1738
+ },
1739
+ e
1740
+ );
1741
+ }
1742
+ }
1743
+ /**
1744
+ * @internal
1745
+ * Private constructor to enforce using the create factory method
1746
+ */
1747
+ constructor(id, name) {
1748
+ super({ id, name });
1749
+ const operations = new StoreOperationsLance({ client: this.lanceClient });
1750
+ this.stores = {
1751
+ operations: new StoreOperationsLance({ client: this.lanceClient }),
1752
+ workflows: new StoreWorkflowsLance({ client: this.lanceClient }),
1753
+ scores: new StoreScoresLance({ client: this.lanceClient }),
1754
+ memory: new StoreMemoryLance({ client: this.lanceClient, operations })
1755
+ };
1756
+ }
1757
+ async createTable({
1758
+ tableName,
1759
+ schema
1760
+ }) {
1761
+ return this.stores.operations.createTable({ tableName, schema });
1762
+ }
1763
+ async dropTable({ tableName }) {
1764
+ return this.stores.operations.dropTable({ tableName });
1765
+ }
1766
+ async alterTable({
1767
+ tableName,
1768
+ schema,
1769
+ ifNotExists
1770
+ }) {
1771
+ return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
1772
+ }
1773
+ async clearTable({ tableName }) {
1774
+ return this.stores.operations.clearTable({ tableName });
1775
+ }
1776
+ async insert({ tableName, record }) {
1777
+ return this.stores.operations.insert({ tableName, record });
1778
+ }
1779
+ async batchInsert({ tableName, records }) {
1780
+ return this.stores.operations.batchInsert({ tableName, records });
1781
+ }
1782
+ async load({ tableName, keys }) {
1783
+ return this.stores.operations.load({ tableName, keys });
1784
+ }
1785
+ async getThreadById({ threadId }) {
1786
+ return this.stores.memory.getThreadById({ threadId });
1787
+ }
1788
+ /**
1789
+ * Saves a thread to the database. This function doesn't overwrite existing threads.
1790
+ * @param thread - The thread to save
1791
+ * @returns The saved thread
1792
+ */
1793
+ async saveThread({ thread }) {
1794
+ return this.stores.memory.saveThread({ thread });
1795
+ }
1796
+ async updateThread({
1797
+ id,
1798
+ title,
1799
+ metadata
1800
+ }) {
1801
+ return this.stores.memory.updateThread({ id, title, metadata });
1802
+ }
1803
+ async deleteThread({ threadId }) {
1804
+ return this.stores.memory.deleteThread({ threadId });
1805
+ }
1806
+ get supports() {
1807
+ return {
1808
+ selectByIncludeResourceScope: true,
1809
+ resourceWorkingMemory: true,
1810
+ hasColumn: true,
1811
+ createTable: true,
1812
+ deleteMessages: false,
1813
+ listScoresBySpan: true
1814
+ };
1815
+ }
1816
+ async getResourceById({ resourceId }) {
1817
+ return this.stores.memory.getResourceById({ resourceId });
1818
+ }
1819
+ async saveResource({ resource }) {
1820
+ return this.stores.memory.saveResource({ resource });
1821
+ }
1822
+ async updateResource({
1823
+ resourceId,
1824
+ workingMemory,
1825
+ metadata
1826
+ }) {
1827
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
1828
+ }
1042
1829
  /**
1043
- * Retrieve a single workflow run by its runId.
1044
- * @param args The ID of the workflow run to retrieve
1045
- * @returns The workflow run object or null if not found
1830
+ * Processes messages to include context messages based on withPreviousMessages and withNextMessages
1831
+ * @param records - The sorted array of records to process
1832
+ * @param include - The array of include specifications with context parameters
1833
+ * @returns The processed array with context messages included
1046
1834
  */
1047
- async getWorkflowRunById(args) {
1048
- try {
1049
- const table = await this.lanceClient.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1050
- let whereClause = `run_id = '${args.runId}'`;
1051
- if (args.workflowName) {
1052
- whereClause += ` AND workflow_name = '${args.workflowName}'`;
1835
+ processMessagesWithContext(records, include) {
1836
+ const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
1837
+ if (messagesWithContext.length === 0) {
1838
+ return records;
1839
+ }
1840
+ const messageIndexMap = /* @__PURE__ */ new Map();
1841
+ records.forEach((message, index) => {
1842
+ messageIndexMap.set(message.id, index);
1843
+ });
1844
+ const additionalIndices = /* @__PURE__ */ new Set();
1845
+ for (const item of messagesWithContext) {
1846
+ const messageIndex = messageIndexMap.get(item.id);
1847
+ if (messageIndex !== void 0) {
1848
+ if (item.withPreviousMessages) {
1849
+ const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
1850
+ for (let i = startIdx; i < messageIndex; i++) {
1851
+ additionalIndices.add(i);
1852
+ }
1853
+ }
1854
+ if (item.withNextMessages) {
1855
+ const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
1856
+ for (let i = messageIndex + 1; i <= endIdx; i++) {
1857
+ additionalIndices.add(i);
1858
+ }
1859
+ }
1053
1860
  }
1054
- const query = table.query().where(whereClause);
1055
- const records = await query.toArray();
1056
- if (records.length === 0) return null;
1057
- const record = records[0];
1058
- return this.parseWorkflowRun(record);
1059
- } catch (error$1) {
1060
- throw new error.MastraError(
1061
- {
1062
- id: "LANCE_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
1063
- domain: error.ErrorDomain.STORAGE,
1064
- category: error.ErrorCategory.THIRD_PARTY,
1065
- details: { runId: args.runId, workflowName: args.workflowName ?? "" }
1066
- },
1067
- error$1
1068
- );
1069
1861
  }
1862
+ if (additionalIndices.size === 0) {
1863
+ return records;
1864
+ }
1865
+ const originalMatchIds = new Set(include.map((item) => item.id));
1866
+ const allIndices = /* @__PURE__ */ new Set();
1867
+ records.forEach((record, index) => {
1868
+ if (originalMatchIds.has(record.id)) {
1869
+ allIndices.add(index);
1870
+ }
1871
+ });
1872
+ additionalIndices.forEach((index) => {
1873
+ allIndices.add(index);
1874
+ });
1875
+ return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
1876
+ }
1877
+ async listMessagesById({ messageIds }) {
1878
+ return this.stores.memory.listMessagesById({ messageIds });
1879
+ }
1880
+ async saveMessages(args) {
1881
+ return this.stores.memory.saveMessages(args);
1882
+ }
1883
+ async updateMessages(_args) {
1884
+ return this.stores.memory.updateMessages(_args);
1885
+ }
1886
+ async listWorkflowRuns(args) {
1887
+ return this.stores.workflows.listWorkflowRuns(args);
1888
+ }
1889
+ async getWorkflowRunById(args) {
1890
+ return this.stores.workflows.getWorkflowRunById(args);
1891
+ }
1892
+ async updateWorkflowResults({
1893
+ workflowName,
1894
+ runId,
1895
+ stepId,
1896
+ result,
1897
+ requestContext
1898
+ }) {
1899
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
1900
+ }
1901
+ async updateWorkflowState({
1902
+ workflowName,
1903
+ runId,
1904
+ opts
1905
+ }) {
1906
+ return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
1070
1907
  }
1071
1908
  async persistWorkflowSnapshot({
1072
1909
  workflowName,
1073
1910
  runId,
1911
+ resourceId,
1074
1912
  snapshot
1075
1913
  }) {
1076
- try {
1077
- const table = await this.lanceClient.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1078
- const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
1079
- const records = await query.toArray();
1080
- let createdAt;
1081
- const now = Date.now();
1082
- if (records.length > 0) {
1083
- createdAt = records[0].createdAt ?? now;
1084
- } else {
1085
- createdAt = now;
1086
- }
1087
- const record = {
1088
- workflow_name: workflowName,
1089
- run_id: runId,
1090
- snapshot: JSON.stringify(snapshot),
1091
- createdAt,
1092
- updatedAt: now
1093
- };
1094
- await table.mergeInsert(["workflow_name", "run_id"]).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
1095
- } catch (error$1) {
1096
- throw new error.MastraError(
1097
- {
1098
- id: "LANCE_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
1099
- domain: error.ErrorDomain.STORAGE,
1100
- category: error.ErrorCategory.THIRD_PARTY,
1101
- details: { workflowName, runId }
1102
- },
1103
- error$1
1104
- );
1105
- }
1914
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
1106
1915
  }
1107
1916
  async loadWorkflowSnapshot({
1108
1917
  workflowName,
1109
1918
  runId
1110
1919
  }) {
1111
- try {
1112
- const table = await this.lanceClient.openTable(storage.TABLE_WORKFLOW_SNAPSHOT);
1113
- const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
1114
- const records = await query.toArray();
1115
- return records.length > 0 ? JSON.parse(records[0].snapshot) : null;
1116
- } catch (error$1) {
1117
- throw new error.MastraError(
1118
- {
1119
- id: "LANCE_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
1120
- domain: error.ErrorDomain.STORAGE,
1121
- category: error.ErrorCategory.THIRD_PARTY,
1122
- details: { workflowName, runId }
1123
- },
1124
- error$1
1125
- );
1126
- }
1920
+ return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
1127
1921
  }
1128
- async getTracesPaginated(_args) {
1129
- throw new error.MastraError(
1130
- {
1131
- id: "LANCE_STORE_GET_TRACES_PAGINATED_FAILED",
1132
- domain: error.ErrorDomain.STORAGE,
1133
- category: error.ErrorCategory.THIRD_PARTY
1134
- },
1135
- "Method not implemented."
1136
- );
1922
+ async getScoreById({ id: _id }) {
1923
+ return this.stores.scores.getScoreById({ id: _id });
1137
1924
  }
1138
- async getThreadsByResourceIdPaginated(_args) {
1139
- throw new error.MastraError(
1140
- {
1141
- id: "LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
1142
- domain: error.ErrorDomain.STORAGE,
1143
- category: error.ErrorCategory.THIRD_PARTY
1144
- },
1145
- "Method not implemented."
1146
- );
1925
+ async listScoresByScorerId({
1926
+ scorerId,
1927
+ source,
1928
+ entityId,
1929
+ entityType,
1930
+ pagination
1931
+ }) {
1932
+ return this.stores.scores.listScoresByScorerId({ scorerId, source, pagination, entityId, entityType });
1147
1933
  }
1148
- async getMessagesPaginated(_args) {
1149
- throw new error.MastraError(
1150
- {
1151
- id: "LANCE_STORE_GET_MESSAGES_PAGINATED_FAILED",
1152
- domain: error.ErrorDomain.STORAGE,
1153
- category: error.ErrorCategory.THIRD_PARTY
1154
- },
1155
- "Method not implemented."
1156
- );
1934
+ async saveScore(_score) {
1935
+ return this.stores.scores.saveScore(_score);
1157
1936
  }
1158
- async updateMessages(_args) {
1159
- this.logger.error("updateMessages is not yet implemented in LanceStore");
1160
- throw new Error("Method not implemented");
1937
+ async listScoresByRunId({
1938
+ runId,
1939
+ pagination
1940
+ }) {
1941
+ return this.stores.scores.listScoresByRunId({ runId, pagination });
1942
+ }
1943
+ async listScoresByEntityId({
1944
+ entityId,
1945
+ entityType,
1946
+ pagination
1947
+ }) {
1948
+ return this.stores.scores.listScoresByEntityId({ entityId, entityType, pagination });
1949
+ }
1950
+ async listScoresBySpan({
1951
+ traceId,
1952
+ spanId,
1953
+ pagination
1954
+ }) {
1955
+ return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
1161
1956
  }
1162
1957
  };
1163
1958
  var LanceFilterTranslator = class extends filter.BaseFilterTranslator {
@@ -1506,7 +2301,7 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
1506
2301
  * ```
1507
2302
  */
1508
2303
  static async create(uri, options) {
1509
- const instance = new _LanceVectorStore();
2304
+ const instance = new _LanceVectorStore(options?.id || crypto.randomUUID());
1510
2305
  try {
1511
2306
  instance.lanceClient = await lancedb.connect(uri, options);
1512
2307
  return instance;
@@ -1526,8 +2321,8 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
1526
2321
  * @internal
1527
2322
  * Private constructor to enforce using the create factory method
1528
2323
  */
1529
- constructor() {
1530
- super();
2324
+ constructor(id) {
2325
+ super({ id });
1531
2326
  }
1532
2327
  close() {
1533
2328
  if (this.lanceClient) {
@@ -2050,7 +2845,44 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2050
2845
  );
2051
2846
  }
2052
2847
  }
2053
- async updateVector({ indexName, id, update }) {
2848
+ async updateVector(params) {
2849
+ const { indexName, update } = params;
2850
+ if ("id" in params && "filter" in params && params.id && params.filter) {
2851
+ throw new error.MastraError({
2852
+ id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
2853
+ domain: error.ErrorDomain.STORAGE,
2854
+ category: error.ErrorCategory.USER,
2855
+ text: "id and filter are mutually exclusive",
2856
+ details: { indexName }
2857
+ });
2858
+ }
2859
+ if (!("id" in params || "filter" in params) || !params.id && !params.filter) {
2860
+ throw new error.MastraError({
2861
+ id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
2862
+ domain: error.ErrorDomain.STORAGE,
2863
+ category: error.ErrorCategory.USER,
2864
+ text: "Either id or filter must be provided",
2865
+ details: { indexName }
2866
+ });
2867
+ }
2868
+ if ("filter" in params && params.filter && Object.keys(params.filter).length === 0) {
2869
+ throw new error.MastraError({
2870
+ id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
2871
+ domain: error.ErrorDomain.STORAGE,
2872
+ category: error.ErrorCategory.USER,
2873
+ text: "Cannot update with empty filter",
2874
+ details: { indexName }
2875
+ });
2876
+ }
2877
+ if (!update.vector && !update.metadata) {
2878
+ throw new error.MastraError({
2879
+ id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
2880
+ domain: error.ErrorDomain.STORAGE,
2881
+ category: error.ErrorCategory.USER,
2882
+ text: "No updates provided",
2883
+ details: { indexName }
2884
+ });
2885
+ }
2054
2886
  try {
2055
2887
  if (!this.lanceClient) {
2056
2888
  throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
@@ -2058,21 +2890,6 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2058
2890
  if (!indexName) {
2059
2891
  throw new Error("indexName is required");
2060
2892
  }
2061
- if (!id) {
2062
- throw new Error("id is required");
2063
- }
2064
- } catch (err) {
2065
- throw new error.MastraError(
2066
- {
2067
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED_INVALID_ARGS",
2068
- domain: error.ErrorDomain.STORAGE,
2069
- category: error.ErrorCategory.USER,
2070
- details: { indexName, id }
2071
- },
2072
- err
2073
- );
2074
- }
2075
- try {
2076
2893
  const tables = await this.lanceClient.tableNames();
2077
2894
  for (const tableName of tables) {
2078
2895
  this.logger.debug("Checking table:" + tableName);
@@ -2082,39 +2899,66 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2082
2899
  const hasColumn = schema.fields.some((field) => field.name === indexName);
2083
2900
  if (hasColumn) {
2084
2901
  this.logger.debug(`Found column ${indexName} in table ${tableName}`);
2085
- const existingRecord = await table.query().where(`id = '${id}'`).select(schema.fields.map((field) => field.name)).limit(1).toArray();
2086
- if (existingRecord.length === 0) {
2087
- throw new Error(`Record with id '${id}' not found in table ${tableName}`);
2902
+ let whereClause;
2903
+ if ("id" in params && params.id) {
2904
+ whereClause = `id = '${params.id}'`;
2905
+ } else if ("filter" in params && params.filter) {
2906
+ const translator = new LanceFilterTranslator();
2907
+ const processFilterKeys = (filter) => {
2908
+ const processedFilter = {};
2909
+ Object.entries(filter).forEach(([key, value]) => {
2910
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
2911
+ Object.entries(value).forEach(([nestedKey, nestedValue]) => {
2912
+ processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
2913
+ });
2914
+ } else {
2915
+ processedFilter[`metadata_${key}`] = value;
2916
+ }
2917
+ });
2918
+ return processedFilter;
2919
+ };
2920
+ const prefixedFilter = processFilterKeys(params.filter);
2921
+ whereClause = translator.translate(prefixedFilter) || "";
2922
+ if (!whereClause) {
2923
+ throw new Error("Failed to translate filter to SQL");
2924
+ }
2925
+ } else {
2926
+ throw new Error("Either id or filter must be provided");
2088
2927
  }
2089
- const rowData = {
2090
- id
2091
- };
2092
- Object.entries(existingRecord[0]).forEach(([key, value]) => {
2093
- if (key !== "id" && key !== "_distance") {
2094
- if (key === indexName) {
2095
- if (!update.vector) {
2096
- if (Array.isArray(value)) {
2097
- rowData[key] = [...value];
2098
- } else if (typeof value === "object" && value !== null) {
2099
- rowData[key] = Array.from(value);
2928
+ const existingRecords = await table.query().where(whereClause).select(schema.fields.map((field) => field.name)).toArray();
2929
+ if (existingRecords.length === 0) {
2930
+ this.logger.info(`No records found matching criteria in table ${tableName}`);
2931
+ return;
2932
+ }
2933
+ const updatedRecords = existingRecords.map((record) => {
2934
+ const rowData = {};
2935
+ Object.entries(record).forEach(([key, value]) => {
2936
+ if (key !== "_distance") {
2937
+ if (key === indexName) {
2938
+ if (update.vector) {
2939
+ rowData[key] = update.vector;
2100
2940
  } else {
2101
- rowData[key] = value;
2941
+ if (Array.isArray(value)) {
2942
+ rowData[key] = [...value];
2943
+ } else if (typeof value === "object" && value !== null) {
2944
+ rowData[key] = Array.from(value);
2945
+ } else {
2946
+ rowData[key] = value;
2947
+ }
2102
2948
  }
2949
+ } else {
2950
+ rowData[key] = value;
2103
2951
  }
2104
- } else {
2105
- rowData[key] = value;
2106
2952
  }
2953
+ });
2954
+ if (update.metadata) {
2955
+ Object.entries(update.metadata).forEach(([key, value]) => {
2956
+ rowData[`metadata_${key}`] = value;
2957
+ });
2107
2958
  }
2959
+ return rowData;
2108
2960
  });
2109
- if (update.vector) {
2110
- rowData[indexName] = update.vector;
2111
- }
2112
- if (update.metadata) {
2113
- Object.entries(update.metadata).forEach(([key, value]) => {
2114
- rowData[`metadata_${key}`] = value;
2115
- });
2116
- }
2117
- await table.add([rowData], { mode: "overwrite" });
2961
+ await table.add(updatedRecords, { mode: "overwrite" });
2118
2962
  return;
2119
2963
  }
2120
2964
  } catch (err) {
@@ -2124,12 +2968,19 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2124
2968
  }
2125
2969
  throw new Error(`No table found with column/index '${indexName}'`);
2126
2970
  } catch (error$1) {
2971
+ if (error$1 instanceof error.MastraError) throw error$1;
2127
2972
  throw new error.MastraError(
2128
2973
  {
2129
2974
  id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED",
2130
2975
  domain: error.ErrorDomain.STORAGE,
2131
2976
  category: error.ErrorCategory.THIRD_PARTY,
2132
- details: { indexName, id, hasVector: !!update.vector, hasMetadata: !!update.metadata }
2977
+ details: {
2978
+ indexName,
2979
+ ..."id" in params && params.id && { id: params.id },
2980
+ ..."filter" in params && params.filter && { filter: JSON.stringify(params.filter) },
2981
+ hasVector: !!update.vector,
2982
+ hasMetadata: !!update.metadata
2983
+ }
2133
2984
  },
2134
2985
  error$1
2135
2986
  );
@@ -2152,7 +3003,10 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2152
3003
  id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED_INVALID_ARGS",
2153
3004
  domain: error.ErrorDomain.STORAGE,
2154
3005
  category: error.ErrorCategory.USER,
2155
- details: { indexName, id }
3006
+ details: {
3007
+ indexName,
3008
+ ...id && { id }
3009
+ }
2156
3010
  },
2157
3011
  err
2158
3012
  );
@@ -2182,7 +3036,10 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2182
3036
  id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED",
2183
3037
  domain: error.ErrorDomain.STORAGE,
2184
3038
  category: error.ErrorCategory.THIRD_PARTY,
2185
- details: { indexName, id }
3039
+ details: {
3040
+ indexName,
3041
+ ...id && { id }
3042
+ }
2186
3043
  },
2187
3044
  error$1
2188
3045
  );
@@ -2213,7 +3070,112 @@ var LanceVectorStore = class _LanceVectorStore extends vector.MastraVector {
2213
3070
  });
2214
3071
  return result;
2215
3072
  }
3073
+ async deleteVectors({ indexName, filter, ids }) {
3074
+ if (ids && filter) {
3075
+ throw new error.MastraError({
3076
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3077
+ domain: error.ErrorDomain.STORAGE,
3078
+ category: error.ErrorCategory.USER,
3079
+ text: "ids and filter are mutually exclusive",
3080
+ details: { indexName }
3081
+ });
3082
+ }
3083
+ if (!ids && !filter) {
3084
+ throw new error.MastraError({
3085
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3086
+ domain: error.ErrorDomain.STORAGE,
3087
+ category: error.ErrorCategory.USER,
3088
+ text: "Either filter or ids must be provided",
3089
+ details: { indexName }
3090
+ });
3091
+ }
3092
+ if (ids && ids.length === 0) {
3093
+ throw new error.MastraError({
3094
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3095
+ domain: error.ErrorDomain.STORAGE,
3096
+ category: error.ErrorCategory.USER,
3097
+ text: "Cannot delete with empty ids array",
3098
+ details: { indexName }
3099
+ });
3100
+ }
3101
+ if (filter && Object.keys(filter).length === 0) {
3102
+ throw new error.MastraError({
3103
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3104
+ domain: error.ErrorDomain.STORAGE,
3105
+ category: error.ErrorCategory.USER,
3106
+ text: "Cannot delete with empty filter",
3107
+ details: { indexName }
3108
+ });
3109
+ }
3110
+ try {
3111
+ if (!this.lanceClient) {
3112
+ throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
3113
+ }
3114
+ if (!indexName) {
3115
+ throw new Error("indexName is required");
3116
+ }
3117
+ const tables = await this.lanceClient.tableNames();
3118
+ for (const tableName of tables) {
3119
+ this.logger.debug("Checking table:" + tableName);
3120
+ const table = await this.lanceClient.openTable(tableName);
3121
+ try {
3122
+ const schema = await table.schema();
3123
+ const hasColumn = schema.fields.some((field) => field.name === indexName);
3124
+ if (hasColumn) {
3125
+ this.logger.debug(`Found column ${indexName} in table ${tableName}`);
3126
+ if (ids) {
3127
+ const idsConditions = ids.map((id) => `id = '${id}'`).join(" OR ");
3128
+ await table.delete(idsConditions);
3129
+ } else if (filter) {
3130
+ const translator = new LanceFilterTranslator();
3131
+ const processFilterKeys = (filter2) => {
3132
+ const processedFilter = {};
3133
+ Object.entries(filter2).forEach(([key, value]) => {
3134
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3135
+ Object.entries(value).forEach(([nestedKey, nestedValue]) => {
3136
+ processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
3137
+ });
3138
+ } else {
3139
+ processedFilter[`metadata_${key}`] = value;
3140
+ }
3141
+ });
3142
+ return processedFilter;
3143
+ };
3144
+ const prefixedFilter = processFilterKeys(filter);
3145
+ const whereClause = translator.translate(prefixedFilter);
3146
+ if (!whereClause) {
3147
+ throw new Error("Failed to translate filter to SQL");
3148
+ }
3149
+ await table.delete(whereClause);
3150
+ }
3151
+ return;
3152
+ }
3153
+ } catch (err) {
3154
+ this.logger.error(`Error checking schema for table ${tableName}:` + err);
3155
+ continue;
3156
+ }
3157
+ }
3158
+ throw new Error(`No table found with column/index '${indexName}'`);
3159
+ } catch (error$1) {
3160
+ if (error$1 instanceof error.MastraError) throw error$1;
3161
+ throw new error.MastraError(
3162
+ {
3163
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_FAILED",
3164
+ domain: error.ErrorDomain.STORAGE,
3165
+ category: error.ErrorCategory.THIRD_PARTY,
3166
+ details: {
3167
+ indexName,
3168
+ ...filter && { filter: JSON.stringify(filter) },
3169
+ ...ids && { idsCount: ids.length }
3170
+ }
3171
+ },
3172
+ error$1
3173
+ );
3174
+ }
3175
+ }
2216
3176
  };
2217
3177
 
2218
3178
  exports.LanceStorage = LanceStorage;
2219
3179
  exports.LanceVectorStore = LanceVectorStore;
3180
+ //# sourceMappingURL=index.cjs.map
3181
+ //# sourceMappingURL=index.cjs.map