@librechat/data-schemas 0.0.33 → 0.0.35

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.es.js CHANGED
@@ -2244,9 +2244,10 @@ const rolePermissionsSchema = new Schema({
2244
2244
  [Permissions.USE]: { type: Boolean },
2245
2245
  },
2246
2246
  [PermissionTypes.PROMPTS]: {
2247
- [Permissions.SHARED_GLOBAL]: { type: Boolean },
2248
2247
  [Permissions.USE]: { type: Boolean },
2249
2248
  [Permissions.CREATE]: { type: Boolean },
2249
+ [Permissions.SHARE]: { type: Boolean },
2250
+ [Permissions.SHARE_PUBLIC]: { type: Boolean },
2250
2251
  },
2251
2252
  [PermissionTypes.MEMORIES]: {
2252
2253
  [Permissions.USE]: { type: Boolean },
@@ -2256,9 +2257,10 @@ const rolePermissionsSchema = new Schema({
2256
2257
  [Permissions.OPT_OUT]: { type: Boolean },
2257
2258
  },
2258
2259
  [PermissionTypes.AGENTS]: {
2259
- [Permissions.SHARED_GLOBAL]: { type: Boolean },
2260
2260
  [Permissions.USE]: { type: Boolean },
2261
2261
  [Permissions.CREATE]: { type: Boolean },
2262
+ [Permissions.SHARE]: { type: Boolean },
2263
+ [Permissions.SHARE_PUBLIC]: { type: Boolean },
2262
2264
  },
2263
2265
  [PermissionTypes.MULTI_CONVO]: {
2264
2266
  [Permissions.USE]: { type: Boolean },
@@ -2290,6 +2292,7 @@ const rolePermissionsSchema = new Schema({
2290
2292
  [Permissions.USE]: { type: Boolean },
2291
2293
  [Permissions.CREATE]: { type: Boolean },
2292
2294
  [Permissions.SHARE]: { type: Boolean },
2295
+ [Permissions.SHARE_PUBLIC]: { type: Boolean },
2293
2296
  },
2294
2297
  }, { _id: false });
2295
2298
  const roleSchema = new Schema({
@@ -2855,8 +2858,8 @@ const createMeiliMongooseModel = ({ index, attributesToIndex, syncOptions, }) =>
2855
2858
  * Get the current sync progress
2856
2859
  */
2857
2860
  static async getSyncProgress() {
2858
- const totalDocuments = await this.countDocuments();
2859
- const indexedDocuments = await this.countDocuments({ _meiliIndex: true });
2861
+ const totalDocuments = await this.countDocuments({ expiredAt: null });
2862
+ const indexedDocuments = await this.countDocuments({ expiredAt: null, _meiliIndex: true });
2860
2863
  return {
2861
2864
  totalProcessed: indexedDocuments,
2862
2865
  totalDocuments,
@@ -2864,93 +2867,84 @@ const createMeiliMongooseModel = ({ index, attributesToIndex, syncOptions, }) =>
2864
2867
  };
2865
2868
  }
2866
2869
  /**
2867
- * Synchronizes the data between the MongoDB collection and the MeiliSearch index.
2868
- * Now uses streaming and batching to reduce memory usage.
2869
- */
2870
- static async syncWithMeili(options) {
2870
+ * Synchronizes data between the MongoDB collection and the MeiliSearch index by
2871
+ * incrementally indexing only documents where `expiredAt` is `null` and `_meiliIndex` is `false`
2872
+ * (i.e., non-expired documents that have not yet been indexed).
2873
+ * */
2874
+ static async syncWithMeili() {
2875
+ const startTime = Date.now();
2876
+ const { batchSize, delayMs } = syncConfig;
2877
+ const collectionName = primaryKey === 'messageId' ? 'messages' : 'conversations';
2878
+ logger.info(`[syncWithMeili] Starting sync for ${collectionName} with batch size ${batchSize}`);
2879
+ // Get approximate total count for raw estimation, the sync should not overcome this number
2880
+ const approxTotalCount = await this.estimatedDocumentCount();
2881
+ logger.info(`[syncWithMeili] Approximate total number of all ${collectionName}: ${approxTotalCount}`);
2871
2882
  try {
2872
- const startTime = Date.now();
2873
- const { batchSize, delayMs } = syncConfig;
2874
- logger.info(`[syncWithMeili] Starting sync for ${primaryKey === 'messageId' ? 'messages' : 'conversations'} with batch size ${batchSize}`);
2875
- // Build query with resume capability
2876
- // Do not sync TTL documents
2877
- const query = { expiredAt: null };
2878
- if (options === null || options === void 0 ? void 0 : options.resumeFromId) {
2879
- query._id = { $gt: options.resumeFromId };
2880
- }
2881
- // Get total count for progress tracking
2882
- const totalCount = await this.countDocuments(query);
2883
- let processedCount = 0;
2884
2883
  // First, handle documents that need to be removed from Meili
2884
+ logger.info(`[syncWithMeili] Starting cleanup of Meili index ${index.uid} before sync`);
2885
2885
  await this.cleanupMeiliIndex(index, primaryKey, batchSize, delayMs);
2886
- // Process MongoDB documents in batches using cursor
2887
- const cursor = this.find(query)
2888
- .select(attributesToIndex.join(' ') + ' _meiliIndex')
2889
- .sort({ _id: 1 })
2890
- .batchSize(batchSize)
2891
- .cursor();
2892
- const format = (doc) => _.omitBy(_.pick(doc, attributesToIndex), (v, k) => k.startsWith('$'));
2893
- let documentBatch = [];
2894
- let updateOps = [];
2895
- // Process documents in streaming fashion
2896
- for await (const doc of cursor) {
2897
- const typedDoc = doc.toObject();
2898
- const formatted = format(typedDoc);
2899
- // Check if document needs indexing
2900
- if (!typedDoc._meiliIndex) {
2901
- documentBatch.push(formatted);
2902
- updateOps.push({
2903
- updateOne: {
2904
- filter: { _id: typedDoc._id },
2905
- update: { $set: { _meiliIndex: true } },
2906
- },
2907
- });
2886
+ logger.info(`[syncWithMeili] Completed cleanup of Meili index: ${index.uid}`);
2887
+ }
2888
+ catch (error) {
2889
+ logger.error('[syncWithMeili] Error during cleanup Meili before sync:', error);
2890
+ throw error;
2891
+ }
2892
+ let processedCount = 0;
2893
+ let hasMore = true;
2894
+ while (hasMore) {
2895
+ const query = {
2896
+ expiredAt: null,
2897
+ _meiliIndex: false,
2898
+ };
2899
+ try {
2900
+ const documents = await this.find(query)
2901
+ .select(attributesToIndex.join(' ') + ' _meiliIndex')
2902
+ .limit(batchSize)
2903
+ .lean();
2904
+ // Check if there are more documents to process
2905
+ if (documents.length === 0) {
2906
+ logger.info('[syncWithMeili] No more documents to process');
2907
+ break;
2908
2908
  }
2909
- processedCount++;
2910
- // Process batch when it reaches the configured size
2911
- if (documentBatch.length >= batchSize) {
2912
- await this.processSyncBatch(index, documentBatch, updateOps);
2913
- documentBatch = [];
2914
- updateOps = [];
2915
- // Log progress
2916
- const progress = Math.round((processedCount / totalCount) * 100);
2917
- logger.info(`[syncWithMeili] Progress: ${progress}% (${processedCount}/${totalCount})`);
2918
- // Add delay to prevent overwhelming resources
2919
- if (delayMs > 0) {
2920
- await new Promise((resolve) => setTimeout(resolve, delayMs));
2921
- }
2909
+ // Process the batch
2910
+ await this.processSyncBatch(index, documents);
2911
+ processedCount += documents.length;
2912
+ logger.info(`[syncWithMeili] Processed: ${processedCount}`);
2913
+ if (documents.length < batchSize) {
2914
+ hasMore = false;
2915
+ }
2916
+ // Add delay to prevent overwhelming resources
2917
+ if (hasMore && delayMs > 0) {
2918
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
2922
2919
  }
2923
2920
  }
2924
- // Process remaining documents
2925
- if (documentBatch.length > 0) {
2926
- await this.processSyncBatch(index, documentBatch, updateOps);
2921
+ catch (error) {
2922
+ logger.error('[syncWithMeili] Error processing documents batch:', error);
2923
+ throw error;
2927
2924
  }
2928
- const duration = Date.now() - startTime;
2929
- logger.info(`[syncWithMeili] Completed sync for ${primaryKey === 'messageId' ? 'messages' : 'conversations'} in ${duration}ms`);
2930
- }
2931
- catch (error) {
2932
- logger.error('[syncWithMeili] Error during sync:', error);
2933
- throw error;
2934
2925
  }
2926
+ const duration = Date.now() - startTime;
2927
+ logger.info(`[syncWithMeili] Completed sync for ${collectionName}. Processed ${processedCount} documents in ${duration}ms`);
2935
2928
  }
2936
2929
  /**
2937
2930
  * Process a batch of documents for syncing
2938
2931
  */
2939
- static async processSyncBatch(index, documents, updateOps) {
2932
+ static async processSyncBatch(index, documents) {
2940
2933
  if (documents.length === 0) {
2941
2934
  return;
2942
2935
  }
2936
+ // Format documents for MeiliSearch
2937
+ const formattedDocs = documents.map((doc) => _.omitBy(_.pick(doc, attributesToIndex), (_v, k) => k.startsWith('$')));
2943
2938
  try {
2944
2939
  // Add documents to MeiliSearch
2945
- await index.addDocuments(documents);
2940
+ await index.addDocumentsInBatches(formattedDocs);
2946
2941
  // Update MongoDB to mark documents as indexed
2947
- if (updateOps.length > 0) {
2948
- await this.collection.bulkWrite(updateOps);
2949
- }
2942
+ const docsIds = documents.map((doc) => doc._id);
2943
+ await this.updateMany({ _id: { $in: docsIds } }, { $set: { _meiliIndex: true } });
2950
2944
  }
2951
2945
  catch (error) {
2952
2946
  logger.error('[processSyncBatch] Error processing batch:', error);
2953
- // Don't throw - allow sync to continue with other documents
2947
+ throw error;
2954
2948
  }
2955
2949
  }
2956
2950
  /**
@@ -2975,10 +2969,14 @@ const createMeiliMongooseModel = ({ index, attributesToIndex, syncOptions, }) =>
2975
2969
  // Delete documents that don't exist in MongoDB
2976
2970
  const toDelete = meiliIds.filter((id) => !existingIds.has(id));
2977
2971
  if (toDelete.length > 0) {
2978
- await Promise.all(toDelete.map((id) => index.deleteDocument(id)));
2972
+ await index.deleteDocuments(toDelete.map(String));
2979
2973
  logger.debug(`[cleanupMeiliIndex] Deleted ${toDelete.length} orphaned documents`);
2980
2974
  }
2981
- offset += batchSize;
2975
+ // if fetch documents request returns less documents than limit, all documents are processed
2976
+ if (batch.results.length < batchSize) {
2977
+ break;
2978
+ }
2979
+ offset += batchSize - toDelete.length;
2982
2980
  // Add delay between batches
2983
2981
  if (delayMs > 0) {
2984
2982
  await new Promise((resolve) => setTimeout(resolve, delayMs));
@@ -5076,10 +5074,10 @@ function createMCPServerMethods(mongoose) {
5076
5074
  }
5077
5075
  /**
5078
5076
  * Find an MCP server by serverName
5079
- * @param serverName - The MCP server ID
5077
+ * @param serverName - The unique server name identifier
5080
5078
  * @returns The MCP server document or null
5081
5079
  */
5082
- async function findMCPServerById(serverName) {
5080
+ async function findMCPServerByServerName(serverName) {
5083
5081
  const MCPServer = mongoose.models.MCPServer;
5084
5082
  return await MCPServer.findOne({ serverName }).lean();
5085
5083
  }
@@ -5208,7 +5206,7 @@ function createMCPServerMethods(mongoose) {
5208
5206
  }
5209
5207
  return {
5210
5208
  createMCPServer,
5211
- findMCPServerById,
5209
+ findMCPServerByServerName,
5212
5210
  findMCPServerByObjectId,
5213
5211
  findMCPServersByAuthor,
5214
5212
  getListMCPServersByIds,