예제 #1
0
        public async Task <IActionResult> StartTalk([FromBody] bool interruptTalk)
        {
            var context = new TalkContext(interruptTalk);
            await _talkSaga.Execute(context);

            return(Ok(context.Talk));
        }
예제 #2
0
        /// <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));
        }
예제 #3
0
        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);
            }
        }
예제 #4
0
        /// <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;
            }
        }
예제 #5
0
        /// <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;
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
 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());
     }
         );
 }
예제 #8
0
        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);
        }
예제 #9
0
        /// <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);
        }
예제 #10
0
        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));
                }
            }
        }
예제 #11
0
 public RoomModel(AccountContext accountContext, TalkContext talkContext)
 {
     _accContext  = accountContext;
     _talkContext = talkContext;
 }
예제 #12
0
        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);
        }
예제 #13
0
        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);
                    }
                }
            }
        }
예제 #14
0
        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));
        }
예제 #15
0
        /// <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);
        }
예제 #16
0
        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)
            });
        }
예제 #17
0
 private static string Substitute(this string message, TalkContext context)
 {
     return(message
            .Substitute(context.Properties)
            .Substitute(context.CollectedData));
 }
예제 #18
0
        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);
            }
        }
예제 #19
0
        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);
        }
예제 #20
0
        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));
        }