public async Task <IActionResult> StartTalk([FromBody] bool interruptTalk) { var context = new TalkContext(interruptTalk); await _talkSaga.Execute(context); return(Ok(context.Talk)); }
/// <summary> /// tokenise human text into a token list /// </summary> /// <param name="config"></param> /// <param name="context"></param> /// <param name="customerMessage"></param> /// <param name="tokenisers"></param> /// <returns></returns> private static List <Token> TokeniseText(IDialogConfig config, TalkContext context, string customerMessage, IEnumerable <IEntityTokeniser> tokenisers) { var currentStep = config.Intents.FirstOrDefault(x => x.Name == context.CurrentIntent); var flattenedTokens = Parser.ParseText(context.Properties, customerMessage, tokenisers); return(flattenedTokens.MostLikely(currentStep.DataToCollect)); }
static void Main(string[] args) { Console.WriteLine("Lets Talk!"); var talkConfig = BuildConfiguration(); var services = new ServiceCollection() .AddTransient <IEntityTokeniser, DateTokeniser>() .AddTransient <IEntityTokeniser, KeywordTokeniser>() .AddTransient <IEntityTokeniser, PropertyKeywordTokeniser>() .AddTransient <IEntityTokeniser, AmountTokeniser>() .AddSingleton <IDialogConfig>(talkConfig); services.AddLogging(loggingBuilder => { loggingBuilder.AddConsole(); }); _serviceProvider = services.BuildServiceProvider(); var config = BuildConfig.Build(talkConfig); //var list = Talk.ParseText(context.Properties, "please take money 20 3rd march 1999 hate this stuff paid marcus poulton", _serviceProvider); using (var scope = _serviceProvider.CreateScope()) { TalkContext context = new TalkContext { IntentGroup = "PreDelinquent", CurrentIntent = "PreDelinquentInitial", Properties = new Dictionary <string, object> { { "Birthday", new DateTime(1999, 3, 3).Date }, { "Person:FirstName", "Marcus" }, { "Person:GivenName", "Poulton" }, { "MinimumPayment", 12.0 }, { "MaximumPayment", 24.0 }, { "Last4Card", 1234.0 }, { "DueDate", DateTime.Today.AddDays(1) }, } }; var configtext = JsonConvert.SerializeObject(config); var contexttext = JsonConvert.SerializeObject(context); var tokenisers = scope.ServiceProvider.GetServices <IEntityTokeniser>(); var tests = BuildTest.Build(); var passed = DialogTestEngine.ExecuteBulkTest(config, tokenisers, tests); // DialogConsole.ExecuteAsConsole(config, context, tokenisers); } }
/// <summary> /// default method for handling NextStep /// </summary> /// <param name="config"></param> /// <param name="context"></param> public static void NextStepDefaultAction(IDialogConfig config, TalkContext context) { var group = config.IntentGroups[context.IntentGroup]; var route = group.IntentRoutes.FirstOrDefault(x => x.FromName == context.CurrentIntent); if (route != null) { context.CurrentIntent = route.ToName; } else { context.CurrentIntent = null; } }
/// <summary> /// default method for handling FailAction /// Moves to next fail step /// </summary> /// <param name="config"></param> /// <param name="context"></param> public static void FailDefaultAction(IDialogConfig config, TalkContext context) { // get next intent from the route var group = config.IntentGroups[context.IntentGroup]; var route = group.IntentRoutes.FirstOrDefault(x => x.FromName == context.CurrentIntent); if (route != null) { context.CurrentIntent = route.FailName; } else { context.CurrentIntent = null; } }
private static void PerformStep( IDialogConfig config, TalkContext context, IEnumerable <IEntityTokeniser> tokenisers, Action <string> Say, Func <string> GetResponse ) { bool quit = false; string humanText = null; do { TalkAction action = ProcessResponse(humanText, config, context, tokenisers); if (action is FailAction failAction) { FailDefaultAction(config, context); } if (action is SayAction sayAction) { // send Say(sayAction.Prompt); humanText = GetResponse(); if (string.IsNullOrEmpty(humanText)) { break; } continue; } if (action is NextStepAction nextAction) { // get next intent from the route humanText = null; NextStepDefaultAction(config, context); } if (context.CurrentIntent == null) { quit = true; } } while (!quit); }
public static void ExecuteAsConsole(IDialogConfig config, TalkContext context, IEnumerable <IEntityTokeniser> tokenisers) { PerformStep( config, context, tokenisers, (x) => { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine(x); }, () => { // receive Console.ForegroundColor = ConsoleColor.White; return(Console.ReadLine()); } ); }
public static bool ExecuteBulkTest(IDialogConfig config, IEnumerable <IEntityTokeniser> tokenisers, List <DialogTest> tests) { foreach (var test in tests) { TalkContext context = new TalkContext { IntentGroup = test.IntentGroup, CurrentIntent = test.CurrentIntent, Properties = test.Properties }; var passed = ExecuteTest(test, config, context, tokenisers); if (!passed) { return(false); } } // all passed return(true); }
/// <summary> /// make a message from a message template key and data in the context /// </summary> /// <param name="message_key"></param> /// <param name="context"></param> /// <returns></returns> private static string MakeMessageFromKey(string message_key, IDialogConfig config, TalkContext context) { var currentStep = config.Intents.FirstOrDefault(x => x.Name == context.CurrentIntent); var msg_template = currentStep.MessageTemplates[message_key]; var msg = msg_template.Substitute(context); return(msg); }
private static void HandleWarnings(List <CollectPropertyMatch> matches, List <string> botResponse, Intent currentStep, IDialogConfig config, TalkContext context) { // find warnings var warn_matches = matches.Where(x => x.Property.Result == CollectProperty.CollectionResult.Warning).ToList(); if (warn_matches != null && warn_matches.Count > 0) { foreach (var warning in warn_matches) { // remove any collected data that matches this property name if (!string.IsNullOrEmpty(warning.Property.PropertyName)) { matches.RemoveAll(x => x.Property.Result == CollectProperty.CollectionResult.Collect && x.Property.PropertyName == warning.Property.PropertyName); } botResponse.Add(MakeMessageFromKey(warning.Property.CapturedTemplate, config, context)); } } }
public RoomModel(AccountContext accountContext, TalkContext talkContext) { _accContext = accountContext; _talkContext = talkContext; }
public static bool ExecuteTest(DialogTest test, IDialogConfig config, TalkContext context, IEnumerable <IEntityTokeniser> tokenisers) { Console.ForegroundColor = ConsoleColor.Black; Console.BackgroundColor = ConsoleColor.White; Console.WriteLine($"Executing: {test.Description}"); var botres = new string(' ', 1); var humres = new string(' ', 30); var botinfo = new string(' ', 60); var contextJson = JsonConvert.SerializeObject(context, jsonsettings); foreach (var response in test.Responses) { // display current intent Console.ForegroundColor = ConsoleColor.Gray; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"{botinfo} {context.IntentGroup}:{context.CurrentIntent} data:{context.CollectedData.Count}"); if (context.IntentGroup != response.IntentGroup || context.CurrentIntent != response.CurrentIntent) { Console.ForegroundColor = ConsoleColor.Red; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Test Failed: Incorrect intent: Expected {response.IntentGroup}:{response.CurrentIntent} but got {context.IntentGroup}:{context.CurrentIntent}"); return(false); } context = JsonConvert.DeserializeObject <TalkContext>(contextJson, jsonsettings); if (!string.IsNullOrEmpty(response.Human)) { Console.ForegroundColor = ConsoleColor.Yellow; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Human: {response.Human}"); } // get bot response var action = ProcessResponse(response.Human, config, context, tokenisers); if (action is SayAction sayAction) { Console.ForegroundColor = ConsoleColor.Cyan; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Bot: {botres} {sayAction.Prompt}"); if (typeof(SayAction) != response.Bot.GetType()) { Console.ForegroundColor = ConsoleColor.Red; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Test Failed: Expected Bot response of type {response.Bot.GetType().Name} but got {action.GetType().Name}"); return(false); } // check we have the right cetagory of response var expected_category = ((SayAction)response.Bot).Category; if (sayAction.Category != expected_category) { Console.ForegroundColor = ConsoleColor.Red; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Test Failed: Expected Bot category of {expected_category} but got {sayAction.Category}"); return(false); } } if (action is FailAction failAction) { Console.ForegroundColor = ConsoleColor.Cyan; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Bot Fail: {botres} {failAction.Reason} {string.Join(",", failAction.Rejections.Select(x=>x.PropertyName))}"); if (typeof(FailAction) != response.Bot.GetType()) { Console.ForegroundColor = ConsoleColor.Red; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Test Failed: Expected Bot response of type {response.Bot.GetType().Name} but got {action.GetType().Name}"); return(false); } FailDefaultAction(config, context); } if (action is NextStepAction nextAction) { Console.ForegroundColor = ConsoleColor.Cyan; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Bot Step: {botres} {nextAction.Prompt}"); if (typeof(NextStepAction) != response.Bot.GetType()) { Console.ForegroundColor = ConsoleColor.Red; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Test Failed: Expected Bot response of type {response.Bot.GetType().Name} but got {action.GetType().Name}"); return(false); } NextStepDefaultAction(config, context); } // save context in json contextJson = JsonConvert.SerializeObject(context, jsonsettings); } Console.ForegroundColor = ConsoleColor.Green; Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine($"Test Passed\n\n"); return(true); }
private static void HandleCollectedData(List <CollectPropertyMatch> matches, List <string> botResponse, Intent currentStep, IDialogConfig config, TalkContext context) { var collect_matches = matches.Where(x => x.Property.Result == CollectProperty.CollectionResult.Collect).ToList(); // got some new data if (collect_matches != null && collect_matches.Count > 0) { // store collected data into the context foreach (var collect in collect_matches) { var key = collect.Property.PropertyName; // replace existing entity if it already exists if (context.CollectedData.ContainsKey(key)) { context.CollectedData.Remove(key); } context.CollectedData.Add(key, collect.MatchingTokens.First()); } // get list of collect matches that need to be repeated back to human var collect_matches_to_verbalise = collect_matches.Where(x => !string.IsNullOrEmpty(x.Property.CapturedTemplate)); if (collect_matches_to_verbalise != null && collect_matches_to_verbalise.Count() > 0 && !string.IsNullOrEmpty(currentStep.CapturedPrompt)) { // output header message var msg = MakeMessageFromKey(currentStep.CapturedPrompt, config, context); botResponse.Add(msg); // output each captured property to verbalise foreach (var collect in collect_matches_to_verbalise) { // show message detailing what I got tis time around var itemmsg = MakeMessageFromKey(collect.Property.CapturedTemplate, config, context); botResponse.Add(itemmsg); } } } }
private static FailAction HandleTooManyWarnings(List <CollectPropertyMatch> matches, Intent currentStep, IDialogConfig config, TalkContext context) { // build a list of missing non-optional "Collect" items var items = matches .Select(x => x.Property) .Where(x => x.MaxTries > 0 && x.Result == CollectProperty.CollectionResult.Warning) .ToList(); return(HandleTooManyRepeats(items, currentStep, config, context)); }
/// <summary> /// check that we have captured all required items /// </summary> /// <param name="config"></param> /// <param name="context"></param> /// <returns></returns> private static bool GotValuesForStep(Intent currentStep, IDialogConfig config, TalkContext context) { // get list of items required on this step. var reqrd_properties = currentStep.DataToCollect.Where(x => x.Result == CollectProperty.CollectionResult.Collect).ToList(); // check the collected data to see if we have everything foreach (var property in reqrd_properties) { if (!context.CollectedData.Any(x => x.Key == property.PropertyName)) { return(false); } } return(true); }
public static TalkAction ProcessResponse( string humanText, IDialogConfig config, TalkContext context, IEnumerable <IEntityTokeniser> tokenisers) { var currentStep = config.Intents.FirstOrDefault(x => x.Name == context.CurrentIntent); // no customer response just yet - assume initial prompt if (humanText == null) { var response = currentStep.InitialPrompt.Substitute(context); return(new SayAction { Prompt = response, Category = "InitialPrompt" }); } List <string> botResponse = new List <string>(); // decode their response var responseTokenList = TokeniseText(config, context, humanText, tokenisers); // collect all data from tokens var matches = Contains(responseTokenList, currentStep.DataToCollect); // report any failures var fail_matches = matches.Where(x => x.Property.Result == CollectProperty.CollectionResult.Fail).ToList(); if (fail_matches != null && fail_matches.Count > 0) { return(new FailAction { Reason = $"Escalated because of {fail_matches.Count} rejection tokens", Rejections = fail_matches.Select(x => x.Property).ToList() }); } HandleWarnings(matches, botResponse, currentStep, config, context); HandleCollectedData(matches, botResponse, currentStep, config, context); // got it all?? if (GotValuesForStep(currentStep, config, context)) { botResponse.Add(MakeMessageFromKey(currentStep.CompletePrompt, config, context)); return(new NextStepAction { Prompt = string.Join("\n", botResponse) }); } // handle repeated failures var failaction = HandleTooManyWarnings(matches, currentStep, config, context); if (failaction != null) { return(failaction); } failaction = HandleTooManyCollects(botResponse, currentStep, config, context); if (failaction != null) { return(failaction); } HandleMissingItems(botResponse, currentStep, config, context); return(new SayAction { Category = "MoreData", Prompt = string.Join("\n", botResponse) }); }
private static string Substitute(this string message, TalkContext context) { return(message .Substitute(context.Properties) .Substitute(context.CollectedData)); }
private static void HandleMissingItems(List <string> botResponse, Intent currentStep, IDialogConfig config, TalkContext context) { var missing_list = new List <CollectProperty>(); // build a list of missing non-optional "Collect" items foreach (var required in currentStep.DataToCollect.Where(x => x.Optional == false && x.Result == CollectProperty.CollectionResult.Collect)) { if (!context.CollectedData.ContainsKey(required.PropertyName)) { missing_list.Add(required); } } if (missing_list.Count > 1) { botResponse.Add(MakeMessageFromKey(currentStep.InCompleteManyPrompt, config, context)); for (int i = 1; i <= missing_list.Count; i++) { botResponse.Add($"{i}) " + MakeMessageFromKey(missing_list[i - 1].PromptTemplate, config, context)); } } if (missing_list.Count == 1) { var s1 = MakeMessageFromKey(currentStep.InCompleteSinglePrompt, config, context); var s2 = MakeMessageFromKey(missing_list[0].PromptTemplate, config, context); botResponse.Add(s1 + s2); } }
private static FailAction HandleTooManyRepeats(List <CollectProperty> items, Intent currentStep, IDialogConfig config, TalkContext context) { // check the number times we have tried to collect this data foreach (var item in items) { // add if new var countkey = $"count_{item.PropertyName}"; if (!context.AskCount.ContainsKey(countkey)) { context.AskCount.Add(countkey, 0); } // increment var tries = context.AskCount[countkey] + 1; context.AskCount[countkey] = tries; // check limit (MaxTries can be 0 for infinite retries) if (item.MaxTries > 0 && tries == item.MaxTries) { return new FailAction { Reason = $"Hi maximum. Got {item.PropertyName} {item.MaxTries} times", Rejections = new List <CollectProperty> { item } } } ; } return(null); }
private static FailAction HandleTooManyCollects(List <string> botResponse, Intent currentStep, IDialogConfig config, TalkContext context) { // build a list of missing non-optional "Collect" items var items = currentStep.DataToCollect .Where(x => x.MaxTries > 0 && x.Optional == false && x.Result == CollectProperty.CollectionResult.Collect) .ToList(); return(HandleTooManyRepeats(items, currentStep, config, context)); }