@mastra/lance 1.0.0-beta.1 → 1.0.0-beta.3

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.
package/dist/index.js CHANGED
@@ -292,15 +292,16 @@ var StoreMemoryLance = class extends MemoryStorage {
292
292
  }
293
293
  async listMessages(args) {
294
294
  const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
295
- if (!threadId.trim()) {
295
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
296
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
296
297
  throw new MastraError(
297
298
  {
298
299
  id: "STORAGE_LANCE_LIST_MESSAGES_INVALID_THREAD_ID",
299
300
  domain: ErrorDomain.STORAGE,
300
301
  category: ErrorCategory.THIRD_PARTY,
301
- details: { threadId }
302
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
302
303
  },
303
- new Error("threadId must be a non-empty string")
304
+ new Error("threadId must be a non-empty string or array of non-empty strings")
304
305
  );
305
306
  }
306
307
  const perPage = normalizePerPage(perPageInput, 40);
@@ -319,7 +320,8 @@ var StoreMemoryLance = class extends MemoryStorage {
319
320
  }
320
321
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
321
322
  const table = await this.client.openTable(TABLE_MESSAGES);
322
- const conditions = [`thread_id = '${this.escapeSql(threadId)}'`];
323
+ const threadCondition = threadIds.length === 1 ? `thread_id = '${this.escapeSql(threadIds[0])}'` : `thread_id IN (${threadIds.map((t) => `'${this.escapeSql(t)}'`).join(", ")})`;
324
+ const conditions = [threadCondition];
323
325
  if (resourceId) {
324
326
  conditions.push(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
325
327
  }
@@ -359,9 +361,9 @@ var StoreMemoryLance = class extends MemoryStorage {
359
361
  }
360
362
  const messageIds = new Set(messages.map((m) => m.id));
361
363
  if (include && include.length > 0) {
362
- const threadIds = [...new Set(include.map((item) => item.threadId || threadId))];
364
+ const threadIds2 = [...new Set(include.map((item) => item.threadId || threadId))];
363
365
  const allThreadMessages = [];
364
- for (const tid of threadIds) {
366
+ for (const tid of threadIds2) {
365
367
  const threadQuery = table.query().where(`thread_id = '${tid}'`);
366
368
  let threadRecords = await threadQuery.toArray();
367
369
  allThreadMessages.push(...threadRecords);
@@ -407,7 +409,7 @@ var StoreMemoryLance = class extends MemoryStorage {
407
409
  domain: ErrorDomain.STORAGE,
408
410
  category: ErrorCategory.THIRD_PARTY,
409
411
  details: {
410
- threadId,
412
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
411
413
  resourceId: resourceId ?? ""
412
414
  }
413
415
  },
@@ -1266,6 +1268,8 @@ var StoreScoresLance = class extends ScoresStorage {
1266
1268
  filteredScore[key] = JSON.stringify(filteredScore[key]);
1267
1269
  }
1268
1270
  }
1271
+ filteredScore.createdAt = /* @__PURE__ */ new Date();
1272
+ filteredScore.updatedAt = /* @__PURE__ */ new Date();
1269
1273
  filteredScore.id = id;
1270
1274
  await table.add([filteredScore], { mode: "append" });
1271
1275
  return { score };
@@ -1288,8 +1292,7 @@ var StoreScoresLance = class extends ScoresStorage {
1288
1292
  const query = table.query().where(`id = '${id}'`).limit(1);
1289
1293
  const records = await query.toArray();
1290
1294
  if (records.length === 0) return null;
1291
- const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
1292
- return processResultWithTypeConversion(records[0], schema);
1295
+ return await this.transformScoreRow(records[0]);
1293
1296
  } catch (error) {
1294
1297
  throw new MastraError(
1295
1298
  {
@@ -1303,6 +1306,22 @@ var StoreScoresLance = class extends ScoresStorage {
1303
1306
  );
1304
1307
  }
1305
1308
  }
1309
+ /**
1310
+ * LanceDB-specific score row transformation.
1311
+ *
1312
+ * Note: This implementation does NOT use coreTransformScoreRow because:
1313
+ * 1. LanceDB stores schema information in the table itself (requires async fetch)
1314
+ * 2. Uses processResultWithTypeConversion utility for LanceDB-specific type handling
1315
+ */
1316
+ async transformScoreRow(row) {
1317
+ const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
1318
+ const transformed = processResultWithTypeConversion(row, schema);
1319
+ return {
1320
+ ...transformed,
1321
+ createdAt: row.createdAt,
1322
+ updatedAt: row.updatedAt
1323
+ };
1324
+ }
1306
1325
  async listScoresByScorerId({
1307
1326
  scorerId,
1308
1327
  pagination,
@@ -1343,8 +1362,7 @@ var StoreScoresLance = class extends ScoresStorage {
1343
1362
  if (start > 0) query = query.offset(start);
1344
1363
  }
1345
1364
  const records = await query.toArray();
1346
- const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
1347
- const scores = processResultWithTypeConversion(records, schema);
1365
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1348
1366
  return {
1349
1367
  pagination: {
1350
1368
  page,
@@ -1385,8 +1403,7 @@ var StoreScoresLance = class extends ScoresStorage {
1385
1403
  if (start > 0) query = query.offset(start);
1386
1404
  }
1387
1405
  const records = await query.toArray();
1388
- const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
1389
- const scores = processResultWithTypeConversion(records, schema);
1406
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1390
1407
  return {
1391
1408
  pagination: {
1392
1409
  page,
@@ -1428,8 +1445,7 @@ var StoreScoresLance = class extends ScoresStorage {
1428
1445
  if (start > 0) query = query.offset(start);
1429
1446
  }
1430
1447
  const records = await query.toArray();
1431
- const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
1432
- const scores = processResultWithTypeConversion(records, schema);
1448
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1433
1449
  return {
1434
1450
  pagination: {
1435
1451
  page,
@@ -1471,8 +1487,7 @@ var StoreScoresLance = class extends ScoresStorage {
1471
1487
  if (start > 0) query = query.offset(start);
1472
1488
  }
1473
1489
  const records = await query.toArray();
1474
- const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
1475
- const scores = processResultWithTypeConversion(records, schema);
1490
+ const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
1476
1491
  return {
1477
1492
  pagination: {
1478
1493
  page,
@@ -2843,7 +2858,44 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2843
2858
  );
2844
2859
  }
2845
2860
  }
2846
- async updateVector({ indexName, id, update }) {
2861
+ async updateVector(params) {
2862
+ const { indexName, update } = params;
2863
+ if ("id" in params && "filter" in params && params.id && params.filter) {
2864
+ throw new MastraError({
2865
+ id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
2866
+ domain: ErrorDomain.STORAGE,
2867
+ category: ErrorCategory.USER,
2868
+ text: "id and filter are mutually exclusive",
2869
+ details: { indexName }
2870
+ });
2871
+ }
2872
+ if (!("id" in params || "filter" in params) || !params.id && !params.filter) {
2873
+ throw new MastraError({
2874
+ id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
2875
+ domain: ErrorDomain.STORAGE,
2876
+ category: ErrorCategory.USER,
2877
+ text: "Either id or filter must be provided",
2878
+ details: { indexName }
2879
+ });
2880
+ }
2881
+ if ("filter" in params && params.filter && Object.keys(params.filter).length === 0) {
2882
+ throw new MastraError({
2883
+ id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
2884
+ domain: ErrorDomain.STORAGE,
2885
+ category: ErrorCategory.USER,
2886
+ text: "Cannot update with empty filter",
2887
+ details: { indexName }
2888
+ });
2889
+ }
2890
+ if (!update.vector && !update.metadata) {
2891
+ throw new MastraError({
2892
+ id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
2893
+ domain: ErrorDomain.STORAGE,
2894
+ category: ErrorCategory.USER,
2895
+ text: "No updates provided",
2896
+ details: { indexName }
2897
+ });
2898
+ }
2847
2899
  try {
2848
2900
  if (!this.lanceClient) {
2849
2901
  throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
@@ -2851,21 +2903,6 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2851
2903
  if (!indexName) {
2852
2904
  throw new Error("indexName is required");
2853
2905
  }
2854
- if (!id) {
2855
- throw new Error("id is required");
2856
- }
2857
- } catch (err) {
2858
- throw new MastraError(
2859
- {
2860
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED_INVALID_ARGS",
2861
- domain: ErrorDomain.STORAGE,
2862
- category: ErrorCategory.USER,
2863
- details: { indexName, id }
2864
- },
2865
- err
2866
- );
2867
- }
2868
- try {
2869
2906
  const tables = await this.lanceClient.tableNames();
2870
2907
  for (const tableName of tables) {
2871
2908
  this.logger.debug("Checking table:" + tableName);
@@ -2875,39 +2912,66 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2875
2912
  const hasColumn = schema.fields.some((field) => field.name === indexName);
2876
2913
  if (hasColumn) {
2877
2914
  this.logger.debug(`Found column ${indexName} in table ${tableName}`);
2878
- const existingRecord = await table.query().where(`id = '${id}'`).select(schema.fields.map((field) => field.name)).limit(1).toArray();
2879
- if (existingRecord.length === 0) {
2880
- throw new Error(`Record with id '${id}' not found in table ${tableName}`);
2915
+ let whereClause;
2916
+ if ("id" in params && params.id) {
2917
+ whereClause = `id = '${params.id}'`;
2918
+ } else if ("filter" in params && params.filter) {
2919
+ const translator = new LanceFilterTranslator();
2920
+ const processFilterKeys = (filter) => {
2921
+ const processedFilter = {};
2922
+ Object.entries(filter).forEach(([key, value]) => {
2923
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
2924
+ Object.entries(value).forEach(([nestedKey, nestedValue]) => {
2925
+ processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
2926
+ });
2927
+ } else {
2928
+ processedFilter[`metadata_${key}`] = value;
2929
+ }
2930
+ });
2931
+ return processedFilter;
2932
+ };
2933
+ const prefixedFilter = processFilterKeys(params.filter);
2934
+ whereClause = translator.translate(prefixedFilter) || "";
2935
+ if (!whereClause) {
2936
+ throw new Error("Failed to translate filter to SQL");
2937
+ }
2938
+ } else {
2939
+ throw new Error("Either id or filter must be provided");
2881
2940
  }
2882
- const rowData = {
2883
- id
2884
- };
2885
- Object.entries(existingRecord[0]).forEach(([key, value]) => {
2886
- if (key !== "id" && key !== "_distance") {
2887
- if (key === indexName) {
2888
- if (!update.vector) {
2889
- if (Array.isArray(value)) {
2890
- rowData[key] = [...value];
2891
- } else if (typeof value === "object" && value !== null) {
2892
- rowData[key] = Array.from(value);
2941
+ const existingRecords = await table.query().where(whereClause).select(schema.fields.map((field) => field.name)).toArray();
2942
+ if (existingRecords.length === 0) {
2943
+ this.logger.info(`No records found matching criteria in table ${tableName}`);
2944
+ return;
2945
+ }
2946
+ const updatedRecords = existingRecords.map((record) => {
2947
+ const rowData = {};
2948
+ Object.entries(record).forEach(([key, value]) => {
2949
+ if (key !== "_distance") {
2950
+ if (key === indexName) {
2951
+ if (update.vector) {
2952
+ rowData[key] = update.vector;
2893
2953
  } else {
2894
- rowData[key] = value;
2954
+ if (Array.isArray(value)) {
2955
+ rowData[key] = [...value];
2956
+ } else if (typeof value === "object" && value !== null) {
2957
+ rowData[key] = Array.from(value);
2958
+ } else {
2959
+ rowData[key] = value;
2960
+ }
2895
2961
  }
2962
+ } else {
2963
+ rowData[key] = value;
2896
2964
  }
2897
- } else {
2898
- rowData[key] = value;
2899
2965
  }
2966
+ });
2967
+ if (update.metadata) {
2968
+ Object.entries(update.metadata).forEach(([key, value]) => {
2969
+ rowData[`metadata_${key}`] = value;
2970
+ });
2900
2971
  }
2972
+ return rowData;
2901
2973
  });
2902
- if (update.vector) {
2903
- rowData[indexName] = update.vector;
2904
- }
2905
- if (update.metadata) {
2906
- Object.entries(update.metadata).forEach(([key, value]) => {
2907
- rowData[`metadata_${key}`] = value;
2908
- });
2909
- }
2910
- await table.add([rowData], { mode: "overwrite" });
2974
+ await table.add(updatedRecords, { mode: "overwrite" });
2911
2975
  return;
2912
2976
  }
2913
2977
  } catch (err) {
@@ -2917,12 +2981,19 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2917
2981
  }
2918
2982
  throw new Error(`No table found with column/index '${indexName}'`);
2919
2983
  } catch (error) {
2984
+ if (error instanceof MastraError) throw error;
2920
2985
  throw new MastraError(
2921
2986
  {
2922
2987
  id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED",
2923
2988
  domain: ErrorDomain.STORAGE,
2924
2989
  category: ErrorCategory.THIRD_PARTY,
2925
- details: { indexName, id, hasVector: !!update.vector, hasMetadata: !!update.metadata }
2990
+ details: {
2991
+ indexName,
2992
+ ..."id" in params && params.id && { id: params.id },
2993
+ ..."filter" in params && params.filter && { filter: JSON.stringify(params.filter) },
2994
+ hasVector: !!update.vector,
2995
+ hasMetadata: !!update.metadata
2996
+ }
2926
2997
  },
2927
2998
  error
2928
2999
  );
@@ -2945,7 +3016,10 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2945
3016
  id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED_INVALID_ARGS",
2946
3017
  domain: ErrorDomain.STORAGE,
2947
3018
  category: ErrorCategory.USER,
2948
- details: { indexName, id }
3019
+ details: {
3020
+ indexName,
3021
+ ...id && { id }
3022
+ }
2949
3023
  },
2950
3024
  err
2951
3025
  );
@@ -2975,7 +3049,10 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2975
3049
  id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED",
2976
3050
  domain: ErrorDomain.STORAGE,
2977
3051
  category: ErrorCategory.THIRD_PARTY,
2978
- details: { indexName, id }
3052
+ details: {
3053
+ indexName,
3054
+ ...id && { id }
3055
+ }
2979
3056
  },
2980
3057
  error
2981
3058
  );
@@ -3006,6 +3083,109 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
3006
3083
  });
3007
3084
  return result;
3008
3085
  }
3086
+ async deleteVectors({ indexName, filter, ids }) {
3087
+ if (ids && filter) {
3088
+ throw new MastraError({
3089
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3090
+ domain: ErrorDomain.STORAGE,
3091
+ category: ErrorCategory.USER,
3092
+ text: "ids and filter are mutually exclusive",
3093
+ details: { indexName }
3094
+ });
3095
+ }
3096
+ if (!ids && !filter) {
3097
+ throw new MastraError({
3098
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3099
+ domain: ErrorDomain.STORAGE,
3100
+ category: ErrorCategory.USER,
3101
+ text: "Either filter or ids must be provided",
3102
+ details: { indexName }
3103
+ });
3104
+ }
3105
+ if (ids && ids.length === 0) {
3106
+ throw new MastraError({
3107
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3108
+ domain: ErrorDomain.STORAGE,
3109
+ category: ErrorCategory.USER,
3110
+ text: "Cannot delete with empty ids array",
3111
+ details: { indexName }
3112
+ });
3113
+ }
3114
+ if (filter && Object.keys(filter).length === 0) {
3115
+ throw new MastraError({
3116
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3117
+ domain: ErrorDomain.STORAGE,
3118
+ category: ErrorCategory.USER,
3119
+ text: "Cannot delete with empty filter",
3120
+ details: { indexName }
3121
+ });
3122
+ }
3123
+ try {
3124
+ if (!this.lanceClient) {
3125
+ throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
3126
+ }
3127
+ if (!indexName) {
3128
+ throw new Error("indexName is required");
3129
+ }
3130
+ const tables = await this.lanceClient.tableNames();
3131
+ for (const tableName of tables) {
3132
+ this.logger.debug("Checking table:" + tableName);
3133
+ const table = await this.lanceClient.openTable(tableName);
3134
+ try {
3135
+ const schema = await table.schema();
3136
+ const hasColumn = schema.fields.some((field) => field.name === indexName);
3137
+ if (hasColumn) {
3138
+ this.logger.debug(`Found column ${indexName} in table ${tableName}`);
3139
+ if (ids) {
3140
+ const idsConditions = ids.map((id) => `id = '${id}'`).join(" OR ");
3141
+ await table.delete(idsConditions);
3142
+ } else if (filter) {
3143
+ const translator = new LanceFilterTranslator();
3144
+ const processFilterKeys = (filter2) => {
3145
+ const processedFilter = {};
3146
+ Object.entries(filter2).forEach(([key, value]) => {
3147
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3148
+ Object.entries(value).forEach(([nestedKey, nestedValue]) => {
3149
+ processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
3150
+ });
3151
+ } else {
3152
+ processedFilter[`metadata_${key}`] = value;
3153
+ }
3154
+ });
3155
+ return processedFilter;
3156
+ };
3157
+ const prefixedFilter = processFilterKeys(filter);
3158
+ const whereClause = translator.translate(prefixedFilter);
3159
+ if (!whereClause) {
3160
+ throw new Error("Failed to translate filter to SQL");
3161
+ }
3162
+ await table.delete(whereClause);
3163
+ }
3164
+ return;
3165
+ }
3166
+ } catch (err) {
3167
+ this.logger.error(`Error checking schema for table ${tableName}:` + err);
3168
+ continue;
3169
+ }
3170
+ }
3171
+ throw new Error(`No table found with column/index '${indexName}'`);
3172
+ } catch (error) {
3173
+ if (error instanceof MastraError) throw error;
3174
+ throw new MastraError(
3175
+ {
3176
+ id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_FAILED",
3177
+ domain: ErrorDomain.STORAGE,
3178
+ category: ErrorCategory.THIRD_PARTY,
3179
+ details: {
3180
+ indexName,
3181
+ ...filter && { filter: JSON.stringify(filter) },
3182
+ ...ids && { idsCount: ids.length }
3183
+ }
3184
+ },
3185
+ error
3186
+ );
3187
+ }
3188
+ }
3009
3189
  };
3010
3190
 
3011
3191
  export { LanceStorage, LanceVectorStore };