diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs index c9c30edc..004d94e6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs @@ -6,10 +6,9 @@ public class Conversation public string AgentId { get; set; } = string.Empty; public string UserId { get; set; } = string.Empty; public string Title { get; set; } = string.Empty; - public string Dialog { get; set; } + public string Dialog { get; set; } = string.Empty; + public ConversationState State { get; set; } public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; public DateTime CreatedTime { get; set; } = DateTime.UtcNow; - - public ConversationState State { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/UserRecord.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/UserRecord.cs index bbefdc47..6500ba27 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/UserRecord.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/UserRecord.cs @@ -25,7 +25,7 @@ public class UserRecord : RecordBase public string Password { get; set; } = string.Empty; [MaxLength(36)] - public string ExternalId { get; set; } + public string? ExternalId { get; set; } [Required] public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/ListExtenstion.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/ListExtenstions.cs similarity index 50% rename from src/Infrastructure/BotSharp.Abstraction/Utilities/ListExtenstion.cs rename to src/Infrastructure/BotSharp.Abstraction/Utilities/ListExtenstions.cs index 7dcc566a..acba3215 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/ListExtenstion.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/ListExtenstions.cs @@ -1,8 +1,8 @@ namespace BotSharp.Abstraction.Utilities; -public static class ListExtenstion +public static class ListExtenstions { - public static bool IsEmpty(this IEnumerable strList) + public static bool IsNullOrEmpty(this IEnumerable strList) { return strList == null || !strList.Any(); } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs index 02b2323c..f7b9f690 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs @@ -29,7 +29,7 @@ public partial class AgentService hook.OnInstructionLoaded(agent.Instruction, templateDict); } - if (!agent.Functions.IsEmpty()) + if (!agent.Functions.IsNullOrEmpty()) { var functions = agent.Functions; hook.OnFunctionsLoaded(ref functions); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 7c4a3183..737af8d7 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -27,10 +27,10 @@ public partial class AgentService if (!string.IsNullOrEmpty(agent.Instruction)) record.Instruction = agent.Instruction; - if (!agent.Functions.IsEmpty()) + if (!agent.Functions.IsNullOrEmpty()) record.Functions = agent.Functions; - if (!agent.Responses.IsEmpty()) + if (!agent.Responses.IsNullOrEmpty()) record.Responses = agent.Responses; db.UpdateAgent(record); diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 87e3ff7a..87a1dbb7 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -39,11 +39,6 @@ public partial class ConversationService : IConversationService public async Task GetConversation(string id) { var db = _services.GetRequiredService(); - //var query = from sess in db.Conversation - // where sess.Id == id - // orderby sess.CreatedTime descending - // select sess.ToConversation(); - var conversation = db.GetConversation(id); return conversation?.ToConversation(); } @@ -51,11 +46,6 @@ public partial class ConversationService : IConversationService public async Task> GetConversations() { var db = _services.GetRequiredService(); - //var query = from sess in db.Conversation - // where sess.UserId == _user.Id - // orderby sess.CreatedTime descending - // select sess.ToConversation(); - var user = db.User.FirstOrDefault(x => x.ExternalId == _user.Id); var conversations = db.GetConversations(user?.Id); return conversations.Select(x => x.ToConversation()).OrderByDescending(x => x.CreatedTime).ToList(); diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs index 82a9162d..82c88c9a 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs @@ -311,7 +311,7 @@ public class FileRepository : IBotSharpRepository File.WriteAllText(instructionFile, agent.Instruction); } - if (!agent.Functions.IsEmpty()) + if (!agent.Functions.IsNullOrEmpty()) { var functionFile = Path.Combine(dir, "functions.json"); var functions = new List(); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Router.cs b/src/Infrastructure/BotSharp.Core/Routing/Router.cs index 8f1b5de4..779f44be 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Router.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Router.cs @@ -36,7 +36,7 @@ public class Router : IAgentRouting var records = db.RoutingItem.Select(x => x.ToRoutingItem()).ToArray(); var profiles = db.RoutingProfile.ToList(); - if (!profiles.IsEmpty()) + if (!profiles.IsNullOrEmpty()) { var state = _services.GetRequiredService(); var name = state.GetState("channel"); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/RoutingController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/RoutingController.cs index bdcb529f..3253d052 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/RoutingController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/RoutingController.cs @@ -16,7 +16,7 @@ public class RoutingController : ControllerBase, IApiAdapter _routingService = routingService; } - [HttpPost("/routing-items")] + [HttpPost("/routing/items")] public async Task> CreateRoutingItems(List routingItems) { var items = routingItems?.Select(x => x.ToRoutingItem())?.ToList() ?? new List(); @@ -24,7 +24,7 @@ public class RoutingController : ControllerBase, IApiAdapter return savedItems.Select(x => RoutingItemViewModel.FromRoutingItem(x)).ToList(); } - [HttpPost("/routing-profiles")] + [HttpPost("/routing/profiles")] public async Task> CreateRoutingProfiles(List routingProfiles) { var profiles = routingProfiles?.Select(x => x.ToRoutingProfile())?.ToList() ?? new List(); @@ -32,13 +32,13 @@ public class RoutingController : ControllerBase, IApiAdapter return savedProfiles.Select(x => RoutingProfileViewModel.FromRoutingProfile(x)).ToList(); } - [HttpDelete("/routing-items")] + [HttpDelete("/routing/items")] public async Task RemoveRoutingItems() { await _routingService.DeleteRoutingItems(); } - [HttpDelete("/routing-profiles")] + [HttpDelete("/routing/profiles")] public async Task RemoveRoutingProfiles() { await _routingService.DeleteRoutingProfiles(); diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs index 6f3912a5..98502ec4 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs @@ -43,10 +43,10 @@ public class AgentUpdateModel if (Samples != null) agent.Samples = Samples; - if (!Functions.IsEmpty()) + if (!Functions.IsNullOrEmpty()) agent.Functions = Functions; - if (!Responses.IsEmpty()) + if (!Responses.IsNullOrEmpty()) agent.Responses = Responses; return agent; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/BotSharp.Plugin.MongoStorage.csproj b/src/Plugins/BotSharp.Plugin.MongoStorage/BotSharp.Plugin.MongoStorage.csproj index d376992e..253f9962 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/BotSharp.Plugin.MongoStorage.csproj +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/BotSharp.Plugin.MongoStorage.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -8,7 +8,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationCollection.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationCollection.cs index c7aaa58e..709b7f05 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationCollection.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationCollection.cs @@ -7,8 +7,6 @@ public class ConversationCollection : MongoBase public Guid AgentId { get; set; } public Guid UserId { get; set; } public string Title { get; set; } - public string Dialog { get; set; } - public List State { get; set; } public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDialogCollection.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDialogCollection.cs new file mode 100644 index 00000000..889fcd49 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDialogCollection.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Plugin.MongoStorage.Collections; + +public class ConversationDialogCollection : MongoBase +{ + public Guid ConversationId { get; set; } + public string Dialog { get; set; } + public DateTime CreatedTime { get; set; } + public DateTime UpdatedTime { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStatesCollection.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStatesCollection.cs new file mode 100644 index 00000000..20bb5851 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStatesCollection.cs @@ -0,0 +1,11 @@ +using BotSharp.Abstraction.Repositories.Models; + +namespace BotSharp.Plugin.MongoStorage.Collections; + +public class ConversationStatesCollection : MongoBase +{ + public Guid ConversationId { get; set; } + public List State { get; set; } + public DateTime CreatedTime { get; set; } + public DateTime UpdatedTime { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserCollection.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserCollection.cs index 4af14a21..03327410 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserCollection.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserCollection.cs @@ -7,7 +7,7 @@ public class UserCollection : MongoBase public string Email { get; set; } public string Salt { get; set; } public string Password { get; set; } - public string ExternalId { get; set; } + public string? ExternalId { get; set; } public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs index 27d7ec27..ee0f7955 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs @@ -31,6 +31,12 @@ public class MongoDbContext public IMongoCollection Conversations => Database.GetCollection("OneBrainConversations"); + public IMongoCollection ConversationDialogs + => Database.GetCollection("OneBrainConversationDialogs"); + + public IMongoCollection ConversationStates + => Database.GetCollection("OneBrainConversationStates"); + public IMongoCollection Users => Database.GetCollection("OneBrainUsers"); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs index 1e3ec3a1..da20baa6 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs @@ -111,16 +111,26 @@ public class MongoRepository : IBotSharpRepository return _conversations.AsQueryable(); } + _conversations = new List(); var conversationDocs = _dc.Conversations?.AsQueryable()?.ToList() ?? new List(); - _conversations = conversationDocs.Select(x => new ConversationRecord + + foreach (var conv in conversationDocs) { - Id = x.Id.ToString(), - AgentId = x.AgentId.ToString(), - UserId = x.UserId.ToString(), - Title = x.Title, - CreatedTime = x.CreatedTime, - UpdatedTime = x.UpdatedTime - }).ToList(); + var convId = conv.Id.ToString(); + var dialog = GetConversationDialog(convId); + var states = GetConversationState(convId); + _conversations.Add(new ConversationRecord + { + Id = convId, + AgentId = conv.AgentId.ToString(), + UserId = conv.UserId.ToString(), + Title = conv.Title, + Dialog = dialog, + State = states, + CreatedTime = conv.CreatedTime, + UpdatedTime = conv.UpdatedTime + }); + } return _conversations.AsQueryable(); } @@ -214,8 +224,6 @@ public class MongoRepository : IBotSharpRepository AgentId = Guid.Parse(x.AgentId), UserId = Guid.Parse(x.UserId), Title = x.Title, - Dialog = x.Dialog, - State = x.State, CreatedTime = x.CreatedTime, UpdatedTime = x.UpdatedTime }).ToList(); @@ -227,8 +235,6 @@ public class MongoRepository : IBotSharpRepository .Set(x => x.AgentId, conversation.AgentId) .Set(x => x.UserId, conversation.UserId) .Set(x => x.Title, conversation.Title) - .Set(x => x.Dialog, conversation.Dialog) - .Set(x => x.State, conversation.State) .Set(x => x.CreatedTime, conversation.CreatedTime) .Set(x => x.UpdatedTime, conversation.UpdatedTime); _dc.Conversations.UpdateOne(filter, update, _options); @@ -461,45 +467,63 @@ public class MongoRepository : IBotSharpRepository { if (conversation == null) return; - var collection = new ConversationCollection + var conv = new ConversationCollection { Id = Guid.Parse(conversation.Id), AgentId = Guid.Parse(conversation.AgentId), UserId = Guid.Parse(conversation.UserId), Title = conversation.Title, + CreatedTime = DateTime.UtcNow, + UpdatedTime = DateTime.UtcNow, + }; + + var dialog = new ConversationDialogCollection + { + Id = Guid.NewGuid(), + ConversationId = conv.Id, Dialog = string.Empty, + CreatedTime = DateTime.UtcNow, + UpdatedTime = DateTime.UtcNow, + }; + + var states = new ConversationStatesCollection + { + Id = Guid.NewGuid(), + ConversationId = conv.Id, State = new List(), CreatedTime = DateTime.UtcNow, UpdatedTime = DateTime.UtcNow, }; - _dc.Conversations.InsertOne(collection); + _dc.Conversations.InsertOne(conv); + _dc.ConversationDialogs.InsertOne(dialog); + _dc.ConversationStates.InsertOne(states); } public string GetConversationDialog(string conversationId) { if (string.IsNullOrEmpty(conversationId)) return string.Empty; - var filterById = Builders.Filter.Eq(x => x.Id, Guid.Parse(conversationId)); - var foundConversation = _dc.Conversations.Find(filterById).FirstOrDefault(); - if (foundConversation == null) return string.Empty; + var filter = Builders.Filter.Eq(x => x.ConversationId, Guid.Parse(conversationId)); + var foundDialog = _dc.ConversationDialogs.Find(filter).FirstOrDefault(); + if (foundDialog == null) return string.Empty; - return foundConversation.Dialog; + return foundDialog.Dialog; } public void UpdateConversationDialog(string conversationId, string dialogs) { if (string.IsNullOrEmpty(conversationId)) return; - var filterById = Builders.Filter.Eq(x => x.Id, Guid.Parse(conversationId)); - var foundConversation = _dc.Conversations.Find(filterById).FirstOrDefault(); - if (foundConversation == null) return; + var filter = Builders.Filter.Eq(x => x.ConversationId, Guid.Parse(conversationId)); + var foundDialog = _dc.ConversationDialogs.Find(filter).FirstOrDefault(); + if (foundDialog == null) return; - var update = Builders.Update + var update = Builders.Update .Set(x => x.Dialog, dialogs) .Set(x => x.UpdatedTime, DateTime.UtcNow); - _dc.Conversations.UpdateOne(filterById, update); + _dc.ConversationDialogs.UpdateOne(filter, update); } public List GetConversationState(string conversationId) @@ -507,11 +531,11 @@ public class MongoRepository : IBotSharpRepository var states = new List(); if (string.IsNullOrEmpty(conversationId)) return states; - var filterById = Builders.Filter.Eq(x => x.Id, Guid.Parse(conversationId)); - var foundConversation = _dc.Conversations.Find(filterById).FirstOrDefault(); - if (foundConversation == null) return states; + var filter = Builders.Filter.Eq(x => x.ConversationId, Guid.Parse(conversationId)); + var foundStates = _dc.ConversationStates.Find(filter).FirstOrDefault(); + if (foundStates == null) return states; - var savedStates = foundConversation.State ?? new List(); + var savedStates = foundStates.State ?? new List(); return savedStates; } @@ -519,15 +543,15 @@ public class MongoRepository : IBotSharpRepository { if (string.IsNullOrEmpty(conversationId)) return; - var filterById = Builders.Filter.Eq(x => x.Id, Guid.Parse(conversationId)); - var foundConversation = _dc.Conversations.Find(filterById).FirstOrDefault(); - if (foundConversation == null) return; + var filter = Builders.Filter.Eq(x => x.ConversationId, Guid.Parse(conversationId)); + var foundStates = _dc.ConversationStates.Find(filter).FirstOrDefault(); + if (foundStates == null) return; - var update = Builders.Update + var update = Builders.Update .Set(x => x.State, state) .Set(x => x.UpdatedTime, DateTime.UtcNow); - _dc.Conversations.UpdateOne(filterById, update); + _dc.ConversationStates.UpdateOne(filter, update); } public ConversationRecord GetConversation(string conversationId) @@ -535,37 +559,54 @@ public class MongoRepository : IBotSharpRepository if (string.IsNullOrEmpty(conversationId)) return null; var filterById = Builders.Filter.Eq(x => x.Id, Guid.Parse(conversationId)); - var found = _dc.Conversations.Find(filterById).FirstOrDefault(); + var filterDialog = Builders.Filter.Eq(x => x.ConversationId, Guid.Parse(conversationId)); + var filterStates = Builders.Filter.Eq(x => x.ConversationId, Guid.Parse(conversationId)); - return found != null ? new ConversationRecord + var conv = _dc.Conversations.Find(filterById).FirstOrDefault(); + var dialog = _dc.ConversationDialogs.Find(filterDialog).FirstOrDefault(); + var states = _dc.ConversationStates.Find(filterStates).FirstOrDefault(); + + if (conv == null) return null; + + return new ConversationRecord { - Id = found.Id.ToString(), - AgentId = found.AgentId.ToString(), - UserId = found.UserId.ToString(), - Title = found.Title, - Dialog = found.Dialog, - State = found.State, - CreatedTime = found.CreatedTime, - UpdatedTime = found.UpdatedTime - }: null; + Id = conv.Id.ToString(), + AgentId = conv.AgentId.ToString(), + UserId = conv.UserId.ToString(), + Title = conv.Title, + Dialog = dialog?.Dialog ?? string.Empty, + State = states?.State ?? new List(), + CreatedTime = conv.CreatedTime, + UpdatedTime = conv.UpdatedTime + }; } public List GetConversations(string userId) { - if (string.IsNullOrEmpty(userId)) return new List(); + var records = new List(); + if (string.IsNullOrEmpty(userId)) return records; var filterByUserId = Builders.Filter.Eq(x => x.UserId, Guid.Parse(userId)); var conversations = _dc.Conversations.Find(filterByUserId).ToList(); - return conversations.Select(x => new ConversationRecord + + foreach (var conv in conversations) { - Id = x.Id.ToString(), - AgentId = x.AgentId.ToString(), - UserId = x.UserId.ToString(), - Title = x.Title, - Dialog = x.Dialog, - State = x.State, - CreatedTime = x.CreatedTime, - UpdatedTime = x.UpdatedTime - }).ToList(); + var convId = conv.Id.ToString(); + var dialog = GetConversationDialog(convId); + var states = GetConversationState(convId); + records.Add(new ConversationRecord + { + Id = convId, + AgentId = conv.AgentId.ToString(), + UserId = conv.UserId.ToString(), + Title = conv.Title, + Dialog = dialog, + State = states, + CreatedTime = conv.CreatedTime, + UpdatedTime = conv.UpdatedTime + }); + } + + return records; } }