From 3c7c3c183b42df1400cfbb30575ce71cce9cd6a4 Mon Sep 17 00:00:00 2001 From: haiping008 Date: Fri, 7 Dec 2018 16:57:26 -0600 Subject: [PATCH] add ContextStorageFactory persist context to file --- .gitignore | 1 + .../AgentStorage/AgentStorageFactory.cs | 2 +- .../AgentStorage/AgentStorageInFile.cs | 5 +-- .../ContextStorage/ContextStorageFactory.cs | 28 +++++++++++++ .../ContextStorage/ContextStorageInFile.cs | 41 ++++++++++++++++++- .../ContextStorageServiceRegister.cs | 35 ++++++++++++++++ BotSharp.Core/PlatformBuilderBase.cs | 19 +++------ BotSharp.Core/PlatformSettingsBase.cs | 3 ++ .../IAgentStorageFactory.cs | 2 +- .../IContextStorage.cs | 4 +- .../IContextStorageFactory.cs | 11 +++++ .../IPlatformBuilder.cs | 2 +- .../IPlatformSettings.cs | 2 + BotSharp.Platform.Dialogflow/DialogflowAi.cs | 12 ++++-- .../ModuleInjector.cs | 2 + BotSharp.WebHost/Settings/DialogflowAi.json | 3 +- BotSharp.WebHost/Settings/db.json | 2 +- 17 files changed, 146 insertions(+), 28 deletions(-) create mode 100644 BotSharp.Core/ContextStorage/ContextStorageFactory.cs create mode 100644 BotSharp.Core/ContextStorage/ContextStorageServiceRegister.cs create mode 100644 BotSharp.Platform.Abstraction/IContextStorageFactory.cs diff --git a/.gitignore b/.gitignore index f0892edc..c2795885 100644 --- a/.gitignore +++ b/.gitignore @@ -291,3 +291,4 @@ __pycache__/ /docs/_build *.RestApi.xml /BotSharp.WebHost/App_Data/AgentStorage +/BotSharp.WebHost/App_Data/SessionStorage diff --git a/BotSharp.Core/AgentStorage/AgentStorageFactory.cs b/BotSharp.Core/AgentStorage/AgentStorageFactory.cs index aaaf3976..47abaa59 100644 --- a/BotSharp.Core/AgentStorage/AgentStorageFactory.cs +++ b/BotSharp.Core/AgentStorage/AgentStorageFactory.cs @@ -19,7 +19,7 @@ namespace BotSharp.Core.AgentStorage this.platformSetting = setting; } - public async Task> Get() + public IAgentStorage Get() { IAgentStorage storage = null; string storageName = this.platformSetting.AgentStorage; diff --git a/BotSharp.Core/AgentStorage/AgentStorageInFile.cs b/BotSharp.Core/AgentStorage/AgentStorageInFile.cs index f937bb37..90ba5b41 100644 --- a/BotSharp.Core/AgentStorage/AgentStorageInFile.cs +++ b/BotSharp.Core/AgentStorage/AgentStorageInFile.cs @@ -16,9 +16,6 @@ namespace BotSharp.Core.AgentStorage public class AgentStorageInFile : IAgentStorage where TAgent : AgentBase { - private static CSRedisClient csredis; - private static string prefix = String.Empty; - private static string storageDir; public AgentStorageInFile() @@ -27,7 +24,7 @@ namespace BotSharp.Core.AgentStorage var db = config.GetSection("Database:Default").Value; storageDir = config.GetSection($"Database:ConnectionStrings:{db}").Value; string contentDir = AppDomain.CurrentDomain.GetData("DataPath").ToString(); - storageDir = storageDir.Replace("|DataDirectory|", contentDir + Path.DirectorySeparatorChar); + storageDir = storageDir.Replace("|DataDirectory|", contentDir + Path.DirectorySeparatorChar + "AgentStorage" + Path.DirectorySeparatorChar); if (!Directory.Exists(storageDir)) { diff --git a/BotSharp.Core/ContextStorage/ContextStorageFactory.cs b/BotSharp.Core/ContextStorage/ContextStorageFactory.cs new file mode 100644 index 00000000..2bc8bc3d --- /dev/null +++ b/BotSharp.Core/ContextStorage/ContextStorageFactory.cs @@ -0,0 +1,28 @@ +using BotSharp.Platform.Abstraction; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Core.ContextStorage +{ + public class ContextStorageFactory : IContextStorageFactory + { + private readonly Func> func; + private readonly IPlatformSettings platformSetting; + + public ContextStorageFactory(IPlatformSettings setting, Func> serviceAccessor) + { + this.func = serviceAccessor; + this.platformSetting = setting; + } + + public IContextStorage Get() + { + IContextStorage storage = null; + string storageName = this.platformSetting.ContextStorage; + storage = func(storageName); + return storage as IContextStorage; + } + } +} diff --git a/BotSharp.Core/ContextStorage/ContextStorageInFile.cs b/BotSharp.Core/ContextStorage/ContextStorageInFile.cs index aa1c25f3..3647456c 100644 --- a/BotSharp.Core/ContextStorage/ContextStorageInFile.cs +++ b/BotSharp.Core/ContextStorage/ContextStorageInFile.cs @@ -1,12 +1,51 @@ using BotSharp.Platform.Abstraction; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; +using System.IO; using System.Text; +using System.Threading.Tasks; namespace BotSharp.Core.ContextStorage { - public class ContextStorageInFile : IContextStorage + public class ContextStorageInFile : IContextStorage { + private static string storageDir; + public ContextStorageInFile() + { + IConfiguration config = (IConfiguration)AppDomain.CurrentDomain.GetData("Configuration"); + var db = config.GetSection("Database:Default").Value; + storageDir = config.GetSection($"Database:ConnectionStrings:{db}").Value; + string contentDir = AppDomain.CurrentDomain.GetData("DataPath").ToString(); + storageDir = storageDir.Replace("|DataDirectory|", contentDir + Path.DirectorySeparatorChar + "SessionStorage" + Path.DirectorySeparatorChar); + + if (!Directory.Exists(storageDir)) + { + Directory.CreateDirectory(storageDir); + } + } + + public async Task Persist(string sessionId, T[] context) + { + var json = JsonConvert.SerializeObject(context, new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + Formatting = Formatting.Indented, + ContractResolver = new CamelCasePropertyNamesContractResolver(), + Converters = new List + { + new Newtonsoft.Json.Converters.StringEnumConverter() + } + }); + + string dataPath = Path.Combine(storageDir, sessionId + ".json"); + + File.WriteAllText(dataPath, json); + + return true; + } } } diff --git a/BotSharp.Core/ContextStorage/ContextStorageServiceRegister.cs b/BotSharp.Core/ContextStorage/ContextStorageServiceRegister.cs new file mode 100644 index 00000000..61ea90ce --- /dev/null +++ b/BotSharp.Core/ContextStorage/ContextStorageServiceRegister.cs @@ -0,0 +1,35 @@ +using BotSharp.Platform.Abstraction; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BotSharp.Core.ContextStorage +{ + public class ContextStorageServiceRegister + { + public static void Register(IServiceCollection services) + { + services.AddSingleton, ContextStorageFactory>(); + + services.AddSingleton>(); + + services.AddSingleton(factory => + { + Func> accesor = key => + { + if (key.Equals("ContextStorageInFile")) + { + return factory.GetService>(); + } + else + { + throw new ArgumentException($"Not Support key : {key}"); + } + }; + + return accesor; + }); + } + } +} diff --git a/BotSharp.Core/PlatformBuilderBase.cs b/BotSharp.Core/PlatformBuilderBase.cs index 9a4bf6f5..903f4311 100644 --- a/BotSharp.Core/PlatformBuilderBase.cs +++ b/BotSharp.Core/PlatformBuilderBase.cs @@ -32,12 +32,11 @@ namespace BotSharp.Core { this.agentStorageFactory = agentStorageFactory; this.settings = settings; + GetAgentStorage(); } public async Task> GetAllAgents() { - await GetStorage(); - return await Storage.Query(); } @@ -80,15 +79,11 @@ namespace BotSharp.Core public async Task GetAgentById(string agentId) { - GetStorage(); - return await Storage.FetchById(agentId); } public async Task GetAgentByName(string agentName) { - await GetStorage(); - return await Storage.FetchByName(agentName); } @@ -195,7 +190,7 @@ namespace BotSharp.Core Console.WriteLine($"TextResponse: {aiResponse.Intent}, {request.SessionId}"); - return await AssembleResult(aiResponse); + return await AssembleResult(request, aiResponse); } public virtual async Task FallbackResponse(AiRequest request) @@ -222,27 +217,25 @@ namespace BotSharp.Core } } - public virtual async Task AssembleResult(AiResponse response) + public virtual async Task AssembleResult(AiRequest request, AiResponse response) { throw new NotImplementedException(); } public virtual async Task SaveAgent(TAgent agent) { - await GetStorage(); - // default save agent in FileStorage await Storage.Persist(agent); return true; } - - protected async Task> GetStorage() + protected IAgentStorage GetAgentStorage() { if (Storage == null) { - Storage = await agentStorageFactory.Get(); + Storage = agentStorageFactory.Get(); } + return Storage; } } diff --git a/BotSharp.Core/PlatformSettingsBase.cs b/BotSharp.Core/PlatformSettingsBase.cs index 5eeebd92..2d7ceb32 100644 --- a/BotSharp.Core/PlatformSettingsBase.cs +++ b/BotSharp.Core/PlatformSettingsBase.cs @@ -16,10 +16,13 @@ namespace BotSharp.Core { BotEngine = "BotSharpNLU"; AgentStorage = "AgentStorageInFile"; + ContextStorage = "ContextStorageInFile"; } public string BotEngine { get; set; } + public string ContextStorage { get; set; } + public string AgentStorage { get; set; } } diff --git a/BotSharp.Platform.Abstraction/IAgentStorageFactory.cs b/BotSharp.Platform.Abstraction/IAgentStorageFactory.cs index 9f5a2bfb..d8995a58 100644 --- a/BotSharp.Platform.Abstraction/IAgentStorageFactory.cs +++ b/BotSharp.Platform.Abstraction/IAgentStorageFactory.cs @@ -6,6 +6,6 @@ namespace BotSharp.Platform.Abstraction { public interface IAgentStorageFactory where TAgent : AgentBase { - Task> Get(); + IAgentStorage Get(); } } diff --git a/BotSharp.Platform.Abstraction/IContextStorage.cs b/BotSharp.Platform.Abstraction/IContextStorage.cs index 9b999f08..71ebe592 100644 --- a/BotSharp.Platform.Abstraction/IContextStorage.cs +++ b/BotSharp.Platform.Abstraction/IContextStorage.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; namespace BotSharp.Platform.Abstraction { - public interface IContextStorage + public interface IContextStorage { + Task Persist(string sessionId, T[] context); } } diff --git a/BotSharp.Platform.Abstraction/IContextStorageFactory.cs b/BotSharp.Platform.Abstraction/IContextStorageFactory.cs new file mode 100644 index 00000000..00b12f12 --- /dev/null +++ b/BotSharp.Platform.Abstraction/IContextStorageFactory.cs @@ -0,0 +1,11 @@ +using BotSharp.Platform.Abstraction; +using BotSharp.Platform.Models; +using System.Threading.Tasks; + +namespace BotSharp.Platform.Abstraction +{ + public interface IContextStorageFactory + { + IContextStorage Get(); + } +} diff --git a/BotSharp.Platform.Abstraction/IPlatformBuilder.cs b/BotSharp.Platform.Abstraction/IPlatformBuilder.cs index 10fe73c0..9d59cba3 100644 --- a/BotSharp.Platform.Abstraction/IPlatformBuilder.cs +++ b/BotSharp.Platform.Abstraction/IPlatformBuilder.cs @@ -51,7 +51,7 @@ namespace BotSharp.Platform.Abstraction Task TextRequest(AiRequest request); - Task AssembleResult(AiResponse response); + Task AssembleResult(AiRequest request, AiResponse response); Task FallbackResponse(AiRequest request); } diff --git a/BotSharp.Platform.Abstraction/IPlatformSettings.cs b/BotSharp.Platform.Abstraction/IPlatformSettings.cs index dafe4fbb..bf476835 100644 --- a/BotSharp.Platform.Abstraction/IPlatformSettings.cs +++ b/BotSharp.Platform.Abstraction/IPlatformSettings.cs @@ -9,5 +9,7 @@ namespace BotSharp.Platform.Abstraction string BotEngine { get; set; } string AgentStorage { get; set; } + + string ContextStorage { get; set; } } } diff --git a/BotSharp.Platform.Dialogflow/DialogflowAi.cs b/BotSharp.Platform.Dialogflow/DialogflowAi.cs index 71c4bf5a..2271828e 100644 --- a/BotSharp.Platform.Dialogflow/DialogflowAi.cs +++ b/BotSharp.Platform.Dialogflow/DialogflowAi.cs @@ -24,11 +24,13 @@ namespace BotSharp.Platform.Dialogflow where TAgent : AgentModel { IConfiguration config; + IContextStorageFactory contextStorageFactory; - public DialogflowAi(IAgentStorageFactory agentStorageFactory, IPlatformSettings settings, IConfiguration config) + public DialogflowAi(IAgentStorageFactory agentStorageFactory, IContextStorageFactory contextStorageFactory, IPlatformSettings settings, IConfiguration config) :base(agentStorageFactory, settings) { this.config = config; + this.contextStorageFactory = contextStorageFactory; } public async Task ExtractorCorpus(TAgent agent) @@ -110,7 +112,7 @@ namespace BotSharp.Platform.Dialogflow } } - public override async Task AssembleResult(AiResponse response) + public override async Task AssembleResult(AiRequest request, AiResponse response) { var intent = Agent.Intents.Find(x => x.Name == response.Intent); var presetResponse = intent.Responses.FirstOrDefault(); @@ -135,7 +137,7 @@ namespace BotSharp.Platform.Dialogflow var matches = Regex.Matches(presetResponse.Messages.Random().Speech, "\".*?\"").Cast(); var speech = matches.Count() == 0 ? String.Empty : matches.ToList().Random().Value; - var contexts = HandleContexts(presetResponse); + var contexts = HandleContexts(request.SessionId, presetResponse); var aiResponse = new AIResponseResult { @@ -160,7 +162,7 @@ namespace BotSharp.Platform.Dialogflow return (TResult)(object)aiResponse; } - private List HandleContexts(IntentResponse response) + private List HandleContexts(string sessionId, IntentResponse response) { var newContexts = response.Contexts.Select(x => new AIContext { @@ -170,6 +172,8 @@ namespace BotSharp.Platform.Dialogflow }).ToList(); // persist + var ctxStore = contextStorageFactory.Get(); + ctxStore.Persist(sessionId, newContexts.ToArray()); return newContexts; } diff --git a/BotSharp.Platform.Dialogflow/ModuleInjector.cs b/BotSharp.Platform.Dialogflow/ModuleInjector.cs index f6fb01bb..7c236750 100644 --- a/BotSharp.Platform.Dialogflow/ModuleInjector.cs +++ b/BotSharp.Platform.Dialogflow/ModuleInjector.cs @@ -1,5 +1,6 @@ using BotSharp.Core; using BotSharp.Core.AgentStorage; +using BotSharp.Core.ContextStorage; using BotSharp.Core.Modules; using BotSharp.Platform.Abstraction; using BotSharp.Platform.Dialogflow.Models; @@ -21,6 +22,7 @@ namespace BotSharp.Platform.Dialogflow services.AddSingleton>(); AgentStorageServiceRegister.Register(services); PlatformConfigServiceRegister.Register("dialogflowAi", services, config); + ContextStorageServiceRegister.Register(services); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) diff --git a/BotSharp.WebHost/Settings/DialogflowAi.json b/BotSharp.WebHost/Settings/DialogflowAi.json index 062c51ce..9409b827 100644 --- a/BotSharp.WebHost/Settings/DialogflowAi.json +++ b/BotSharp.WebHost/Settings/DialogflowAi.json @@ -2,6 +2,7 @@ // if you want to override platform setting, please set corresponding value, otherwise you don't need this section. "dialogflowAi": { "botEngine": "BotSharpNLU", - "agentStorage": "AgentStorageInFile" + "agentStorage": "AgentStorageInFile", + "contextStorage": "ContextStorageInFile" } } diff --git a/BotSharp.WebHost/Settings/db.json b/BotSharp.WebHost/Settings/db.json index 4482fb34..4bdaccab 100644 --- a/BotSharp.WebHost/Settings/db.json +++ b/BotSharp.WebHost/Settings/db.json @@ -6,7 +6,7 @@ "Redis": "127.0.0.1:6379,defaultDatabase=BotSharp,poolsize=50,ssl=false,writeBuffer=10240,prefix=agent_", "Sqlite": "Data Source=|DataDirectory|BotSharp.db;", "SqlServer": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=BotSharp;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", - "File": "|DataDirectory|AgentStorage" + "File": "|DataDirectory|" } } } \ No newline at end of file