| | const { logger } = require('@librechat/data-schemas'); |
| | const { ConversationTag, Conversation } = require('~/db/models'); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | const getConversationTags = async (user) => { |
| | try { |
| | return await ConversationTag.find({ user }).sort({ position: 1 }).lean(); |
| | } catch (error) { |
| | logger.error('[getConversationTags] Error getting conversation tags', error); |
| | throw new Error('Error getting conversation tags'); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const createConversationTag = async (user, data) => { |
| | try { |
| | const { tag, description, addToConversation, conversationId } = data; |
| |
|
| | const existingTag = await ConversationTag.findOne({ user, tag }).lean(); |
| | if (existingTag) { |
| | return existingTag; |
| | } |
| |
|
| | const maxPosition = await ConversationTag.findOne({ user }).sort('-position').lean(); |
| | const position = (maxPosition?.position || 0) + 1; |
| |
|
| | const newTag = await ConversationTag.findOneAndUpdate( |
| | { tag, user }, |
| | { |
| | tag, |
| | user, |
| | count: addToConversation ? 1 : 0, |
| | position, |
| | description, |
| | $setOnInsert: { createdAt: new Date() }, |
| | }, |
| | { |
| | new: true, |
| | upsert: true, |
| | lean: true, |
| | }, |
| | ); |
| |
|
| | if (addToConversation && conversationId) { |
| | await Conversation.findOneAndUpdate( |
| | { user, conversationId }, |
| | { $addToSet: { tags: tag } }, |
| | { new: true }, |
| | ); |
| | } |
| |
|
| | return newTag; |
| | } catch (error) { |
| | logger.error('[createConversationTag] Error creating conversation tag', error); |
| | throw new Error('Error creating conversation tag'); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const updateConversationTag = async (user, oldTag, data) => { |
| | try { |
| | const { tag: newTag, description, position } = data; |
| |
|
| | const existingTag = await ConversationTag.findOne({ user, tag: oldTag }).lean(); |
| | if (!existingTag) { |
| | return null; |
| | } |
| |
|
| | if (newTag && newTag !== oldTag) { |
| | const tagAlreadyExists = await ConversationTag.findOne({ user, tag: newTag }).lean(); |
| | if (tagAlreadyExists) { |
| | throw new Error('Tag already exists'); |
| | } |
| |
|
| | await Conversation.updateMany({ user, tags: oldTag }, { $set: { 'tags.$': newTag } }); |
| | } |
| |
|
| | const updateData = {}; |
| | if (newTag) { |
| | updateData.tag = newTag; |
| | } |
| | if (description !== undefined) { |
| | updateData.description = description; |
| | } |
| | if (position !== undefined) { |
| | await adjustPositions(user, existingTag.position, position); |
| | updateData.position = position; |
| | } |
| |
|
| | return await ConversationTag.findOneAndUpdate({ user, tag: oldTag }, updateData, { |
| | new: true, |
| | lean: true, |
| | }); |
| | } catch (error) { |
| | logger.error('[updateConversationTag] Error updating conversation tag', error); |
| | throw new Error('Error updating conversation tag'); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const adjustPositions = async (user, oldPosition, newPosition) => { |
| | if (oldPosition === newPosition) { |
| | return; |
| | } |
| |
|
| | const update = oldPosition < newPosition ? { $inc: { position: -1 } } : { $inc: { position: 1 } }; |
| | const position = |
| | oldPosition < newPosition |
| | ? { |
| | $gt: Math.min(oldPosition, newPosition), |
| | $lte: Math.max(oldPosition, newPosition), |
| | } |
| | : { |
| | $gte: Math.min(oldPosition, newPosition), |
| | $lt: Math.max(oldPosition, newPosition), |
| | }; |
| |
|
| | await ConversationTag.updateMany( |
| | { |
| | user, |
| | position, |
| | }, |
| | update, |
| | ); |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | const deleteConversationTag = async (user, tag) => { |
| | try { |
| | const deletedTag = await ConversationTag.findOneAndDelete({ user, tag }).lean(); |
| | if (!deletedTag) { |
| | return null; |
| | } |
| |
|
| | await Conversation.updateMany({ user, tags: tag }, { $pull: { tags: tag } }); |
| |
|
| | await ConversationTag.updateMany( |
| | { user, position: { $gt: deletedTag.position } }, |
| | { $inc: { position: -1 } }, |
| | ); |
| |
|
| | return deletedTag; |
| | } catch (error) { |
| | logger.error('[deleteConversationTag] Error deleting conversation tag', error); |
| | throw new Error('Error deleting conversation tag'); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const updateTagsForConversation = async (user, conversationId, tags) => { |
| | try { |
| | const conversation = await Conversation.findOne({ user, conversationId }).lean(); |
| | if (!conversation) { |
| | throw new Error('Conversation not found'); |
| | } |
| |
|
| | const oldTags = new Set(conversation.tags); |
| | const newTags = new Set(tags); |
| |
|
| | const addedTags = [...newTags].filter((tag) => !oldTags.has(tag)); |
| | const removedTags = [...oldTags].filter((tag) => !newTags.has(tag)); |
| |
|
| | const bulkOps = []; |
| |
|
| | for (const tag of addedTags) { |
| | bulkOps.push({ |
| | updateOne: { |
| | filter: { user, tag }, |
| | update: { $inc: { count: 1 } }, |
| | upsert: true, |
| | }, |
| | }); |
| | } |
| |
|
| | for (const tag of removedTags) { |
| | bulkOps.push({ |
| | updateOne: { |
| | filter: { user, tag }, |
| | update: { $inc: { count: -1 } }, |
| | }, |
| | }); |
| | } |
| |
|
| | if (bulkOps.length > 0) { |
| | await ConversationTag.bulkWrite(bulkOps); |
| | } |
| |
|
| | const updatedConversation = ( |
| | await Conversation.findOneAndUpdate( |
| | { user, conversationId }, |
| | { $set: { tags: [...newTags] } }, |
| | { new: true }, |
| | ) |
| | ).toObject(); |
| |
|
| | return updatedConversation.tags; |
| | } catch (error) { |
| | logger.error('[updateTagsForConversation] Error updating tags', error); |
| | throw new Error('Error updating tags for conversation'); |
| | } |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | const bulkIncrementTagCounts = async (user, tags) => { |
| | if (!tags || tags.length === 0) { |
| | return; |
| | } |
| |
|
| | try { |
| | const uniqueTags = [...new Set(tags.filter(Boolean))]; |
| | if (uniqueTags.length === 0) { |
| | return; |
| | } |
| |
|
| | const bulkOps = uniqueTags.map((tag) => ({ |
| | updateOne: { |
| | filter: { user, tag }, |
| | update: { $inc: { count: 1 } }, |
| | }, |
| | })); |
| |
|
| | const result = await ConversationTag.bulkWrite(bulkOps); |
| | if (result && result.modifiedCount > 0) { |
| | logger.debug( |
| | `user: ${user} | Incremented tag counts - modified ${result.modifiedCount} tags`, |
| | ); |
| | } |
| | } catch (error) { |
| | logger.error('[bulkIncrementTagCounts] Error incrementing tag counts', error); |
| | } |
| | }; |
| |
|
| | module.exports = { |
| | getConversationTags, |
| | createConversationTag, |
| | updateConversationTag, |
| | deleteConversationTag, |
| | bulkIncrementTagCounts, |
| | updateTagsForConversation, |
| | }; |
| |
|