@librechat/data-schemas 0.0.10 → 0.0.11
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.cjs +244 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +244 -95
- package/dist/index.es.js.map +1 -1
- package/dist/types/methods/index.d.ts +3 -3
- package/dist/types/methods/pluginAuth.d.ts +1 -2
- package/dist/types/models/plugins/mongoMeili.d.ts +26 -2
- package/dist/types/schema/defaults.d.ts +14 -1
- package/dist/types/schema/preset.d.ts +4 -0
- package/dist/types/types/convo.d.ts +4 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -427,10 +427,23 @@ const conversationPreset = {
|
|
|
427
427
|
max_tokens: {
|
|
428
428
|
type: Number,
|
|
429
429
|
},
|
|
430
|
-
|
|
430
|
+
useResponsesApi: {
|
|
431
|
+
type: Boolean,
|
|
432
|
+
},
|
|
433
|
+
/** OpenAI Responses API / Anthropic API / Google API */
|
|
434
|
+
web_search: {
|
|
435
|
+
type: Boolean,
|
|
436
|
+
},
|
|
437
|
+
disableStreaming: {
|
|
438
|
+
type: Boolean,
|
|
439
|
+
},
|
|
440
|
+
/** Reasoning models only */
|
|
431
441
|
reasoning_effort: {
|
|
432
442
|
type: String,
|
|
433
443
|
},
|
|
444
|
+
reasoning_summary: {
|
|
445
|
+
type: String,
|
|
446
|
+
},
|
|
434
447
|
};
|
|
435
448
|
|
|
436
449
|
const convoSchema = new mongoose.Schema({
|
|
@@ -883,7 +896,9 @@ const promptGroupSchema = new mongoose.Schema({
|
|
|
883
896
|
});
|
|
884
897
|
promptGroupSchema.index({ createdAt: 1, updatedAt: 1 });
|
|
885
898
|
|
|
886
|
-
|
|
899
|
+
/**
|
|
900
|
+
* Uses a sub-schema for permissions. Notice we disable `_id` for this subdocument.
|
|
901
|
+
*/
|
|
887
902
|
const rolePermissionsSchema = new mongoose.Schema({
|
|
888
903
|
[librechatDataProvider.PermissionTypes.BOOKMARKS]: {
|
|
889
904
|
[librechatDataProvider.Permissions.USE]: { type: Boolean, default: true },
|
|
@@ -917,6 +932,9 @@ const rolePermissionsSchema = new mongoose.Schema({
|
|
|
917
932
|
[librechatDataProvider.PermissionTypes.WEB_SEARCH]: {
|
|
918
933
|
[librechatDataProvider.Permissions.USE]: { type: Boolean, default: true },
|
|
919
934
|
},
|
|
935
|
+
[librechatDataProvider.PermissionTypes.FILE_SEARCH]: {
|
|
936
|
+
[librechatDataProvider.Permissions.USE]: { type: Boolean, default: true },
|
|
937
|
+
},
|
|
920
938
|
}, { _id: false });
|
|
921
939
|
const roleSchema = new mongoose.Schema({
|
|
922
940
|
name: { type: String, required: true, unique: true, index: true },
|
|
@@ -944,6 +962,7 @@ const roleSchema = new mongoose.Schema({
|
|
|
944
962
|
[librechatDataProvider.PermissionTypes.TEMPORARY_CHAT]: { [librechatDataProvider.Permissions.USE]: true },
|
|
945
963
|
[librechatDataProvider.PermissionTypes.RUN_CODE]: { [librechatDataProvider.Permissions.USE]: true },
|
|
946
964
|
[librechatDataProvider.PermissionTypes.WEB_SEARCH]: { [librechatDataProvider.Permissions.USE]: true },
|
|
965
|
+
[librechatDataProvider.PermissionTypes.FILE_SEARCH]: { [librechatDataProvider.Permissions.USE]: true },
|
|
947
966
|
}),
|
|
948
967
|
},
|
|
949
968
|
});
|
|
@@ -1338,6 +1357,13 @@ const searchEnabled = process.env.SEARCH != null && process.env.SEARCH.toLowerCa
|
|
|
1338
1357
|
* Flag to indicate if MeiliSearch is enabled based on required environment variables.
|
|
1339
1358
|
*/
|
|
1340
1359
|
const meiliEnabled = process.env.MEILI_HOST != null && process.env.MEILI_MASTER_KEY != null && searchEnabled;
|
|
1360
|
+
/**
|
|
1361
|
+
* Get sync configuration from environment variables
|
|
1362
|
+
*/
|
|
1363
|
+
const getSyncConfig = () => ({
|
|
1364
|
+
batchSize: parseInt(process.env.MEILI_SYNC_BATCH_SIZE || '100', 10),
|
|
1365
|
+
delayMs: parseInt(process.env.MEILI_SYNC_DELAY_MS || '100', 10),
|
|
1366
|
+
});
|
|
1341
1367
|
/**
|
|
1342
1368
|
* Local implementation of parseTextParts to avoid dependency on librechat-data-provider
|
|
1343
1369
|
* Extracts text content from an array of content items
|
|
@@ -1369,6 +1395,19 @@ const validateOptions = (options) => {
|
|
|
1369
1395
|
}
|
|
1370
1396
|
});
|
|
1371
1397
|
};
|
|
1398
|
+
/**
|
|
1399
|
+
* Helper function to process documents in batches with rate limiting
|
|
1400
|
+
*/
|
|
1401
|
+
const processBatch = async (items, batchSize, delayMs, processor) => {
|
|
1402
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
1403
|
+
const batch = items.slice(i, i + batchSize);
|
|
1404
|
+
await processor(batch);
|
|
1405
|
+
// Add delay between batches to prevent overwhelming resources
|
|
1406
|
+
if (i + batchSize < items.length && delayMs > 0) {
|
|
1407
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1372
1411
|
/**
|
|
1373
1412
|
* Factory function to create a MeiliMongooseModel class which extends a Mongoose model.
|
|
1374
1413
|
* This class contains static and instance methods to synchronize and manage the MeiliSearch index
|
|
@@ -1377,97 +1416,148 @@ const validateOptions = (options) => {
|
|
|
1377
1416
|
* @param config - Configuration object.
|
|
1378
1417
|
* @param config.index - The MeiliSearch index object.
|
|
1379
1418
|
* @param config.attributesToIndex - List of attributes to index.
|
|
1419
|
+
* @param config.syncOptions - Sync configuration options.
|
|
1380
1420
|
* @returns A class definition that will be loaded into the Mongoose schema.
|
|
1381
1421
|
*/
|
|
1382
|
-
const createMeiliMongooseModel = ({ index, attributesToIndex, }) => {
|
|
1422
|
+
const createMeiliMongooseModel = ({ index, attributesToIndex, syncOptions, }) => {
|
|
1383
1423
|
const primaryKey = attributesToIndex[0];
|
|
1424
|
+
const syncConfig = { ...getSyncConfig(), ...syncOptions };
|
|
1384
1425
|
class MeiliMongooseModel {
|
|
1426
|
+
/**
|
|
1427
|
+
* Get the current sync progress
|
|
1428
|
+
*/
|
|
1429
|
+
static async getSyncProgress() {
|
|
1430
|
+
const totalDocuments = await this.countDocuments();
|
|
1431
|
+
const indexedDocuments = await this.countDocuments({ _meiliIndex: true });
|
|
1432
|
+
return {
|
|
1433
|
+
totalProcessed: indexedDocuments,
|
|
1434
|
+
totalDocuments,
|
|
1435
|
+
isComplete: indexedDocuments === totalDocuments,
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1385
1438
|
/**
|
|
1386
1439
|
* Synchronizes the data between the MongoDB collection and the MeiliSearch index.
|
|
1387
|
-
*
|
|
1388
|
-
* The synchronization process involves:
|
|
1389
|
-
* 1. Fetching all documents from the MongoDB collection and MeiliSearch index.
|
|
1390
|
-
* 2. Comparing documents from both sources.
|
|
1391
|
-
* 3. Deleting documents from MeiliSearch that no longer exist in MongoDB.
|
|
1392
|
-
* 4. Adding documents to MeiliSearch that exist in MongoDB but not in the index.
|
|
1393
|
-
* 5. Updating documents in MeiliSearch if key fields (such as `text` or `title`) differ.
|
|
1394
|
-
* 6. Updating the `_meiliIndex` field in MongoDB to indicate the indexing status.
|
|
1395
|
-
*
|
|
1396
|
-
* Note: The function processes documents in batches because MeiliSearch's
|
|
1397
|
-
* `index.getDocuments` requires an exact limit and `index.addDocuments` does not handle
|
|
1398
|
-
* partial failures in a batch.
|
|
1399
|
-
*
|
|
1400
|
-
* @returns {Promise<void>} Resolves when the synchronization is complete.
|
|
1440
|
+
* Now uses streaming and batching to reduce memory usage.
|
|
1401
1441
|
*/
|
|
1402
|
-
static async syncWithMeili() {
|
|
1442
|
+
static async syncWithMeili(options) {
|
|
1403
1443
|
try {
|
|
1404
|
-
|
|
1405
|
-
const
|
|
1444
|
+
const startTime = Date.now();
|
|
1445
|
+
const { batchSize, delayMs } = syncConfig;
|
|
1446
|
+
logger$1.info(`[syncWithMeili] Starting sync for ${primaryKey === 'messageId' ? 'messages' : 'conversations'} with batch size ${batchSize}`);
|
|
1447
|
+
// Build query with resume capability
|
|
1448
|
+
const query = {};
|
|
1449
|
+
if (options === null || options === void 0 ? void 0 : options.resumeFromId) {
|
|
1450
|
+
query._id = { $gt: options.resumeFromId };
|
|
1451
|
+
}
|
|
1452
|
+
// Get total count for progress tracking
|
|
1453
|
+
const totalCount = await this.countDocuments(query);
|
|
1454
|
+
let processedCount = 0;
|
|
1455
|
+
// First, handle documents that need to be removed from Meili
|
|
1456
|
+
await this.cleanupMeiliIndex(index, primaryKey, batchSize, delayMs);
|
|
1457
|
+
// Process MongoDB documents in batches using cursor
|
|
1458
|
+
const cursor = this.find(query)
|
|
1459
|
+
.select(attributesToIndex.join(' ') + ' _meiliIndex')
|
|
1460
|
+
.sort({ _id: 1 })
|
|
1461
|
+
.batchSize(batchSize)
|
|
1462
|
+
.cursor();
|
|
1406
1463
|
const format = (doc) => _.omitBy(_.pick(doc, attributesToIndex), (v, k) => k.startsWith('$'));
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1464
|
+
let documentBatch = [];
|
|
1465
|
+
let updateOps = [];
|
|
1466
|
+
// Process documents in streaming fashion
|
|
1467
|
+
for await (const doc of cursor) {
|
|
1468
|
+
const typedDoc = doc.toObject();
|
|
1469
|
+
const formatted = format(typedDoc);
|
|
1470
|
+
// Check if document needs indexing
|
|
1471
|
+
if (!typedDoc._meiliIndex) {
|
|
1472
|
+
documentBatch.push(formatted);
|
|
1473
|
+
updateOps.push({
|
|
1474
|
+
updateOne: {
|
|
1475
|
+
filter: { _id: typedDoc._id },
|
|
1476
|
+
update: { $set: { _meiliIndex: true } },
|
|
1477
|
+
},
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
processedCount++;
|
|
1481
|
+
// Process batch when it reaches the configured size
|
|
1482
|
+
if (documentBatch.length >= batchSize) {
|
|
1483
|
+
await this.processSyncBatch(index, documentBatch, updateOps);
|
|
1484
|
+
documentBatch = [];
|
|
1485
|
+
updateOps = [];
|
|
1486
|
+
// Log progress
|
|
1487
|
+
const progress = Math.round((processedCount / totalCount) * 100);
|
|
1488
|
+
logger$1.info(`[syncWithMeili] Progress: ${progress}% (${processedCount}/${totalCount})`);
|
|
1489
|
+
// Add delay to prevent overwhelming resources
|
|
1490
|
+
if (delayMs > 0) {
|
|
1491
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
// Process remaining documents
|
|
1496
|
+
if (documentBatch.length > 0) {
|
|
1497
|
+
await this.processSyncBatch(index, documentBatch, updateOps);
|
|
1498
|
+
}
|
|
1499
|
+
const duration = Date.now() - startTime;
|
|
1500
|
+
logger$1.info(`[syncWithMeili] Completed sync for ${primaryKey === 'messageId' ? 'messages' : 'conversations'} in ${duration}ms`);
|
|
1501
|
+
}
|
|
1502
|
+
catch (error) {
|
|
1503
|
+
logger$1.error('[syncWithMeili] Error during sync:', error);
|
|
1504
|
+
throw error;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Process a batch of documents for syncing
|
|
1509
|
+
*/
|
|
1510
|
+
static async processSyncBatch(index, documents, updateOps) {
|
|
1511
|
+
if (documents.length === 0) {
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
try {
|
|
1515
|
+
// Add documents to MeiliSearch
|
|
1516
|
+
await index.addDocuments(documents);
|
|
1517
|
+
// Update MongoDB to mark documents as indexed
|
|
1518
|
+
if (updateOps.length > 0) {
|
|
1519
|
+
await this.collection.bulkWrite(updateOps);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
catch (error) {
|
|
1523
|
+
logger$1.error('[processSyncBatch] Error processing batch:', error);
|
|
1524
|
+
// Don't throw - allow sync to continue with other documents
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Clean up documents in MeiliSearch that no longer exist in MongoDB
|
|
1529
|
+
*/
|
|
1530
|
+
static async cleanupMeiliIndex(index, primaryKey, batchSize, delayMs) {
|
|
1531
|
+
try {
|
|
1412
1532
|
let offset = 0;
|
|
1413
|
-
|
|
1533
|
+
let moreDocuments = true;
|
|
1414
1534
|
while (moreDocuments) {
|
|
1415
1535
|
const batch = await index.getDocuments({ limit: batchSize, offset });
|
|
1416
1536
|
if (batch.results.length === 0) {
|
|
1417
1537
|
moreDocuments = false;
|
|
1538
|
+
break;
|
|
1418
1539
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1540
|
+
const meiliIds = batch.results.map((doc) => doc[primaryKey]);
|
|
1541
|
+
const query = {};
|
|
1542
|
+
query[primaryKey] = { $in: meiliIds };
|
|
1543
|
+
// Find which documents exist in MongoDB
|
|
1544
|
+
const existingDocs = await this.find(query).select(primaryKey).lean();
|
|
1545
|
+
const existingIds = new Set(existingDocs.map((doc) => doc[primaryKey]));
|
|
1546
|
+
// Delete documents that don't exist in MongoDB
|
|
1547
|
+
const toDelete = meiliIds.filter((id) => !existingIds.has(id));
|
|
1548
|
+
if (toDelete.length > 0) {
|
|
1549
|
+
await Promise.all(toDelete.map((id) => index.deleteDocument(id)));
|
|
1550
|
+
logger$1.debug(`[cleanupMeiliIndex] Deleted ${toDelete.length} orphaned documents`);
|
|
1421
1551
|
}
|
|
1422
1552
|
offset += batchSize;
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
// Process documents present in the MeiliSearch index
|
|
1427
|
-
for (const [id, doc] of indexMap) {
|
|
1428
|
-
const update = {};
|
|
1429
|
-
update[primaryKey] = id;
|
|
1430
|
-
if (mongoMap.has(id)) {
|
|
1431
|
-
const mongoDoc = mongoMap.get(id);
|
|
1432
|
-
if ((doc.text && doc.text !== (mongoDoc === null || mongoDoc === void 0 ? void 0 : mongoDoc.text)) ||
|
|
1433
|
-
(doc.title && doc.title !== (mongoDoc === null || mongoDoc === void 0 ? void 0 : mongoDoc.title))) {
|
|
1434
|
-
logger$1.debug(`[syncWithMeili] ${id} had document discrepancy in ${doc.text ? 'text' : 'title'} field`);
|
|
1435
|
-
updateOps.push({
|
|
1436
|
-
updateOne: { filter: update, update: { $set: { _meiliIndex: true } } },
|
|
1437
|
-
});
|
|
1438
|
-
await index.addDocuments([doc]);
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
else {
|
|
1442
|
-
await index.deleteDocument(id);
|
|
1443
|
-
updateOps.push({
|
|
1444
|
-
updateOne: { filter: update, update: { $set: { _meiliIndex: false } } },
|
|
1445
|
-
});
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
// Process documents present in MongoDB
|
|
1449
|
-
for (const [id, doc] of mongoMap) {
|
|
1450
|
-
const update = {};
|
|
1451
|
-
update[primaryKey] = id;
|
|
1452
|
-
if (!indexMap.has(id)) {
|
|
1453
|
-
await index.addDocuments([doc]);
|
|
1454
|
-
updateOps.push({
|
|
1455
|
-
updateOne: { filter: update, update: { $set: { _meiliIndex: true } } },
|
|
1456
|
-
});
|
|
1457
|
-
}
|
|
1458
|
-
else if (doc._meiliIndex === false) {
|
|
1459
|
-
updateOps.push({
|
|
1460
|
-
updateOne: { filter: update, update: { $set: { _meiliIndex: true } } },
|
|
1461
|
-
});
|
|
1553
|
+
// Add delay between batches
|
|
1554
|
+
if (delayMs > 0) {
|
|
1555
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1462
1556
|
}
|
|
1463
1557
|
}
|
|
1464
|
-
if (updateOps.length > 0) {
|
|
1465
|
-
await this.collection.bulkWrite(updateOps);
|
|
1466
|
-
logger$1.debug(`[syncWithMeili] Finished indexing ${primaryKey === 'messageId' ? 'messages' : 'conversations'}`);
|
|
1467
|
-
}
|
|
1468
1558
|
}
|
|
1469
1559
|
catch (error) {
|
|
1470
|
-
logger$1.error('[
|
|
1560
|
+
logger$1.error('[cleanupMeiliIndex] Error during cleanup:', error);
|
|
1471
1561
|
}
|
|
1472
1562
|
}
|
|
1473
1563
|
/**
|
|
@@ -1523,16 +1613,26 @@ const createMeiliMongooseModel = ({ index, attributesToIndex, }) => {
|
|
|
1523
1613
|
return object;
|
|
1524
1614
|
}
|
|
1525
1615
|
/**
|
|
1526
|
-
* Adds the current document to the MeiliSearch index
|
|
1616
|
+
* Adds the current document to the MeiliSearch index with retry logic
|
|
1527
1617
|
*/
|
|
1528
1618
|
async addObjectToMeili(next) {
|
|
1529
1619
|
const object = this.preprocessObjectForIndex();
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1620
|
+
const maxRetries = 3;
|
|
1621
|
+
let retryCount = 0;
|
|
1622
|
+
while (retryCount < maxRetries) {
|
|
1623
|
+
try {
|
|
1624
|
+
await index.addDocuments([object]);
|
|
1625
|
+
break;
|
|
1626
|
+
}
|
|
1627
|
+
catch (error) {
|
|
1628
|
+
retryCount++;
|
|
1629
|
+
if (retryCount >= maxRetries) {
|
|
1630
|
+
logger$1.error('[addObjectToMeili] Error adding document to Meili after retries:', error);
|
|
1631
|
+
return next();
|
|
1632
|
+
}
|
|
1633
|
+
// Exponential backoff
|
|
1634
|
+
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
|
|
1635
|
+
}
|
|
1536
1636
|
}
|
|
1537
1637
|
try {
|
|
1538
1638
|
await this.collection.updateMany({ _id: this._id }, { $set: { _meiliIndex: true } });
|
|
@@ -1633,6 +1733,8 @@ const createMeiliMongooseModel = ({ index, attributesToIndex, }) => {
|
|
|
1633
1733
|
* @param options.apiKey - The MeiliSearch API key.
|
|
1634
1734
|
* @param options.indexName - The name of the MeiliSearch index.
|
|
1635
1735
|
* @param options.primaryKey - The primary key field for indexing.
|
|
1736
|
+
* @param options.syncBatchSize - Batch size for sync operations.
|
|
1737
|
+
* @param options.syncDelayMs - Delay between batches in milliseconds.
|
|
1636
1738
|
*/
|
|
1637
1739
|
function mongoMeili(schema, options) {
|
|
1638
1740
|
const mongoose = options.mongoose;
|
|
@@ -1647,9 +1749,37 @@ function mongoMeili(schema, options) {
|
|
|
1647
1749
|
},
|
|
1648
1750
|
});
|
|
1649
1751
|
const { host, apiKey, indexName, primaryKey } = options;
|
|
1752
|
+
const syncOptions = {
|
|
1753
|
+
batchSize: options.syncBatchSize || getSyncConfig().batchSize,
|
|
1754
|
+
delayMs: options.syncDelayMs || getSyncConfig().delayMs,
|
|
1755
|
+
};
|
|
1650
1756
|
const client = new meilisearch.MeiliSearch({ host, apiKey });
|
|
1651
|
-
|
|
1757
|
+
/** Create index only if it doesn't exist */
|
|
1652
1758
|
const index = client.index(indexName);
|
|
1759
|
+
// Check if index exists and create if needed
|
|
1760
|
+
(async () => {
|
|
1761
|
+
try {
|
|
1762
|
+
await index.getRawInfo();
|
|
1763
|
+
logger$1.debug(`[mongoMeili] Index ${indexName} already exists`);
|
|
1764
|
+
}
|
|
1765
|
+
catch (error) {
|
|
1766
|
+
const errorCode = error === null || error === void 0 ? void 0 : error.code;
|
|
1767
|
+
if (errorCode === 'index_not_found') {
|
|
1768
|
+
try {
|
|
1769
|
+
logger$1.info(`[mongoMeili] Creating new index: ${indexName}`);
|
|
1770
|
+
await client.createIndex(indexName, { primaryKey });
|
|
1771
|
+
logger$1.info(`[mongoMeili] Successfully created index: ${indexName}`);
|
|
1772
|
+
}
|
|
1773
|
+
catch (createError) {
|
|
1774
|
+
// Index might have been created by another instance
|
|
1775
|
+
logger$1.debug(`[mongoMeili] Index ${indexName} may already exist:`, createError);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
else {
|
|
1779
|
+
logger$1.error(`[mongoMeili] Error checking index ${indexName}:`, error);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
})();
|
|
1653
1783
|
// Collect attributes from the schema that should be indexed
|
|
1654
1784
|
const attributesToIndex = [
|
|
1655
1785
|
...Object.entries(schema.obj).reduce((results, [key, value]) => {
|
|
@@ -1657,7 +1787,7 @@ function mongoMeili(schema, options) {
|
|
|
1657
1787
|
return schemaValue.meiliIndex ? [...results, key] : results;
|
|
1658
1788
|
}, []),
|
|
1659
1789
|
];
|
|
1660
|
-
schema.loadClass(createMeiliMongooseModel({ index, attributesToIndex }));
|
|
1790
|
+
schema.loadClass(createMeiliMongooseModel({ index, attributesToIndex, syncOptions }));
|
|
1661
1791
|
// Register Mongoose hooks
|
|
1662
1792
|
schema.post('save', function (doc, next) {
|
|
1663
1793
|
var _a;
|
|
@@ -1678,29 +1808,38 @@ function mongoMeili(schema, options) {
|
|
|
1678
1808
|
}
|
|
1679
1809
|
try {
|
|
1680
1810
|
const conditions = this.getQuery();
|
|
1811
|
+
const { batchSize, delayMs } = syncOptions;
|
|
1681
1812
|
if (Object.prototype.hasOwnProperty.call(schema.obj, 'messages')) {
|
|
1682
1813
|
const convoIndex = client.index('convos');
|
|
1683
1814
|
const deletedConvos = await mongoose
|
|
1684
1815
|
.model('Conversation')
|
|
1685
1816
|
.find(conditions)
|
|
1817
|
+
.select('conversationId')
|
|
1686
1818
|
.lean();
|
|
1687
|
-
|
|
1688
|
-
await
|
|
1819
|
+
// Process deletions in batches
|
|
1820
|
+
await processBatch(deletedConvos, batchSize, delayMs, async (batch) => {
|
|
1821
|
+
const promises = batch.map((convo) => convoIndex.deleteDocument(convo.conversationId));
|
|
1822
|
+
await Promise.all(promises);
|
|
1823
|
+
});
|
|
1689
1824
|
}
|
|
1690
1825
|
if (Object.prototype.hasOwnProperty.call(schema.obj, 'messageId')) {
|
|
1691
1826
|
const messageIndex = client.index('messages');
|
|
1692
1827
|
const deletedMessages = await mongoose
|
|
1693
1828
|
.model('Message')
|
|
1694
1829
|
.find(conditions)
|
|
1830
|
+
.select('messageId')
|
|
1695
1831
|
.lean();
|
|
1696
|
-
|
|
1697
|
-
await
|
|
1832
|
+
// Process deletions in batches
|
|
1833
|
+
await processBatch(deletedMessages, batchSize, delayMs, async (batch) => {
|
|
1834
|
+
const promises = batch.map((message) => messageIndex.deleteDocument(message.messageId));
|
|
1835
|
+
await Promise.all(promises);
|
|
1836
|
+
});
|
|
1698
1837
|
}
|
|
1699
1838
|
return next();
|
|
1700
1839
|
}
|
|
1701
1840
|
catch (error) {
|
|
1702
1841
|
if (meiliEnabled) {
|
|
1703
|
-
logger$1.error('[MeiliMongooseModel.deleteMany] There was an issue deleting conversation indexes upon deletion. Next startup may
|
|
1842
|
+
logger$1.error('[MeiliMongooseModel.deleteMany] There was an issue deleting conversation indexes upon deletion. Next startup may trigger syncing.', error);
|
|
1704
1843
|
}
|
|
1705
1844
|
return next();
|
|
1706
1845
|
}
|
|
@@ -2374,12 +2513,9 @@ class SessionError extends Error {
|
|
|
2374
2513
|
}
|
|
2375
2514
|
}
|
|
2376
2515
|
const { REFRESH_TOKEN_EXPIRY } = (_a = process.env) !== null && _a !== void 0 ? _a : {};
|
|
2377
|
-
const expires = REFRESH_TOKEN_EXPIRY
|
|
2378
|
-
? eval(REFRESH_TOKEN_EXPIRY)
|
|
2379
|
-
: 1000 * 60 * 60 * 24 * 7; // 7 days default
|
|
2516
|
+
const expires = REFRESH_TOKEN_EXPIRY ? eval(REFRESH_TOKEN_EXPIRY) : 1000 * 60 * 60 * 24 * 7; // 7 days default
|
|
2380
2517
|
// Factory function that takes mongoose instance and returns the methods
|
|
2381
2518
|
function createSessionMethods(mongoose) {
|
|
2382
|
-
const Session = mongoose.models.Session;
|
|
2383
2519
|
/**
|
|
2384
2520
|
* Creates a new session for a user
|
|
2385
2521
|
*/
|
|
@@ -2388,12 +2524,13 @@ function createSessionMethods(mongoose) {
|
|
|
2388
2524
|
throw new SessionError('User ID is required', 'INVALID_USER_ID');
|
|
2389
2525
|
}
|
|
2390
2526
|
try {
|
|
2391
|
-
const
|
|
2527
|
+
const Session = mongoose.models.Session;
|
|
2528
|
+
const currentSession = new Session({
|
|
2392
2529
|
user: userId,
|
|
2393
2530
|
expiration: options.expiration || new Date(Date.now() + expires),
|
|
2394
2531
|
});
|
|
2395
|
-
const refreshToken = await generateRefreshToken(
|
|
2396
|
-
return { session, refreshToken };
|
|
2532
|
+
const refreshToken = await generateRefreshToken(currentSession);
|
|
2533
|
+
return { session: currentSession, refreshToken };
|
|
2397
2534
|
}
|
|
2398
2535
|
catch (error) {
|
|
2399
2536
|
logger.error('[createSession] Error creating session:', error);
|
|
@@ -2405,6 +2542,7 @@ function createSessionMethods(mongoose) {
|
|
|
2405
2542
|
*/
|
|
2406
2543
|
async function findSession(params, options = { lean: true }) {
|
|
2407
2544
|
try {
|
|
2545
|
+
const Session = mongoose.models.Session;
|
|
2408
2546
|
const query = {};
|
|
2409
2547
|
if (!params.refreshToken && !params.userId && !params.sessionId) {
|
|
2410
2548
|
throw new SessionError('At least one search parameter is required', 'INVALID_SEARCH_PARAMS');
|
|
@@ -2445,6 +2583,7 @@ function createSessionMethods(mongoose) {
|
|
|
2445
2583
|
*/
|
|
2446
2584
|
async function updateExpiration(session, newExpiration) {
|
|
2447
2585
|
try {
|
|
2586
|
+
const Session = mongoose.models.Session;
|
|
2448
2587
|
const sessionDoc = typeof session === 'string' ? await Session.findById(session) : session;
|
|
2449
2588
|
if (!sessionDoc) {
|
|
2450
2589
|
throw new SessionError('Session not found', 'SESSION_NOT_FOUND');
|
|
@@ -2462,6 +2601,7 @@ function createSessionMethods(mongoose) {
|
|
|
2462
2601
|
*/
|
|
2463
2602
|
async function deleteSession(params) {
|
|
2464
2603
|
try {
|
|
2604
|
+
const Session = mongoose.models.Session;
|
|
2465
2605
|
if (!params.refreshToken && !params.sessionId) {
|
|
2466
2606
|
throw new SessionError('Either refreshToken or sessionId is required', 'INVALID_DELETE_PARAMS');
|
|
2467
2607
|
}
|
|
@@ -2488,6 +2628,7 @@ function createSessionMethods(mongoose) {
|
|
|
2488
2628
|
*/
|
|
2489
2629
|
async function deleteAllUserSessions(userId, options = {}) {
|
|
2490
2630
|
try {
|
|
2631
|
+
const Session = mongoose.models.Session;
|
|
2491
2632
|
if (!userId) {
|
|
2492
2633
|
throw new SessionError('User ID is required', 'INVALID_USER_ID');
|
|
2493
2634
|
}
|
|
@@ -2544,6 +2685,7 @@ function createSessionMethods(mongoose) {
|
|
|
2544
2685
|
*/
|
|
2545
2686
|
async function countActiveSessions(userId) {
|
|
2546
2687
|
try {
|
|
2688
|
+
const Session = mongoose.models.Session;
|
|
2547
2689
|
if (!userId) {
|
|
2548
2690
|
throw new SessionError('User ID is required', 'INVALID_USER_ID');
|
|
2549
2691
|
}
|
|
@@ -2714,7 +2856,6 @@ const formatDate = (date) => {
|
|
|
2714
2856
|
};
|
|
2715
2857
|
// Factory function that takes mongoose instance and returns the methods
|
|
2716
2858
|
function createMemoryMethods(mongoose) {
|
|
2717
|
-
const MemoryEntry = mongoose.models.MemoryEntry;
|
|
2718
2859
|
/**
|
|
2719
2860
|
* Creates a new memory entry for a user
|
|
2720
2861
|
* Throws an error if a memory with the same key already exists
|
|
@@ -2724,6 +2865,7 @@ function createMemoryMethods(mongoose) {
|
|
|
2724
2865
|
if ((key === null || key === void 0 ? void 0 : key.toLowerCase()) === 'nothing') {
|
|
2725
2866
|
return { ok: false };
|
|
2726
2867
|
}
|
|
2868
|
+
const MemoryEntry = mongoose.models.MemoryEntry;
|
|
2727
2869
|
const existingMemory = await MemoryEntry.findOne({ userId, key });
|
|
2728
2870
|
if (existingMemory) {
|
|
2729
2871
|
throw new Error('Memory with this key already exists');
|
|
@@ -2749,6 +2891,7 @@ function createMemoryMethods(mongoose) {
|
|
|
2749
2891
|
if ((key === null || key === void 0 ? void 0 : key.toLowerCase()) === 'nothing') {
|
|
2750
2892
|
return { ok: false };
|
|
2751
2893
|
}
|
|
2894
|
+
const MemoryEntry = mongoose.models.MemoryEntry;
|
|
2752
2895
|
await MemoryEntry.findOneAndUpdate({ userId, key }, {
|
|
2753
2896
|
value,
|
|
2754
2897
|
tokenCount,
|
|
@@ -2768,6 +2911,7 @@ function createMemoryMethods(mongoose) {
|
|
|
2768
2911
|
*/
|
|
2769
2912
|
async function deleteMemory({ userId, key }) {
|
|
2770
2913
|
try {
|
|
2914
|
+
const MemoryEntry = mongoose.models.MemoryEntry;
|
|
2771
2915
|
const result = await MemoryEntry.findOneAndDelete({ userId, key });
|
|
2772
2916
|
return { ok: !!result };
|
|
2773
2917
|
}
|
|
@@ -2780,6 +2924,7 @@ function createMemoryMethods(mongoose) {
|
|
|
2780
2924
|
*/
|
|
2781
2925
|
async function getAllUserMemories(userId) {
|
|
2782
2926
|
try {
|
|
2927
|
+
const MemoryEntry = mongoose.models.MemoryEntry;
|
|
2783
2928
|
return (await MemoryEntry.find({ userId }).lean());
|
|
2784
2929
|
}
|
|
2785
2930
|
catch (error) {
|
|
@@ -3190,12 +3335,12 @@ function createShareMethods(mongoose) {
|
|
|
3190
3335
|
|
|
3191
3336
|
// Factory function that takes mongoose instance and returns the methods
|
|
3192
3337
|
function createPluginAuthMethods(mongoose) {
|
|
3193
|
-
const PluginAuth = mongoose.models.PluginAuth;
|
|
3194
3338
|
/**
|
|
3195
3339
|
* Finds a single plugin auth entry by userId and authField
|
|
3196
3340
|
*/
|
|
3197
3341
|
async function findOnePluginAuth({ userId, authField, }) {
|
|
3198
3342
|
try {
|
|
3343
|
+
const PluginAuth = mongoose.models.PluginAuth;
|
|
3199
3344
|
return await PluginAuth.findOne({ userId, authField }).lean();
|
|
3200
3345
|
}
|
|
3201
3346
|
catch (error) {
|
|
@@ -3210,6 +3355,7 @@ function createPluginAuthMethods(mongoose) {
|
|
|
3210
3355
|
if (!pluginKeys || pluginKeys.length === 0) {
|
|
3211
3356
|
return [];
|
|
3212
3357
|
}
|
|
3358
|
+
const PluginAuth = mongoose.models.PluginAuth;
|
|
3213
3359
|
return await PluginAuth.find({
|
|
3214
3360
|
userId,
|
|
3215
3361
|
pluginKey: { $in: pluginKeys },
|
|
@@ -3224,6 +3370,7 @@ function createPluginAuthMethods(mongoose) {
|
|
|
3224
3370
|
*/
|
|
3225
3371
|
async function updatePluginAuth({ userId, authField, pluginKey, value, }) {
|
|
3226
3372
|
try {
|
|
3373
|
+
const PluginAuth = mongoose.models.PluginAuth;
|
|
3227
3374
|
const existingAuth = await PluginAuth.findOne({ userId, pluginKey, authField }).lean();
|
|
3228
3375
|
if (existingAuth) {
|
|
3229
3376
|
return await PluginAuth.findOneAndUpdate({ userId, pluginKey, authField }, { $set: { value } }, { new: true, upsert: true }).lean();
|
|
@@ -3248,6 +3395,7 @@ function createPluginAuthMethods(mongoose) {
|
|
|
3248
3395
|
*/
|
|
3249
3396
|
async function deletePluginAuth({ userId, authField, pluginKey, all = false, }) {
|
|
3250
3397
|
try {
|
|
3398
|
+
const PluginAuth = mongoose.models.PluginAuth;
|
|
3251
3399
|
if (all) {
|
|
3252
3400
|
const filter = { userId };
|
|
3253
3401
|
if (pluginKey) {
|
|
@@ -3269,6 +3417,7 @@ function createPluginAuthMethods(mongoose) {
|
|
|
3269
3417
|
*/
|
|
3270
3418
|
async function deleteAllUserPluginAuths(userId) {
|
|
3271
3419
|
try {
|
|
3420
|
+
const PluginAuth = mongoose.models.PluginAuth;
|
|
3272
3421
|
return await PluginAuth.deleteMany({ userId });
|
|
3273
3422
|
}
|
|
3274
3423
|
catch (error) {
|