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