2024-01-13 02:13:38 +00:00
|
|
|
using BotSharp.Abstraction.Conversations.Models;
|
|
|
|
|
using BotSharp.Abstraction.Repositories.Filters;
|
|
|
|
|
|
|
|
|
|
namespace BotSharp.Plugin.MongoStorage.Repository;
|
|
|
|
|
|
|
|
|
|
public partial class MongoRepository
|
|
|
|
|
{
|
|
|
|
|
public void CreateNewConversation(Conversation conversation)
|
|
|
|
|
{
|
|
|
|
|
if (conversation == null) return;
|
|
|
|
|
|
2024-03-26 17:10:34 +00:00
|
|
|
var utcNow = DateTime.UtcNow;
|
2024-01-13 02:13:38 +00:00
|
|
|
var convDoc = new ConversationDocument
|
|
|
|
|
{
|
|
|
|
|
Id = !string.IsNullOrEmpty(conversation.Id) ? conversation.Id : Guid.NewGuid().ToString(),
|
|
|
|
|
AgentId = conversation.AgentId,
|
|
|
|
|
UserId = !string.IsNullOrEmpty(conversation.UserId) ? conversation.UserId : string.Empty,
|
|
|
|
|
Title = conversation.Title,
|
|
|
|
|
Channel = conversation.Channel,
|
2024-10-25 02:18:43 +00:00
|
|
|
ChannelId = conversation.ChannelId,
|
2024-02-12 15:14:33 +00:00
|
|
|
TaskId = conversation.TaskId,
|
2024-01-13 02:13:38 +00:00
|
|
|
Status = conversation.Status,
|
2024-10-18 20:50:39 +00:00
|
|
|
Tags = conversation.Tags ?? new(),
|
2024-03-26 17:10:34 +00:00
|
|
|
CreatedTime = utcNow,
|
|
|
|
|
UpdatedTime = utcNow
|
2024-01-13 02:13:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var dialogDoc = new ConversationDialogDocument
|
|
|
|
|
{
|
|
|
|
|
Id = Guid.NewGuid().ToString(),
|
|
|
|
|
ConversationId = convDoc.Id,
|
2025-01-29 22:21:19 +00:00
|
|
|
AgentId = conversation.AgentId,
|
2024-01-13 02:13:38 +00:00
|
|
|
Dialogs = new List<DialogMongoElement>()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var stateDoc = new ConversationStateDocument
|
|
|
|
|
{
|
|
|
|
|
Id = Guid.NewGuid().ToString(),
|
|
|
|
|
ConversationId = convDoc.Id,
|
2025-01-29 22:21:19 +00:00
|
|
|
AgentId = conversation.AgentId,
|
2024-04-08 14:58:25 +00:00
|
|
|
States = new List<StateMongoElement>(),
|
2024-04-08 03:15:51 +00:00
|
|
|
Breakpoints = new List<BreakpointMongoElement>()
|
2024-01-13 02:13:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_dc.Conversations.InsertOne(convDoc);
|
|
|
|
|
_dc.ConversationDialogs.InsertOne(dialogDoc);
|
|
|
|
|
_dc.ConversationStates.InsertOne(stateDoc);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-07 07:34:52 +00:00
|
|
|
public bool DeleteConversations(IEnumerable<string> conversationIds)
|
2024-01-13 02:13:38 +00:00
|
|
|
{
|
2024-03-07 07:34:52 +00:00
|
|
|
if (conversationIds.IsNullOrEmpty()) return false;
|
2024-01-13 02:13:38 +00:00
|
|
|
|
2024-03-07 07:34:52 +00:00
|
|
|
var filterConv = Builders<ConversationDocument>.Filter.In(x => x.Id, conversationIds);
|
|
|
|
|
var filterDialog = Builders<ConversationDialogDocument>.Filter.In(x => x.ConversationId, conversationIds);
|
|
|
|
|
var filterSates = Builders<ConversationStateDocument>.Filter.In(x => x.ConversationId, conversationIds);
|
|
|
|
|
var filterExeLog = Builders<ExecutionLogDocument>.Filter.In(x => x.ConversationId, conversationIds);
|
|
|
|
|
var filterPromptLog = Builders<LlmCompletionLogDocument>.Filter.In(x => x.ConversationId, conversationIds);
|
|
|
|
|
var filterContentLog = Builders<ConversationContentLogDocument>.Filter.In(x => x.ConversationId, conversationIds);
|
|
|
|
|
var filterStateLog = Builders<ConversationStateLogDocument>.Filter.In(x => x.ConversationId, conversationIds);
|
2024-12-10 18:50:13 +00:00
|
|
|
var conbTabItems = Builders<CrontabItemDocument>.Filter.In(x => x.ConversationId, conversationIds);
|
2024-01-13 02:13:38 +00:00
|
|
|
|
|
|
|
|
var exeLogDeleted = _dc.ExectionLogs.DeleteMany(filterExeLog);
|
|
|
|
|
var promptLogDeleted = _dc.LlmCompletionLogs.DeleteMany(filterPromptLog);
|
2024-02-15 20:43:36 +00:00
|
|
|
var contentLogDeleted = _dc.ContentLogs.DeleteMany(filterContentLog);
|
|
|
|
|
var stateLogDeleted = _dc.StateLogs.DeleteMany(filterStateLog);
|
2024-01-13 02:13:38 +00:00
|
|
|
var statesDeleted = _dc.ConversationStates.DeleteMany(filterSates);
|
|
|
|
|
var dialogDeleted = _dc.ConversationDialogs.DeleteMany(filterDialog);
|
2024-12-10 18:50:13 +00:00
|
|
|
var cronDeleted = _dc.CrontabItems.DeleteMany(conbTabItems);
|
2024-01-13 02:13:38 +00:00
|
|
|
var convDeleted = _dc.Conversations.DeleteMany(filterConv);
|
2024-08-22 04:09:53 +00:00
|
|
|
|
2024-01-13 02:13:38 +00:00
|
|
|
return convDeleted.DeletedCount > 0 || dialogDeleted.DeletedCount > 0 || statesDeleted.DeletedCount > 0
|
2024-02-15 20:43:36 +00:00
|
|
|
|| exeLogDeleted.DeletedCount > 0 || promptLogDeleted.DeletedCount > 0
|
2024-12-10 18:50:13 +00:00
|
|
|
|| contentLogDeleted.DeletedCount > 0 || stateLogDeleted.DeletedCount > 0
|
|
|
|
|
|| convDeleted.DeletedCount > 0;
|
2024-01-13 02:13:38 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-04 22:44:41 +00:00
|
|
|
[SideCar]
|
2024-01-13 02:13:38 +00:00
|
|
|
public List<DialogElement> GetConversationDialogs(string conversationId)
|
|
|
|
|
{
|
|
|
|
|
var dialogs = new List<DialogElement>();
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId)) return dialogs;
|
|
|
|
|
|
|
|
|
|
var filter = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var foundDialog = _dc.ConversationDialogs.Find(filter).FirstOrDefault();
|
|
|
|
|
if (foundDialog == null) return dialogs;
|
|
|
|
|
|
|
|
|
|
var formattedDialog = foundDialog.Dialogs?.Select(x => DialogMongoElement.ToDomainElement(x))?.ToList();
|
|
|
|
|
return formattedDialog ?? new List<DialogElement>();
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-04 22:44:41 +00:00
|
|
|
[SideCar]
|
2024-01-13 02:13:38 +00:00
|
|
|
public void AppendConversationDialogs(string conversationId, List<DialogElement> dialogs)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId)) return;
|
|
|
|
|
|
|
|
|
|
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
|
|
|
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var dialogElements = dialogs.Select(x => DialogMongoElement.ToMongoElement(x)).ToList();
|
|
|
|
|
var updateDialog = Builders<ConversationDialogDocument>.Update.PushEach(x => x.Dialogs, dialogElements);
|
2024-03-25 22:29:08 +00:00
|
|
|
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
|
|
|
|
.Inc(x => x.DialogCount, dialogs.Count);
|
2024-01-13 02:13:38 +00:00
|
|
|
|
|
|
|
|
_dc.ConversationDialogs.UpdateOne(filterDialog, updateDialog);
|
|
|
|
|
_dc.Conversations.UpdateOne(filterConv, updateConv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UpdateConversationTitle(string conversationId, string title)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId)) return;
|
|
|
|
|
|
|
|
|
|
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
|
|
|
var updateConv = Builders<ConversationDocument>.Update
|
|
|
|
|
.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
|
|
|
|
.Set(x => x.Title, title);
|
|
|
|
|
|
|
|
|
|
_dc.Conversations.UpdateOne(filterConv, updateConv);
|
|
|
|
|
}
|
2024-12-12 03:28:00 +00:00
|
|
|
public void UpdateConversationTitleAlias(string conversationId, string titleAlias)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId)) return;
|
|
|
|
|
|
|
|
|
|
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
|
|
|
var updateConv = Builders<ConversationDocument>.Update
|
|
|
|
|
.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
|
|
|
|
.Set(x => x.TitleAlias, titleAlias);
|
|
|
|
|
|
|
|
|
|
_dc.Conversations.UpdateOne(filterConv, updateConv);
|
|
|
|
|
}
|
2024-01-13 02:13:38 +00:00
|
|
|
|
2024-10-18 20:50:39 +00:00
|
|
|
public bool UpdateConversationTags(string conversationId, List<string> tags)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId)) return false;
|
|
|
|
|
|
|
|
|
|
var filter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
|
|
|
var update = Builders<ConversationDocument>.Update
|
|
|
|
|
.Set(x => x.Tags, tags ?? new())
|
|
|
|
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
|
|
|
|
|
|
|
|
var res = _dc.Conversations.UpdateOne(filter, update);
|
|
|
|
|
return res.ModifiedCount > 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-13 07:14:36 +00:00
|
|
|
public bool AppendConversationTags(string conversationId, List<string> tags)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId) || tags.IsNullOrEmpty()) return false;
|
|
|
|
|
|
|
|
|
|
var filter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
|
|
|
var conv = _dc.Conversations.Find(filter).FirstOrDefault();
|
|
|
|
|
if (conv == null) return false;
|
|
|
|
|
|
|
|
|
|
var curTags = conv.Tags ?? new();
|
|
|
|
|
var newTags = curTags.Concat(tags).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList();
|
|
|
|
|
var update = Builders<ConversationDocument>.Update
|
|
|
|
|
.Set(x => x.Tags, newTags)
|
|
|
|
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
|
|
|
|
|
|
|
|
var res = _dc.Conversations.UpdateOne(filter, update);
|
|
|
|
|
return res.ModifiedCount > 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-11 20:07:41 +00:00
|
|
|
public bool UpdateConversationMessage(string conversationId, UpdateMessageRequest request)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId)) return false;
|
|
|
|
|
|
|
|
|
|
var filter = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var foundDialog = _dc.ConversationDialogs.Find(filter).FirstOrDefault();
|
|
|
|
|
if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dialogs = foundDialog.Dialogs;
|
|
|
|
|
var candidates = dialogs.Where(x => x.MetaData.MessageId == request.Message.MetaData.MessageId
|
|
|
|
|
&& x.MetaData.Role == request.Message.MetaData.Role).ToList();
|
|
|
|
|
|
|
|
|
|
var found = candidates.Where((_, idx) => idx == request.InnderIndex).FirstOrDefault();
|
|
|
|
|
if (found == null) return false;
|
|
|
|
|
|
|
|
|
|
found.Content = request.Message.Content;
|
|
|
|
|
found.RichContent = request.Message.RichContent;
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(found.SecondaryContent))
|
|
|
|
|
{
|
|
|
|
|
found.SecondaryContent = request.Message.Content;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(found.SecondaryRichContent))
|
|
|
|
|
{
|
|
|
|
|
found.SecondaryRichContent = request.Message.RichContent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var update = Builders<ConversationDialogDocument>.Update.Set(x => x.Dialogs, dialogs);
|
|
|
|
|
_dc.ConversationDialogs.UpdateOne(filter, update);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-04 22:44:41 +00:00
|
|
|
[SideCar]
|
2024-04-08 03:15:51 +00:00
|
|
|
public void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint)
|
2024-03-24 01:31:15 +00:00
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId)) return;
|
|
|
|
|
|
2024-03-26 17:10:34 +00:00
|
|
|
var newBreakpoint = new BreakpointMongoElement()
|
|
|
|
|
{
|
2024-04-08 03:15:51 +00:00
|
|
|
MessageId = breakpoint.MessageId,
|
|
|
|
|
Breakpoint = breakpoint.Breakpoint,
|
|
|
|
|
CreatedTime = DateTime.UtcNow,
|
|
|
|
|
Reason = breakpoint.Reason
|
2024-03-26 17:10:34 +00:00
|
|
|
};
|
|
|
|
|
var filterState = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var updateState = Builders<ConversationStateDocument>.Update.Push(x => x.Breakpoints, newBreakpoint);
|
2024-03-24 01:31:15 +00:00
|
|
|
|
2024-03-26 17:10:34 +00:00
|
|
|
_dc.ConversationStates.UpdateOne(filterState, updateState);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-04 22:44:41 +00:00
|
|
|
[SideCar]
|
2024-04-08 03:15:51 +00:00
|
|
|
public ConversationBreakpoint? GetConversationBreakpoint(string conversationId)
|
2024-03-26 17:10:34 +00:00
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId))
|
|
|
|
|
{
|
2024-04-08 03:15:51 +00:00
|
|
|
return null;
|
2024-03-26 17:10:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var filter = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var state = _dc.ConversationStates.Find(filter).FirstOrDefault();
|
2024-04-08 14:58:25 +00:00
|
|
|
var leafNode = state?.Breakpoints?.LastOrDefault();
|
2024-03-26 17:10:34 +00:00
|
|
|
|
2024-04-08 14:58:25 +00:00
|
|
|
if (leafNode == null)
|
2024-03-26 17:10:34 +00:00
|
|
|
{
|
2024-04-08 03:15:51 +00:00
|
|
|
return null;
|
2024-03-26 17:10:34 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-08 14:58:25 +00:00
|
|
|
return new ConversationBreakpoint
|
2024-04-08 03:15:51 +00:00
|
|
|
{
|
2024-04-08 14:58:25 +00:00
|
|
|
Breakpoint = leafNode.Breakpoint,
|
|
|
|
|
MessageId = leafNode.MessageId,
|
|
|
|
|
Reason = leafNode.Reason,
|
|
|
|
|
CreatedTime = leafNode.CreatedTime,
|
|
|
|
|
};
|
2024-03-24 01:31:15 +00:00
|
|
|
}
|
|
|
|
|
|
2024-01-13 02:13:38 +00:00
|
|
|
public ConversationState GetConversationStates(string conversationId)
|
|
|
|
|
{
|
|
|
|
|
var states = new ConversationState();
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId)) return states;
|
|
|
|
|
|
|
|
|
|
var filter = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var foundStates = _dc.ConversationStates.Find(filter).FirstOrDefault();
|
|
|
|
|
if (foundStates == null || foundStates.States.IsNullOrEmpty()) return states;
|
|
|
|
|
|
|
|
|
|
var savedStates = foundStates.States.Select(x => StateMongoElement.ToDomainElement(x)).ToList();
|
|
|
|
|
return new ConversationState(savedStates);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
|
|
|
|
|
{
|
2024-03-25 22:29:08 +00:00
|
|
|
if (string.IsNullOrEmpty(conversationId) || states == null) return;
|
2024-01-13 02:13:38 +00:00
|
|
|
|
|
|
|
|
var filterStates = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var saveStates = states.Select(x => StateMongoElement.ToMongoElement(x)).ToList();
|
|
|
|
|
var updateStates = Builders<ConversationStateDocument>.Update.Set(x => x.States, saveStates);
|
|
|
|
|
|
|
|
|
|
_dc.ConversationStates.UpdateOne(filterStates, updateStates);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UpdateConversationStatus(string conversationId, string status)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(status)) return;
|
|
|
|
|
|
|
|
|
|
var filter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
|
|
|
var update = Builders<ConversationDocument>.Update
|
|
|
|
|
.Set(x => x.Status, status)
|
|
|
|
|
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
|
|
|
|
|
|
|
|
|
_dc.Conversations.UpdateOne(filter, update);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Conversation GetConversation(string conversationId)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId)) return null;
|
|
|
|
|
|
|
|
|
|
var filterConv = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
|
|
|
var filterDialog = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var filterState = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
|
|
|
|
|
var conv = _dc.Conversations.Find(filterConv).FirstOrDefault();
|
|
|
|
|
var dialog = _dc.ConversationDialogs.Find(filterDialog).FirstOrDefault();
|
|
|
|
|
var states = _dc.ConversationStates.Find(filterState).FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
if (conv == null) return null;
|
|
|
|
|
|
|
|
|
|
var dialogElements = dialog?.Dialogs?.Select(x => DialogMongoElement.ToDomainElement(x))?.ToList() ?? new List<DialogElement>();
|
|
|
|
|
var curStates = new Dictionary<string, string>();
|
|
|
|
|
states.States.ForEach(x =>
|
|
|
|
|
{
|
|
|
|
|
curStates[x.Key] = x.Values?.LastOrDefault()?.Data ?? string.Empty;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return new Conversation
|
|
|
|
|
{
|
|
|
|
|
Id = conv.Id.ToString(),
|
|
|
|
|
AgentId = conv.AgentId.ToString(),
|
|
|
|
|
UserId = conv.UserId.ToString(),
|
|
|
|
|
Title = conv.Title,
|
|
|
|
|
Channel = conv.Channel,
|
|
|
|
|
Status = conv.Status,
|
|
|
|
|
Dialogs = dialogElements,
|
|
|
|
|
States = curStates,
|
2024-03-25 22:29:08 +00:00
|
|
|
DialogCount = conv.DialogCount,
|
2024-10-18 20:50:39 +00:00
|
|
|
Tags = conv.Tags,
|
2024-01-13 02:13:38 +00:00
|
|
|
CreatedTime = conv.CreatedTime,
|
2024-03-26 17:10:34 +00:00
|
|
|
UpdatedTime = conv.UpdatedTime
|
2024-01-13 02:13:38 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 05:23:20 +00:00
|
|
|
public PagedItems<Conversation> GetConversations(ConversationFilter filter)
|
2024-01-13 02:13:38 +00:00
|
|
|
{
|
2024-11-13 23:16:49 +00:00
|
|
|
if (filter == null)
|
|
|
|
|
{
|
|
|
|
|
filter = ConversationFilter.Empty();
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-09 07:34:49 +00:00
|
|
|
var convBuilder = Builders<ConversationDocument>.Filter;
|
|
|
|
|
var convFilters = new List<FilterDefinition<ConversationDocument>>() { convBuilder.Empty };
|
2024-01-13 02:13:38 +00:00
|
|
|
|
2024-07-09 07:34:49 +00:00
|
|
|
// Filter conversations
|
2024-03-25 06:28:20 +00:00
|
|
|
if (!string.IsNullOrEmpty(filter?.Id))
|
|
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
convFilters.Add(convBuilder.Eq(x => x.Id, filter.Id));
|
2024-03-25 06:28:20 +00:00
|
|
|
}
|
2024-08-22 04:09:53 +00:00
|
|
|
if (!string.IsNullOrEmpty(filter?.Title))
|
|
|
|
|
{
|
|
|
|
|
convFilters.Add(convBuilder.Regex(x => x.Title, new BsonRegularExpression(filter.Title, "i")));
|
|
|
|
|
}
|
2024-12-12 03:28:00 +00:00
|
|
|
if (!string.IsNullOrEmpty(filter?.TitleAlias))
|
|
|
|
|
{
|
|
|
|
|
convFilters.Add(convBuilder.Regex(x => x.Title, new BsonRegularExpression(filter.TitleAlias, "i")));
|
|
|
|
|
}
|
2024-03-25 06:28:20 +00:00
|
|
|
if (!string.IsNullOrEmpty(filter?.AgentId))
|
|
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
convFilters.Add(convBuilder.Eq(x => x.AgentId, filter.AgentId));
|
2024-03-25 06:28:20 +00:00
|
|
|
}
|
|
|
|
|
if (!string.IsNullOrEmpty(filter?.Status))
|
|
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
convFilters.Add(convBuilder.Eq(x => x.Status, filter.Status));
|
2024-03-25 06:28:20 +00:00
|
|
|
}
|
|
|
|
|
if (!string.IsNullOrEmpty(filter?.Channel))
|
|
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
convFilters.Add(convBuilder.Eq(x => x.Channel, filter.Channel));
|
2024-03-25 06:28:20 +00:00
|
|
|
}
|
|
|
|
|
if (!string.IsNullOrEmpty(filter?.UserId))
|
|
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
convFilters.Add(convBuilder.Eq(x => x.UserId, filter.UserId));
|
2024-03-25 06:28:20 +00:00
|
|
|
}
|
|
|
|
|
if (!string.IsNullOrEmpty(filter?.TaskId))
|
|
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
convFilters.Add(convBuilder.Eq(x => x.TaskId, filter.TaskId));
|
2024-03-25 06:28:20 +00:00
|
|
|
}
|
|
|
|
|
if (filter?.StartTime != null)
|
|
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
convFilters.Add(convBuilder.Gte(x => x.CreatedTime, filter.StartTime.Value));
|
2024-03-25 06:28:20 +00:00
|
|
|
}
|
2024-10-18 20:50:39 +00:00
|
|
|
if (filter?.Tags != null && filter.Tags.Any())
|
|
|
|
|
{
|
|
|
|
|
convFilters.Add(convBuilder.AnyIn(x => x.Tags, filter.Tags));
|
|
|
|
|
}
|
2024-01-13 02:13:38 +00:00
|
|
|
|
2024-07-09 07:34:49 +00:00
|
|
|
// Filter states
|
|
|
|
|
var stateFilters = new List<FilterDefinition<ConversationStateDocument>>();
|
|
|
|
|
if (filter != null && string.IsNullOrEmpty(filter.Id) && !filter.States.IsNullOrEmpty())
|
2024-03-08 22:02:08 +00:00
|
|
|
{
|
|
|
|
|
foreach (var pair in filter.States)
|
|
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
var elementFilters = new List<FilterDefinition<StateMongoElement>> { Builders<StateMongoElement>.Filter.Eq(x => x.Key, pair.Key) };
|
|
|
|
|
if (!string.IsNullOrEmpty(pair.Value))
|
2024-03-08 22:02:08 +00:00
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
elementFilters.Add(Builders<StateMongoElement>.Filter.Eq("Values.Data", pair.Value));
|
|
|
|
|
}
|
|
|
|
|
stateFilters.Add(Builders<ConversationStateDocument>.Filter.ElemMatch(x => x.States, Builders<StateMongoElement>.Filter.And(elementFilters)));
|
2024-03-08 22:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-09 07:34:49 +00:00
|
|
|
var targetConvIds = _dc.ConversationStates.Find(Builders<ConversationStateDocument>.Filter.And(stateFilters)).ToEnumerable().Select(x => x.ConversationId).Distinct().ToList();
|
|
|
|
|
convFilters.Add(convBuilder.In(x => x.Id, targetConvIds));
|
2024-03-08 22:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-09 07:34:49 +00:00
|
|
|
// Sort and paginate
|
|
|
|
|
var filterDef = convBuilder.And(convFilters);
|
2024-02-04 05:42:13 +00:00
|
|
|
var sortDef = Builders<ConversationDocument>.Sort.Descending(x => x.CreatedTime);
|
2024-01-23 05:04:06 +00:00
|
|
|
var pager = filter?.Pager ?? new Pagination();
|
2024-08-22 04:00:36 +00:00
|
|
|
|
|
|
|
|
// Apply sorting based on sort and order fields
|
|
|
|
|
if (!string.IsNullOrEmpty(pager?.Sort))
|
|
|
|
|
{
|
|
|
|
|
var sortField = ConvertSnakeCaseToPascalCase(pager.Sort);
|
|
|
|
|
|
|
|
|
|
if (pager.Order == "asc")
|
|
|
|
|
{
|
|
|
|
|
sortDef = Builders<ConversationDocument>.Sort.Ascending(sortField);
|
|
|
|
|
}
|
|
|
|
|
else if (pager.Order == "desc")
|
|
|
|
|
{
|
|
|
|
|
sortDef = Builders<ConversationDocument>.Sort.Descending(sortField);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-04 05:42:13 +00:00
|
|
|
var conversationDocs = _dc.Conversations.Find(filterDef).Sort(sortDef).Skip(pager.Offset).Limit(pager.Size).ToList();
|
2024-01-18 05:23:20 +00:00
|
|
|
var count = _dc.Conversations.CountDocuments(filterDef);
|
2024-01-13 02:13:38 +00:00
|
|
|
|
2024-07-09 07:34:49 +00:00
|
|
|
var conversations = conversationDocs.Select(x => new Conversation
|
2024-01-13 02:13:38 +00:00
|
|
|
{
|
2024-07-09 07:34:49 +00:00
|
|
|
Id = x.Id.ToString(),
|
|
|
|
|
AgentId = x.AgentId.ToString(),
|
|
|
|
|
UserId = x.UserId.ToString(),
|
|
|
|
|
TaskId = x.TaskId,
|
|
|
|
|
Title = x.Title,
|
|
|
|
|
Channel = x.Channel,
|
|
|
|
|
Status = x.Status,
|
|
|
|
|
DialogCount = x.DialogCount,
|
2024-10-18 20:50:39 +00:00
|
|
|
Tags = x.Tags ?? new(),
|
2024-07-09 07:34:49 +00:00
|
|
|
CreatedTime = x.CreatedTime,
|
|
|
|
|
UpdatedTime = x.UpdatedTime
|
|
|
|
|
}).ToList();
|
2024-01-13 02:13:38 +00:00
|
|
|
|
2024-01-18 05:23:20 +00:00
|
|
|
return new PagedItems<Conversation>
|
|
|
|
|
{
|
|
|
|
|
Items = conversations,
|
|
|
|
|
Count = (int)count
|
|
|
|
|
};
|
2024-01-13 02:13:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<Conversation> GetLastConversations()
|
|
|
|
|
{
|
|
|
|
|
var records = new List<Conversation>();
|
|
|
|
|
var conversations = _dc.Conversations.Aggregate()
|
2024-03-08 22:02:08 +00:00
|
|
|
.Group(c => c.UserId, g => g.First(x => x.CreatedTime == g.Select(y => y.CreatedTime).Max()))
|
2024-01-13 02:13:38 +00:00
|
|
|
.ToList();
|
|
|
|
|
return conversations.Select(c => new Conversation()
|
|
|
|
|
{
|
|
|
|
|
Id = c.Id.ToString(),
|
|
|
|
|
AgentId = c.AgentId.ToString(),
|
|
|
|
|
UserId = c.UserId.ToString(),
|
|
|
|
|
Title = c.Title,
|
|
|
|
|
Channel = c.Channel,
|
|
|
|
|
Status = c.Status,
|
2024-03-25 22:29:08 +00:00
|
|
|
DialogCount = c.DialogCount,
|
2024-10-18 20:50:39 +00:00
|
|
|
Tags = c.Tags ?? new(),
|
2024-01-13 02:13:38 +00:00
|
|
|
CreatedTime = c.CreatedTime,
|
2024-03-26 17:10:34 +00:00
|
|
|
UpdatedTime = c.UpdatedTime
|
2024-01-13 02:13:38 +00:00
|
|
|
}).ToList();
|
|
|
|
|
}
|
2024-01-29 01:33:40 +00:00
|
|
|
|
2024-09-09 17:26:26 +00:00
|
|
|
public List<string> GetIdleConversations(int batchSize, int messageLimit, int bufferHours, IEnumerable<string> excludeAgentIds)
|
2024-03-07 07:34:52 +00:00
|
|
|
{
|
2024-03-07 17:25:23 +00:00
|
|
|
var page = 1;
|
2024-03-11 20:33:28 +00:00
|
|
|
var batchLimit = 100;
|
2024-03-07 17:30:51 +00:00
|
|
|
var utcNow = DateTime.UtcNow;
|
2024-03-07 17:25:23 +00:00
|
|
|
var conversationIds = new List<string>();
|
|
|
|
|
|
|
|
|
|
if (batchSize <= 0 || batchSize > batchLimit)
|
|
|
|
|
{
|
|
|
|
|
batchSize = batchLimit;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-25 22:29:08 +00:00
|
|
|
if (bufferHours <= 0)
|
|
|
|
|
{
|
|
|
|
|
bufferHours = 12;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (messageLimit <= 0)
|
|
|
|
|
{
|
|
|
|
|
messageLimit = 2;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-08 22:02:08 +00:00
|
|
|
while (true)
|
2024-03-07 17:25:23 +00:00
|
|
|
{
|
|
|
|
|
var skip = (page - 1) * batchSize;
|
|
|
|
|
var candidates = _dc.Conversations.AsQueryable()
|
2025-01-10 18:58:16 +00:00
|
|
|
.Where(x => ((!excludeAgentIds.Contains(x.AgentId) && x.DialogCount <= messageLimit)
|
|
|
|
|
|| (excludeAgentIds.Contains(x.AgentId) && x.DialogCount == 0))
|
|
|
|
|
&& x.UpdatedTime <= utcNow.AddHours(-bufferHours))
|
2024-03-07 17:25:23 +00:00
|
|
|
.Skip(skip)
|
|
|
|
|
.Take(batchSize)
|
|
|
|
|
.Select(x => x.Id)
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
if (candidates.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-08-22 04:09:53 +00:00
|
|
|
|
2024-03-25 22:29:08 +00:00
|
|
|
conversationIds = conversationIds.Concat(candidates).Distinct().ToList();
|
2024-03-07 17:25:23 +00:00
|
|
|
if (conversationIds.Count >= batchSize)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
page++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return conversationIds.Take(batchSize).ToList();
|
2024-03-07 07:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
2024-05-06 22:31:52 +00:00
|
|
|
public IEnumerable<string> TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
|
2024-01-29 01:33:40 +00:00
|
|
|
{
|
2024-05-06 22:31:52 +00:00
|
|
|
var deletedMessageIds = new List<string>();
|
|
|
|
|
if (string.IsNullOrEmpty(conversationId) || string.IsNullOrEmpty(messageId))
|
|
|
|
|
{
|
|
|
|
|
return deletedMessageIds;
|
|
|
|
|
}
|
2024-01-29 01:33:40 +00:00
|
|
|
|
|
|
|
|
var dialogFilter = Builders<ConversationDialogDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var foundDialog = _dc.ConversationDialogs.Find(dialogFilter).FirstOrDefault();
|
2024-05-06 22:31:52 +00:00
|
|
|
if (foundDialog == null || foundDialog.Dialogs.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
return deletedMessageIds;
|
|
|
|
|
}
|
2024-01-29 01:33:40 +00:00
|
|
|
|
|
|
|
|
var foundIdx = foundDialog.Dialogs.FindIndex(x => x.MetaData?.MessageId == messageId);
|
2024-05-06 22:31:52 +00:00
|
|
|
if (foundIdx < 0)
|
|
|
|
|
{
|
|
|
|
|
return deletedMessageIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deletedMessageIds = foundDialog.Dialogs.Where((x, idx) => idx >= foundIdx && !string.IsNullOrEmpty(x.MetaData?.MessageId))
|
|
|
|
|
.Select(x => x.MetaData.MessageId).Distinct().ToList();
|
2024-01-29 01:33:40 +00:00
|
|
|
|
|
|
|
|
// Handle truncated dialogs
|
|
|
|
|
var truncatedDialogs = foundDialog.Dialogs.Where((x, idx) => idx < foundIdx).ToList();
|
2024-08-22 04:09:53 +00:00
|
|
|
|
2024-01-29 01:33:40 +00:00
|
|
|
// Handle truncated states
|
|
|
|
|
var refTime = foundDialog.Dialogs.ElementAt(foundIdx).MetaData.CreateTime;
|
|
|
|
|
var stateFilter = Builders<ConversationStateDocument>.Filter.Eq(x => x.ConversationId, conversationId);
|
|
|
|
|
var foundStates = _dc.ConversationStates.Find(stateFilter).FirstOrDefault();
|
|
|
|
|
|
2024-03-26 17:10:34 +00:00
|
|
|
if (foundStates != null)
|
2024-01-29 01:33:40 +00:00
|
|
|
{
|
2024-03-26 17:10:34 +00:00
|
|
|
// Truncate states
|
|
|
|
|
if (!foundStates.States.IsNullOrEmpty())
|
2024-03-25 04:40:15 +00:00
|
|
|
{
|
2024-03-26 17:10:34 +00:00
|
|
|
var truncatedStates = new List<StateMongoElement>();
|
|
|
|
|
foreach (var state in foundStates.States)
|
|
|
|
|
{
|
2024-03-26 21:57:13 +00:00
|
|
|
if (!state.Versioning)
|
|
|
|
|
{
|
|
|
|
|
truncatedStates.Add(state);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var values = state.Values.Where(x => x.MessageId != messageId)
|
|
|
|
|
.Where(x => x.UpdateTime < refTime)
|
|
|
|
|
.ToList();
|
2024-03-26 17:10:34 +00:00
|
|
|
if (values.Count == 0) continue;
|
2024-03-25 04:40:15 +00:00
|
|
|
|
2024-03-26 17:10:34 +00:00
|
|
|
state.Values = values;
|
|
|
|
|
truncatedStates.Add(state);
|
|
|
|
|
}
|
|
|
|
|
foundStates.States = truncatedStates;
|
2024-03-25 04:40:15 +00:00
|
|
|
}
|
2024-01-29 01:33:40 +00:00
|
|
|
|
2024-03-26 17:10:34 +00:00
|
|
|
// Truncate breakpoints
|
|
|
|
|
if (!foundStates.Breakpoints.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
var breakpoints = foundStates.Breakpoints ?? new List<BreakpointMongoElement>();
|
2024-05-13 16:32:11 +00:00
|
|
|
var truncatedBreakpoints = breakpoints.Where(x => x.CreatedTime < refTime).ToList();
|
2024-03-26 17:10:34 +00:00
|
|
|
foundStates.Breakpoints = truncatedBreakpoints;
|
|
|
|
|
}
|
2024-08-22 04:09:53 +00:00
|
|
|
|
2024-03-26 17:10:34 +00:00
|
|
|
// Update
|
2024-03-25 04:40:15 +00:00
|
|
|
_dc.ConversationStates.ReplaceOne(stateFilter, foundStates);
|
2024-01-29 01:33:40 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-25 22:29:08 +00:00
|
|
|
// Save dialogs
|
2024-01-29 01:33:40 +00:00
|
|
|
foundDialog.Dialogs = truncatedDialogs;
|
|
|
|
|
_dc.ConversationDialogs.ReplaceOne(dialogFilter, foundDialog);
|
2024-02-28 23:37:36 +00:00
|
|
|
|
2024-03-25 22:29:08 +00:00
|
|
|
// Update conversation
|
|
|
|
|
var convFilter = Builders<ConversationDocument>.Filter.Eq(x => x.Id, conversationId);
|
|
|
|
|
var updateConv = Builders<ConversationDocument>.Update.Set(x => x.UpdatedTime, DateTime.UtcNow)
|
|
|
|
|
.Set(x => x.DialogCount, truncatedDialogs.Count);
|
|
|
|
|
_dc.Conversations.UpdateOne(convFilter, updateConv);
|
|
|
|
|
|
2024-02-28 23:37:36 +00:00
|
|
|
// Remove logs
|
|
|
|
|
if (cleanLog)
|
|
|
|
|
{
|
|
|
|
|
var contentLogBuilder = Builders<ConversationContentLogDocument>.Filter;
|
|
|
|
|
var stateLogBuilder = Builders<ConversationStateLogDocument>.Filter;
|
|
|
|
|
|
|
|
|
|
var contentLogFilters = new List<FilterDefinition<ConversationContentLogDocument>>()
|
|
|
|
|
{
|
|
|
|
|
contentLogBuilder.Eq(x => x.ConversationId, conversationId),
|
|
|
|
|
contentLogBuilder.Gte(x => x.CreateTime, refTime)
|
|
|
|
|
};
|
|
|
|
|
var stateLogFilters = new List<FilterDefinition<ConversationStateLogDocument>>()
|
|
|
|
|
{
|
|
|
|
|
stateLogBuilder.Eq(x => x.ConversationId, conversationId),
|
|
|
|
|
stateLogBuilder.Gte(x => x.CreateTime, refTime)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_dc.ContentLogs.DeleteMany(contentLogBuilder.And(contentLogFilters));
|
|
|
|
|
_dc.StateLogs.DeleteMany(stateLogBuilder.And(stateLogFilters));
|
|
|
|
|
}
|
2024-08-22 04:09:53 +00:00
|
|
|
|
2024-05-06 22:31:52 +00:00
|
|
|
return deletedMessageIds;
|
2024-01-29 01:33:40 +00:00
|
|
|
}
|
2024-08-22 04:00:36 +00:00
|
|
|
|
|
|
|
|
private string ConvertSnakeCaseToPascalCase(string snakeCase)
|
|
|
|
|
{
|
|
|
|
|
string[] words = snakeCase.Split('_');
|
|
|
|
|
StringBuilder pascalCase = new();
|
|
|
|
|
|
|
|
|
|
foreach (string word in words)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(word))
|
|
|
|
|
{
|
|
|
|
|
string firstLetter = word[..1].ToUpper();
|
|
|
|
|
string restOfWord = word[1..].ToLower();
|
|
|
|
|
pascalCase.Append(firstLetter + restOfWord);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pascalCase.ToString();
|
|
|
|
|
}
|
2024-01-13 02:13:38 +00:00
|
|
|
}
|