@mastra/lance 0.0.0-share-agent-metadata-with-cloud-20250718123411 → 0.0.0-sidebar-window-undefined-fix-20251029233656

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