From 010c92db1a1335945896ea1ae37732debf6556e9 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Sun, 31 Dec 2023 22:56:10 -0600 Subject: [PATCH] refine conversation states --- .../IConversationStateService.cs | 2 +- .../Models/ConversationHistoryState.cs | 22 ++++++ .../Models/HistoryStateKeyValue.cs | 30 +++++++ .../Repositories/IBotSharpRepository.cs | 4 +- .../Services/ConversationService.cs | 2 +- .../Services/ConversationStateService.cs | 78 ++++++++++++++----- .../Repository/BotSharpDbContext.cs | 4 +- .../Repository/FileRepository.cs | 53 ++++++------- .../Controllers/ConversationController.cs | 10 +-- .../Controllers/InstructModeController.cs | 8 +- .../ChatbotUiController.cs | 10 +-- .../Collections/ConversationDocument.cs | 3 - .../Collections/ConversationStateDocument.cs | 9 +++ .../Collections/LlmCompletionLogDocument.cs | 2 +- ...LogElement.cs => PromptLogMongoElement.cs} | 2 +- .../Models/StateMongoElement.cs | 54 +++++++++++++ .../MongoDbContext.cs | 3 + .../Repository/MongoRepository.cs | 71 +++++++++++------ 18 files changed, 272 insertions(+), 95 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationHistoryState.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Conversations/Models/HistoryStateKeyValue.cs create mode 100644 src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStateDocument.cs rename src/Plugins/BotSharp.Plugin.MongoStorage/Models/{PromptLogElement.cs => PromptLogMongoElement.cs} (88%) create mode 100644 src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs index eeb39f73..65570dc4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs @@ -12,7 +12,7 @@ public interface IConversationStateService string GetState(string name, string defaultValue = ""); bool ContainsState(string name); ConversationState GetStates(); - IConversationStateService SetState(string name, T value); + IConversationStateService SetState(string name, T value, bool isConst = false); void SaveStateByArgs(JsonDocument args); void CleanState(); void Save(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationHistoryState.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationHistoryState.cs new file mode 100644 index 00000000..224fe735 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationHistoryState.cs @@ -0,0 +1,22 @@ +namespace BotSharp.Abstraction.Conversations.Models; + +public class ConversationHistoryState : Dictionary> +{ + public ConversationHistoryState() + { + + } + + public ConversationHistoryState(List pairs) + { + foreach (var pair in pairs) + { + this[pair.Key] = pair.Values; + } + } + + //public List ToKeyValueList() + //{ + // return this.Select(x => new StateKeyValue(x.Key, x.Value)).ToList(); + //} +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/HistoryStateKeyValue.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/HistoryStateKeyValue.cs new file mode 100644 index 00000000..2134f9e8 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/HistoryStateKeyValue.cs @@ -0,0 +1,30 @@ +namespace BotSharp.Abstraction.Conversations.Models; + +public class HistoryStateKeyValue +{ + public string Key { get; set; } + public List Values { get; set; } = new List(); + + public HistoryStateKeyValue() + { + + } + + public HistoryStateKeyValue(string key, List values) + { + Key = key; + Values = values; + } +} + +public class HistoryStateValue +{ + public string? MessageId { get; set; } + public string Data { get; set; } + public DateTime UpdateTime { get; set; } + + public HistoryStateValue() + { + + } +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 1834dfd5..860de96b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -33,8 +33,8 @@ public interface IBotSharpRepository List GetConversationDialogs(string conversationId); void UpdateConversationDialogElements(string conversationId, List updateElements); void AppendConversationDialogs(string conversationId, List dialogs); - List GetConversationStates(string conversationId); - void UpdateConversationStates(string conversationId, List states); + List GetConversationStates(string conversationId); + void UpdateConversationStates(string conversationId, List states); void UpdateConversationStatus(string conversationId, string status); Conversation GetConversation(string conversationId); List GetConversations(ConversationFilter filter); diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index d6a15072..a39af217 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -120,6 +120,6 @@ public partial class ConversationService : IConversationService { _conversationId = conversationId; _state.Load(_conversationId); - states.ForEach(x => _state.SetState(x.Split('=')[0], x.Split('=')[1])); + states.ForEach(x => _state.SetState(x.Split('=')[0], x.Split('=')[1], true)); } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 23e589b1..1b560f43 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Repositories; -using System.IO; namespace BotSharp.Core.Conversations.Services; @@ -11,26 +10,33 @@ public class ConversationStateService : IConversationStateService, IDisposable private readonly ILogger _logger; private readonly IServiceProvider _services; private ConversationState _states; - private BotSharpDatabaseSettings _dbSettings; + private ConversationHistoryState _historyStates; private string _conversationId; private readonly IBotSharpRepository _db; - private List _savedStates; public ConversationStateService(ILogger logger, - IServiceProvider services, - BotSharpDatabaseSettings dbSettings, + IServiceProvider services, IBotSharpRepository db) { _logger = logger; _services = services; - _dbSettings = dbSettings; _db = db; _states = new ConversationState(); + _historyStates = new ConversationHistoryState(); } public string GetConversationId() => _conversationId; - public IConversationStateService SetState(string name, T value) + + /// + /// Set conversation state + /// + /// + /// + /// + /// whether the state is related to message or not + /// + public IConversationStateService SetState(string name, T value, bool isConst = false) { if (value == null) { @@ -40,6 +46,7 @@ public class ConversationStateService : IConversationStateService, IDisposable var currentValue = value.ToString(); var hooks = _services.GetServices(); string preValue = _states.ContainsKey(name) ? _states[name] : ""; + if (!_states.ContainsKey(name) || _states[name] != currentValue) { _states[name] = currentValue; @@ -48,6 +55,28 @@ public class ConversationStateService : IConversationStateService, IDisposable { hook.OnStateChanged(name, preValue, currentValue).Wait(); } + + var historyStateValue = new HistoryStateValue + { + Data = currentValue, + UpdateTime = DateTime.UtcNow + }; + + if (!_historyStates.ContainsKey(name)) + { + _historyStates[name] = new List(); + } + + if (isConst) + { + _historyStates[name] = new List { historyStateValue }; + } + else + { + var messageId = GetCurrentMessageId(); + historyStateValue.MessageId = messageId ?? string.Empty; + _historyStates[name].Add(historyStateValue); + } } return this; @@ -57,14 +86,16 @@ public class ConversationStateService : IConversationStateService, IDisposable { _conversationId = conversationId; - _savedStates = _db.GetConversationStates(_conversationId).ToList(); + var savedStates = _db.GetConversationStates(_conversationId).ToList(); + _historyStates = new ConversationHistoryState(savedStates); - if (!_savedStates.IsNullOrEmpty()) + if (!savedStates.IsNullOrEmpty()) { - foreach (var data in _savedStates) + foreach (var state in savedStates) { - _states[data.Key] = data.Value; - _logger.LogInformation($"[STATE] {data.Key} : {data.Value}"); + var value = state.Values.LastOrDefault()?.Data ?? string.Empty; + _states[state.Key] = value; + _logger.LogInformation($"[STATE] {state.Key} : {value}"); } } @@ -85,24 +116,23 @@ public class ConversationStateService : IConversationStateService, IDisposable return; } - var states = new List(); + var historyStates = new List(); - foreach (var dic in _states) + foreach (var dic in _historyStates) { - states.Add(new StateKeyValue(dic.Key, dic.Value)); + historyStates.Add(new HistoryStateKeyValue(dic.Key, dic.Value)); } - _db.UpdateConversationStates(_conversationId, states); - _logger.LogInformation($"Saved state {_conversationId}"); + _db.UpdateConversationStates(_conversationId, historyStates); + _logger.LogInformation($"Saved states of conversation {_conversationId}"); } public void CleanState() { - //File.Delete(_file); + } - public ConversationState GetStates() - => _states; + public ConversationState GetStates() => _states; public string GetState(string name, string defaultValue = "") { @@ -142,4 +172,12 @@ public class ConversationStateService : IConversationStateService, IDisposable } } } + + private string? GetCurrentMessageId() + { + if (string.IsNullOrEmpty(_conversationId)) return null; + + var dialogs = _db.GetConversationDialogs(_conversationId); + return dialogs.LastOrDefault()?.MetaData?.MessageId; + } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index ec8ac5f6..901440d5 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -152,7 +152,7 @@ public class BotSharpDbContext : Database, IBotSharpRepository throw new NotImplementedException(); } - public List GetConversationStates(string conversationId) + public List GetConversationStates(string conversationId) { throw new NotImplementedException(); } @@ -165,7 +165,7 @@ public class BotSharpDbContext : Database, IBotSharpRepository { throw new NotImplementedException(); } - public void UpdateConversationStates(string conversationId, List states) + public void UpdateConversationStates(string conversationId, List states) { throw new NotImplementedException(); } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs index 0df9ed98..34e0c8b0 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs @@ -644,7 +644,7 @@ public class FileRepository : IBotSharpRepository var stateDir = Path.Combine(dir, "state.dict"); if (!File.Exists(stateDir)) { - File.WriteAllText(stateDir, string.Empty); + File.WriteAllText(stateDir, "[]"); } } @@ -730,33 +730,31 @@ public class FileRepository : IBotSharpRepository } } } - public List GetConversationStates(string conversationId) + public List GetConversationStates(string conversationId) { - var curStates = new List(); + var curStates = new List(); var convDir = FindConversationDirectory(conversationId); if (!string.IsNullOrEmpty(convDir)) { - var stateDir = Path.Combine(convDir, "state.dict"); - curStates = CollectConversationStates(stateDir); + var stateFile = Path.Combine(convDir, "state.dict"); + curStates = CollectConversationStates(stateFile); } return curStates; } - public void UpdateConversationStates(string conversationId, List states) + public void UpdateConversationStates(string conversationId, List states) { - var localStates = new List(); + if (states.IsNullOrEmpty()) return; + var convDir = FindConversationDirectory(conversationId); if (!string.IsNullOrEmpty(convDir)) { - var stateDir = Path.Combine(convDir, "state.dict"); - if (File.Exists(stateDir)) + var stateFile = Path.Combine(convDir, "state.dict"); + if (File.Exists(stateFile)) { - foreach (var data in states) - { - localStates.Add($"{data.Key}={data.Value}"); - } - File.WriteAllLines(stateDir, localStates); + var stateStr = JsonSerializer.Serialize(states, _options); + File.WriteAllText(stateFile, stateStr); } } } @@ -796,8 +794,13 @@ public class FileRepository : IBotSharpRepository var stateFile = Path.Combine(convDir, "state.dict"); if (record != null) { - var states = CollectConversationStates(stateFile); - record.States = new ConversationState(states); + var historyStates = CollectConversationStates(stateFile); + var recentStates = historyStates.Select(x => new StateKeyValue + { + Key = x.Key, + Value = x.Values.LastOrDefault()?.Data ?? string.Empty + }).ToList(); + record.States = new ConversationState(recentStates); } return record; @@ -1082,18 +1085,16 @@ public class FileRepository : IBotSharpRepository return dialogTexts; } - private List CollectConversationStates(string stateDir) + private List CollectConversationStates(string stateFile) { - var states = new List(); - if (!File.Exists(stateDir)) return states; + var states = new List(); + if (!File.Exists(stateFile)) return states; - var dict = File.ReadAllLines(stateDir); - foreach (var line in dict) - { - var data = line.Split('='); - states.Add(new StateKeyValue(data[0], data[1])); - } - return states; + var stateStr = File.ReadAllText(stateFile); + if (string.IsNullOrEmpty(stateStr)) return states; + + states = JsonSerializer.Deserialize>(stateStr, _options); + return states ?? new List(); } private int GetNextLlmCompletionLogIndex(string logDir, string id) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 505f794f..4d5fed54 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -117,11 +117,11 @@ public class ConversationController : ControllerBase { var conv = _services.GetRequiredService(); conv.SetConversationId(conversationId, input.States); - conv.States.SetState("channel", input.Channel) - .SetState("provider", input.Provider) - .SetState("model", input.Model) - .SetState("temperature", input.Temperature) - .SetState("sampling_factor", input.SamplingFactor); + conv.States.SetState("channel", input.Channel, true) + .SetState("provider", input.Provider, true) + .SetState("model", input.Model, true) + .SetState("temperature", input.Temperature, true) + .SetState("sampling_factor", input.SamplingFactor, true); var response = new ChatResponseModel(); var inputMsg = new RoleDialogModel(AgentRole.User, input.Text); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index 750719b8..5bd8e1c2 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -22,10 +22,10 @@ public class InstructModeController : ControllerBase [FromBody] InstructMessageModel input) { var state = _services.GetRequiredService(); - input.States.ForEach(x => state.SetState(x.Split('=')[0], x.Split('=')[1])); - state.SetState("provider", input.Provider) - .SetState("model", input.Model) - .SetState("instruction", input.Instruction) + input.States.ForEach(x => state.SetState(x.Split('=')[0], x.Split('=')[1], true)); + state.SetState("provider", input.Provider, true) + .SetState("model", input.Model, true) + .SetState("instruction", input.Instruction, true) .SetState("input_text", input.Text); var instructor = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.ChatbotUI/ChatbotUiController.cs b/src/Plugins/BotSharp.Plugin.ChatbotUI/ChatbotUiController.cs index 9c422a7a..b6f3b0b2 100644 --- a/src/Plugins/BotSharp.Plugin.ChatbotUI/ChatbotUiController.cs +++ b/src/Plugins/BotSharp.Plugin.ChatbotUI/ChatbotUiController.cs @@ -75,11 +75,11 @@ public class ChatbotUiController : ControllerBase var conv = _services.GetRequiredService(); conv.SetConversationId(input.ConversationId, input.States); - conv.States.SetState("channel", input.Channel) - .SetState("provider", input.Provider) - .SetState("model", input.Model) - .SetState("temperature", input.Temperature) - .SetState("sampling_factor", input.SamplingFactor); + conv.States.SetState("channel", input.Channel, true) + .SetState("provider", input.Provider, true) + .SetState("model", input.Model, true) + .SetState("temperature", input.Temperature, true) + .SetState("sampling_factor", input.SamplingFactor, true); var result = await conv.SendMessage(input.AgentId, message, diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs index 4606974d..877d1c96 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs @@ -1,5 +1,3 @@ -using BotSharp.Abstraction.Conversations.Models; - namespace BotSharp.Plugin.MongoStorage.Collections; public class ConversationDocument : MongoBase @@ -9,7 +7,6 @@ public class ConversationDocument : MongoBase public string Title { get; set; } public string Channel { get; set; } public string Status { get; set; } - public List States { get; set; } public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStateDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStateDocument.cs new file mode 100644 index 00000000..8f5edfa5 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationStateDocument.cs @@ -0,0 +1,9 @@ +using BotSharp.Plugin.MongoStorage.Models; + +namespace BotSharp.Plugin.MongoStorage.Collections; + +public class ConversationStateDocument : MongoBase +{ + public string ConversationId { get; set; } + public List States { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/LlmCompletionLogDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/LlmCompletionLogDocument.cs index e90714cb..f04cb46c 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/LlmCompletionLogDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/LlmCompletionLogDocument.cs @@ -5,5 +5,5 @@ namespace BotSharp.Plugin.MongoStorage.Collections; public class LlmCompletionLogDocument : MongoBase { public string ConversationId { get; set; } - public List Logs { get; set; } + public List Logs { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/PromptLogElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/PromptLogMongoElement.cs similarity index 88% rename from src/Plugins/BotSharp.Plugin.MongoStorage/Models/PromptLogElement.cs rename to src/Plugins/BotSharp.Plugin.MongoStorage/Models/PromptLogMongoElement.cs index 91c5e4d1..ced9030d 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/PromptLogElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/PromptLogMongoElement.cs @@ -1,6 +1,6 @@ namespace BotSharp.Plugin.MongoStorage.Models; -public class PromptLogElement +public class PromptLogMongoElement { public string MessageId { get; set; } public string AgentId { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs new file mode 100644 index 00000000..4053d2e7 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/StateMongoElement.cs @@ -0,0 +1,54 @@ +using BotSharp.Abstraction.Conversations.Models; + +namespace BotSharp.Plugin.MongoStorage.Models; + +public class StateMongoElement +{ + public string Key { get; set; } + public List Values { get; set; } + + public static StateMongoElement ToMongoElement(HistoryStateKeyValue state) + { + return new StateMongoElement + { + Key = state.Key, + Values = state.Values?.Select(x => StateValueMongoElement.ToMongoElement(x))?.ToList() ?? new List() + }; + } + + public static HistoryStateKeyValue ToDomainElement(StateMongoElement state) + { + return new HistoryStateKeyValue + { + Key = state.Key, + Values = state.Values?.Select(x => StateValueMongoElement.ToDomainElement(x))?.ToList() ?? new List() + }; + } +} + +public class StateValueMongoElement +{ + public string? MessageId { get; set; } + public string Data { get; set; } + public DateTime UpdateTime { get; set; } + + public static StateValueMongoElement ToMongoElement(HistoryStateValue element) + { + return new StateValueMongoElement + { + MessageId = element.MessageId, + Data = element.Data, + UpdateTime = element.UpdateTime + }; + } + + public static HistoryStateValue ToDomainElement(StateValueMongoElement element) + { + return new HistoryStateValue + { + MessageId = element.MessageId, + Data = element.Data, + UpdateTime = element.UpdateTime + }; + } +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs index 11a1af33..1f5cbb92 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs @@ -37,6 +37,9 @@ public class MongoDbContext public IMongoCollection ConversationDialogs => Database.GetCollection($"{_collectionPrefix}_ConversationDialogs"); + public IMongoCollection ConversationStates + => Database.GetCollection($"{_collectionPrefix}_ConversationStates"); + public IMongoCollection ExectionLogs => Database.GetCollection($"{_collectionPrefix}_ExecutionLogs"); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs index 4b94911d..005f2830 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs @@ -447,7 +447,7 @@ public class MongoRepository : IBotSharpRepository { query = query.Where(x => x.Name.ToLower() == filter.AgentName.ToLower()); } - + if (filter.Disabled.HasValue) { query = query.Where(x => x.Disabled == filter.Disabled); @@ -514,9 +514,9 @@ public class MongoRepository : IBotSharpRepository public List GetAgentsByUser(string userId) { var agentIds = (from ua in _dc.UserAgents.AsQueryable() - join u in _dc.Users.AsQueryable() on ua.UserId equals u.Id - where ua.UserId == userId || u.ExternalId == userId - select ua.AgentId).ToList(); + join u in _dc.Users.AsQueryable() on ua.UserId equals u.Id + where ua.UserId == userId || u.ExternalId == userId + select ua.AgentId).ToList(); var filter = new AgentFilter { @@ -608,7 +608,7 @@ public class MongoRepository : IBotSharpRepository { return false; } - + } #endregion @@ -625,7 +625,6 @@ public class MongoRepository : IBotSharpRepository Title = conversation.Title, Channel = conversation.Channel, Status = conversation.Status, - States = conversation.States?.ToKeyValueList() ?? new List(), CreatedTime = DateTime.UtcNow, UpdatedTime = DateTime.UtcNow, }; @@ -637,8 +636,16 @@ public class MongoRepository : IBotSharpRepository Dialogs = new List() }; + var states = new ConversationStateDocument + { + Id = Guid.NewGuid().ToString(), + ConversationId = conv.Id, + States = new List() + }; + _dc.Conversations.InsertOne(conv); _dc.ConversationDialogs.InsertOne(dialog); + _dc.ConversationStates.InsertOne(states); } public bool DeleteConversation(string conversationId) @@ -647,14 +654,16 @@ public class MongoRepository : IBotSharpRepository var filterConv = Builders.Filter.Eq(x => x.Id, conversationId); var filterDialog = Builders.Filter.Eq(x => x.ConversationId, conversationId); + var filterSates = Builders.Filter.Eq(x => x.ConversationId, conversationId); var filterExeLog = Builders.Filter.Eq(x => x.ConversationId, conversationId); var filterPromptLog = Builders.Filter.Eq(x => x.ConversationId, conversationId); var exeLogDeleted = _dc.ExectionLogs.DeleteMany(filterExeLog); var promptLogDeleted = _dc.LlmCompletionLogs.DeleteMany(filterPromptLog); + var statesDeleted = _dc.ConversationStates.DeleteMany(filterSates); var dialogDeleted = _dc.ConversationDialogs.DeleteMany(filterDialog); var convDeleted = _dc.Conversations.DeleteMany(filterConv); - return convDeleted.DeletedCount > 0 || dialogDeleted.DeletedCount > 0 + return convDeleted.DeletedCount > 0 || dialogDeleted.DeletedCount > 0 || statesDeleted.DeletedCount > 0 || exeLogDeleted.DeletedCount > 0 || promptLogDeleted.DeletedCount > 0; } @@ -688,7 +697,7 @@ public class MongoRepository : IBotSharpRepository } return x; }).ToList(); - + _dc.ConversationDialogs.ReplaceOne(filterDialog, foundDialog); } @@ -727,30 +736,37 @@ public class MongoRepository : IBotSharpRepository _dc.Conversations.UpdateOne(filterConv, updateConv); } - public List GetConversationStates(string conversationId) + public List GetConversationStates(string conversationId) { - var states = new List(); + var states = new List(); if (string.IsNullOrEmpty(conversationId)) return states; - var filter = Builders.Filter.Eq(x => x.Id, conversationId); - var foundConversation = _dc.Conversations.Find(filter).FirstOrDefault(); - var savedStates = foundConversation?.States ?? new List(); + var filter = Builders.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 savedStates; } - public void UpdateConversationStates(string conversationId, List states) + public void UpdateConversationStates(string conversationId, List states) { - if (string.IsNullOrEmpty(conversationId)) return; + if (string.IsNullOrEmpty(conversationId) || states.IsNullOrEmpty()) return; - var filter = Builders.Filter.Eq(x => x.Id, conversationId); - var foundConv = _dc.Conversations.Find(filter).FirstOrDefault(); + var filterConv = Builders.Filter.Eq(x => x.Id, conversationId); + var foundConv = _dc.Conversations.Find(filterConv).FirstOrDefault(); if (foundConv == null) return; - var update = Builders.Update - .Set(x => x.States, states) - .Set(x => x.UpdatedTime, DateTime.UtcNow); + var filterStates = Builders.Filter.Eq(x => x.ConversationId, conversationId); + var foundStates = _dc.ConversationStates.Find(filterStates).FirstOrDefault(); + if (foundStates == null) return; - _dc.Conversations.UpdateOne(filter, update); + var saveStates = states.Select(x => StateMongoElement.ToMongoElement(x)).ToList(); + var updateStates = Builders.Update.Set(x => x.States, saveStates); + var updateConv = Builders.Update.Set(x => x.UpdatedTime, DateTime.UtcNow); + + _dc.ConversationStates.UpdateOne(filterStates, updateStates); + _dc.Conversations.UpdateOne(filterConv, updateConv); } public void UpdateConversationStatus(string conversationId, string status) @@ -774,14 +790,21 @@ public class MongoRepository : IBotSharpRepository var filterConv = Builders.Filter.Eq(x => x.Id, conversationId); var filterDialog = Builders.Filter.Eq(x => x.ConversationId, conversationId); + var filterState = Builders.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(); - + var recentStates = states.States?.Select(x => new StateKeyValue + { + Key = x.Key, + Value = x.Values.LastOrDefault()?.Data ?? string.Empty + })?.ToList() ?? new List(); + return new Conversation { Id = conv.Id.ToString(), @@ -791,7 +814,7 @@ public class MongoRepository : IBotSharpRepository Channel = conv.Channel, Status = conv.Status, Dialogs = dialogElements, - States = new ConversationState(conv.States ?? new List()), + States = new ConversationState(recentStates), CreatedTime = conv.CreatedTime, UpdatedTime = conv.UpdatedTime }; @@ -938,7 +961,7 @@ public class MongoRepository : IBotSharpRepository var conversationId = log.ConversationId.IfNullOrEmptyAs(Guid.NewGuid().ToString()); var messageId = log.MessageId.IfNullOrEmptyAs(Guid.NewGuid().ToString()); - var logElement = new PromptLogElement + var logElement = new PromptLogMongoElement { MessageId = messageId, AgentId = log.AgentId,