diff --git a/BotSharp.Core/Agents/AgentDriver.cs b/BotSharp.Core/Agents/AgentDriver.cs index fd1faefd..5e18394c 100644 --- a/BotSharp.Core/Agents/AgentDriver.cs +++ b/BotSharp.Core/Agents/AgentDriver.cs @@ -137,94 +137,6 @@ namespace BotSharp.Core.Agents }); }); - return trainingData; - } - - public static RasaTrainingData GrabCorpusPerContexts(this Agent agent, Database dc, List ctx) - { - var trainingData = new RasaTrainingData - { - Entities = new List(), - UserSays = new List() - }; - - var expressParts = new List(); - - var intents = dc.Table() - .Include(x => x.Contexts) - .Include(x => x.UserSays).ThenInclude(say => say.Data) - .Where(x => x.UserSays.Count > 0) - .ToList(); - - var contexts = ctx.OrderBy(x => x.Name).Select(x => x.Name.ToLower()).ToList(); - - // search all potential intents which input context included in contexts - intents = intents.Where(it => - { - if (contexts.Count == 0) - { - return it.Contexts.Count() == 0; - } - else - { - return it.Contexts.Count() > 0 && it.Contexts.Count(x => contexts.Contains(x.Name.ToLower())) == it.Contexts.Count; - } - }).OrderByDescending(x => x.Contexts.Count).ToList(); - - intents.ForEach(intent => - { - intent.UserSays.ForEach(exp => - { - var say = new RasaIntentExpression - { - Intent = intent.Name, - Text = String.Join("", exp.Data.OrderBy(x => x.UpdatedTime).Select(x => x.Text)), - }; - - // convert entity format - exp.Data.Where(x => !String.IsNullOrEmpty(x.Meta)) - .ToList() - .ForEach(x => - { - int start = say.Text.IndexOf(x.Text); - - var part = new RasaIntentExpressionPart - { - Value = x.Text, - Entity = x.Alias, - Start = start, - End = start + x.Text.Length - }; - - if (say.Entities == null) say.Entities = new List(); - say.Entities.Add(part); - - // assemble entity synonmus - if (!trainingData.Entities.Any(y => y.EntityType == x.Alias && y.EntityValue == x.Text)) - { - var allSynonyms = (from e in dc.Table() - join ee in dc.Table() on e.Id equals ee.EntityId - join ees in dc.Table() on ee.Id equals ees.EntityEntryId - where e.Name == x.Alias && ee.Value == x.Text & ees.Synonym != x.Text - select ees.Synonym ).ToList(); - - var te = new RasaTraningEntity - { - EntityType = x.Alias, - EntityValue = x.Text, - Synonyms = allSynonyms - }; - - trainingData.Entities.Add(te); - } - }); - - trainingData.UserSays.Add(say); - }); - }); - - - return trainingData; } } diff --git a/BotSharp.Core/Engines/Dialogflow/AIResponseCustomPayload.cs b/BotSharp.Core/Engines/Dialogflow/AIResponseCustomPayload.cs index 57701442..3af9984a 100644 --- a/BotSharp.Core/Engines/Dialogflow/AIResponseCustomPayload.cs +++ b/BotSharp.Core/Engines/Dialogflow/AIResponseCustomPayload.cs @@ -8,6 +8,6 @@ namespace BotSharp.Core.Models { public string Task { get; set; } - public Object Body { get; set; } + public List Parameters { get; set; } } } diff --git a/BotSharp.Core/Engines/Dialogflow/AgentImporterInDialogflow.cs b/BotSharp.Core/Engines/Dialogflow/AgentImporterInDialogflow.cs index 1b0a9439..63e8f997 100644 --- a/BotSharp.Core/Engines/Dialogflow/AgentImporterInDialogflow.cs +++ b/BotSharp.Core/Engines/Dialogflow/AgentImporterInDialogflow.cs @@ -83,6 +83,8 @@ namespace BotSharp.Core.Engines intentJson = intentJson.Replace("\"prompts\":", "\"promptList\":"); var intent = JsonConvert.DeserializeObject(intentJson); + // void id confict + intent.Id = Guid.NewGuid().ToString(); intent.Name = intent.Name.Replace("/","_"); // load user expressions if (fileName.Contains("Default Fallback Intent")) diff --git a/BotSharp.Core/Engines/Rasa/RasaAi.cs b/BotSharp.Core/Engines/Rasa/RasaAi.cs index 06c74f87..80ffec3e 100644 --- a/BotSharp.Core/Engines/Rasa/RasaAi.cs +++ b/BotSharp.Core/Engines/Rasa/RasaAi.cs @@ -1,5 +1,6 @@ using BotSharp.Core.Agents; using BotSharp.Core.Entities; +using BotSharp.Core.Intents; using BotSharp.Core.Models; using EntityFrameworkCore.BootKit; using Microsoft.EntityFrameworkCore; @@ -86,5 +87,48 @@ namespace BotSharp.Core.Engines return String.Empty; } } + + public string TrainWithContexts() + { + var corpus = agent.GrabCorpus(dc); + + string json = JsonConvert.SerializeObject(new { rasa_nlu_data = corpus }, + new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore + }); + + var client = new RestClient($"{Database.Configuration.GetSection("Rasa:Host").Value}"); + var rest = new RestRequest("train", Method.POST); + rest.AddQueryParameter("project", agent.Id); + rest.AddParameter("application/json", json, ParameterType.RequestBody); + + var response = client.Execute(rest); + + if (response.IsSuccessful) + { + var result = JObject.Parse(response.Content); + + string modelName = result["info"].Value().Split(": ")[1]; + + dc.Table().Add(new ContextModelMapping + { + AgentId = agent.Id, + ModelName = modelName, + //ContextId = contextId + }); + + return modelName; + } + else + { + var result = JObject.Parse(response.Content); + + Console.WriteLine(result["error"]); + + return String.Empty; + } + } } } diff --git a/BotSharp.Core/Engines/Rasa/RasaIntentExpression.cs b/BotSharp.Core/Engines/Rasa/RasaIntentExpression.cs index b7562340..6ab8d0a3 100644 --- a/BotSharp.Core/Engines/Rasa/RasaIntentExpression.cs +++ b/BotSharp.Core/Engines/Rasa/RasaIntentExpression.cs @@ -12,6 +12,7 @@ namespace BotSharp.Core.Models public String Text { get; set; } public String Intent { get; set; } + public String ContextHash { get; set; } public List Entities { get; set; } } } diff --git a/BotSharp.Core/Engines/RequestExtension.cs b/BotSharp.Core/Engines/RequestExtension.cs index d966ad0f..4266c5ae 100644 --- a/BotSharp.Core/Engines/RequestExtension.cs +++ b/BotSharp.Core/Engines/RequestExtension.cs @@ -113,8 +113,9 @@ namespace BotSharp.Core.Engines response.Intent }; } - response.IntentRanking = response.IntentRanking - .Where(x => x.Confidence > decimal.Parse("0.2") && intents.Select(i => i.Name).Contains(x.Name)).ToList(); + + response.IntentRanking = response.IntentRanking.Where(x => x.Confidence > decimal.Parse("0.1")).ToList(); + response.IntentRanking = response.IntentRanking.Where(x => intents.Select(i => i.Name).Contains(x.Name)).ToList(); // add Default Fallback Intent if (response.IntentRanking.Count == 0) @@ -314,23 +315,10 @@ namespace BotSharp.Core.Engines } }).OrderByDescending(x => x.Contexts.Count).ToList(); - // training per request contexts + // query per request contexts { string contextId = $"{String.Join(',', contexts.Select(x => x.Name))}".GetMd5Hash(); string modelName = dc.Table().FirstOrDefault(x => x.ContextId == contextId)?.ModelName; - // need training - if (String.IsNullOrEmpty(modelName)) - { - request.Contexts = contexts.Select(x => new AIContext { Name = x.Name.ToLower() }) - .OrderBy(x => x.Name) - .ToList(); - - dc.DbTran(() => - { - modelName = TrainWithContexts(rasa, dc, request, contextId); - }); - } - var result = CallRasa(rasa.agent.Id, request.Query.First(), modelName); if (result.Data.Intent != null) @@ -351,15 +339,6 @@ namespace BotSharp.Core.Engines string modelName = dc.Table().FirstOrDefault(x => x.ContextId == contextId)?.ModelName; - // need training - if (String.IsNullOrEmpty(modelName)) - { - dc.DbTran(() => - { - modelName = TrainWithContexts(rasa, dc, request, contextId); - }); - } - var result = CallRasa(rasa.agent.Id, request.Query.First(), modelName); if (result.Data.Intent != null) @@ -470,74 +449,5 @@ namespace BotSharp.Core.Engines return aiResponse; } - - /// - /// Need two categories at least - /// - /// - /// - /// - /// - /// - public static string TrainWithContexts(this RasaAi console, Database dc, AIRequest request, String contextId) - { - var corpus = console.agent.GrabCorpusPerContexts(dc, request.Contexts); - - corpus.UserSays.Add(new RasaIntentExpression - { - Intent = "Welcome", - Text = "Hi" - }); - - corpus.UserSays.Add(new RasaIntentExpression - { - Intent = "Welcome", - Text = "Hey" - }); - - corpus.UserSays.Add(new RasaIntentExpression - { - Intent = "Welcome", - Text = "Hello" - }); - - string json = JsonConvert.SerializeObject(new { rasa_nlu_data = corpus }, - new JsonSerializerSettings - { - ContractResolver = new CamelCasePropertyNamesContractResolver(), - NullValueHandling = NullValueHandling.Ignore - }); - - var client = new RestClient($"{Database.Configuration.GetSection("Rasa:Host").Value}"); - var rest = new RestRequest("train", Method.POST); - rest.AddQueryParameter("project", console.agent.Id); - rest.AddParameter("application/json", json, ParameterType.RequestBody); - - var response = client.Execute(rest); - - if (response.IsSuccessful) - { - var result = JObject.Parse(response.Content); - - string modelName = result["info"].Value().Split(": ")[1]; - - dc.Table().Add(new ContextModelMapping - { - AgentId = console.agent.Id, - ModelName = modelName, - ContextId = contextId - }); - - return modelName; - } - else - { - var result = JObject.Parse(response.Content); - - Console.WriteLine(result["error"]); - - return String.Empty; - } - } } } diff --git a/BotSharp.Core/Entities/EntrySynonym.cs b/BotSharp.Core/Entities/EntrySynonym.cs index 1b00f295..a2222dfa 100644 --- a/BotSharp.Core/Entities/EntrySynonym.cs +++ b/BotSharp.Core/Entities/EntrySynonym.cs @@ -14,7 +14,7 @@ namespace BotSharp.Core.Entities [StringLength(36)] public String EntityEntryId { get; set; } - [MaxLength(64)] + [MaxLength(128)] public String Synonym { get; set; } } } diff --git a/BotSharp.Core/Intents/Intent.cs b/BotSharp.Core/Intents/Intent.cs index 5d20ded0..25ee198c 100644 --- a/BotSharp.Core/Intents/Intent.cs +++ b/BotSharp.Core/Intents/Intent.cs @@ -1,8 +1,10 @@ -using EntityFrameworkCore.BootKit; +using DotNetToolkit; +using EntityFrameworkCore.BootKit; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; using System.Text; namespace BotSharp.Core.Intents @@ -26,6 +28,18 @@ namespace BotSharp.Core.Intents [ForeignKey("IntentId")] public List Contexts { get; set; } + /// + /// Get input contexts hash + /// + [NotMapped] + public String ContextHash + { + get + { + return $"{String.Join(',', Contexts.OrderBy(x => x.Name).Select(x => x.Name))}".GetMd5Hash(); + } + } + [ForeignKey("IntentId")] public List UserSays { get; set; } diff --git a/BotSharp.UnitTest/AgentTest.cs b/BotSharp.UnitTest/AgentTest.cs index 87506f92..7c616554 100644 --- a/BotSharp.UnitTest/AgentTest.cs +++ b/BotSharp.UnitTest/AgentTest.cs @@ -73,5 +73,18 @@ namespace BotSharp.UnitTest Assert.IsTrue(!String.IsNullOrEmpty(msg)); } + + [TestMethod] + public void TrainAgentPerContextTest() + { + var config = new AIConfiguration(BOT_CLIENT_TOKEN, SupportedLanguage.English); + config.SessionId = Guid.NewGuid().ToString(); + + var rasa = new RasaAi(dc, config); + + string msg = rasa.TrainWithContexts(); + + Assert.IsTrue(!String.IsNullOrEmpty(msg)); + } } } diff --git a/BotSharp.UnitTest/BotSharp.UnitTest.csproj b/BotSharp.UnitTest/BotSharp.UnitTest.csproj index 44a9776a..57854ba7 100644 --- a/BotSharp.UnitTest/BotSharp.UnitTest.csproj +++ b/BotSharp.UnitTest/BotSharp.UnitTest.csproj @@ -7,208 +7,21 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - PreserveNewest @@ -237,7 +50,7 @@ - + diff --git a/BotSharp.UnitTest/IntentTest.cs b/BotSharp.UnitTest/IntentTest.cs index 533e65f4..d2c2bc83 100644 --- a/BotSharp.UnitTest/IntentTest.cs +++ b/BotSharp.UnitTest/IntentTest.cs @@ -20,8 +20,8 @@ namespace BotSharp.UnitTest var rasa = new RasaAi(dc, config); // Round 1 - var response = rasa.TextRequest(new AIRequest { Query = new String[] { "Hi" } }); - Assert.AreEqual(response.Result.Metadata.IntentName, "greet"); + var response = rasa.TextRequest(new AIRequest { Query = new String[] { "Can you play country music?" } }); + Assert.AreEqual(response.Result.Metadata.IntentName, "music.play"); } } } diff --git a/BotSharp.UnitTest/TestEssential.cs b/BotSharp.UnitTest/TestEssential.cs index 44ae611c..0c6993f0 100644 --- a/BotSharp.UnitTest/TestEssential.cs +++ b/BotSharp.UnitTest/TestEssential.cs @@ -10,10 +10,10 @@ namespace BotSharp.UnitTest { public abstract class TestEssential { - public static String BOT_ID = "5f98a0fd-e7e9-4155-9610-d3f40d026162"; - public static String BOT_CLIENT_TOKEN = "2fffb9a1a9214144ab2717a37fa43c33"; - public static String BOT_DEVELOPER_TOKEN = "2c7d224cf7274f9d93b4c65c31ca82fe"; - public static String BOT_NAME = "Handybot"; + public static String BOT_ID = "6a9fd374-c43d-447a-97f2-f37540d0c725"; + public static String BOT_CLIENT_TOKEN = "43a0f48e3f1e41da822092e7e699426b"; + public static String BOT_DEVELOPER_TOKEN = "cd1e4685c6a04d7db1f59e6853fd597b"; + public static String BOT_NAME = "Spotify"; protected Database dc { get; set; } protected string contentRoot;