public async Task TestIsSkillHelper() { using (StreamReader sr = new StreamReader(@".\Skills\manifestTemplate.json")) { string manifestBody = await sr.ReadToEndAsync(); var skillManifest = JsonConvert.DeserializeObject <SkillManifest>(manifestBody); List <SkillManifest> skillManifests = new List <SkillManifest>(); skillManifests.Add(skillManifest); Assert.IsNotNull(SkillRouter.IsSkill(skillManifests, "calendarSkill/createEvent")); Assert.IsNotNull(SkillRouter.IsSkill(skillManifests, "calendarSkill/updateEvent")); Assert.IsNull(SkillRouter.IsSkill(skillManifests, "calendarSkill/MISSINGEVENT")); } }
// Runs when the dialog stack is empty, and a new message activity comes in. protected override async Task OnMessageActivityAsync(DialogContext innerDc, CancellationToken cancellationToken = default) { var activity = innerDc.Context.Activity.AsMessageActivity(); var userProfile = await _userProfileState.GetAsync(innerDc.Context, () => new UserProfileState()); if (!string.IsNullOrEmpty(activity.Text)) { // Get current cognitive models for the current locale. CognitiveModelSet localizedServices = _services.GetCognitiveModels(); // Get dispatch result from turn state. var dispatchResult = innerDc.Context.TurnState.Get <DispatchLuis>(StateProperties.DispatchResult); (var dispatchIntent, var dispatchScore) = dispatchResult.TopIntent(); // Check if the dispatch intent maps to a skill. var identifiedSkill = SkillRouter.IsSkill(_settings.Skills, dispatchIntent.ToString()); if (identifiedSkill != null) { // Start the skill dialog. await innerDc.BeginDialogAsync(identifiedSkill.Id); } else if (dispatchIntent == DispatchLuis.Intent.q_Faq) { await innerDc.BeginDialogAsync("Faq"); } else if (dispatchIntent == DispatchLuis.Intent.q_Chitchat) { innerDc.SuppressCompletionMessage(true); await innerDc.BeginDialogAsync("Chitchat"); } else if (dispatchIntent == DispatchLuis.Intent.q_HRBenefits) { innerDc.SuppressCompletionMessage(true); await innerDc.BeginDialogAsync("HRBenefits"); } else { innerDc.SuppressCompletionMessage(true); await innerDc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("UnsupportedMessage", userProfile)); } } }
protected override async Task RouteAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken)) { // Get cognitive models for locale var locale = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName; var cognitiveModels = _services.CognitiveModelSets[locale]; // Check dispatch result var dispatchResult = await cognitiveModels.DispatchService.RecognizeAsync <DispatchLuis>(dc.Context, CancellationToken.None); var intent = dispatchResult.TopIntent().intent; // Identify if the dispatch intent matches any Action within a Skill if so, we pass to the appropriate SkillDialog to hand-off var identifiedSkill = SkillRouter.IsSkill(_settings.Skills, intent.ToString()); if (identifiedSkill != null) { // We have identiifed a skill so initialize the skill connection with the target skill var result = await dc.BeginDialogAsync(identifiedSkill.Id); if (result.Status == DialogTurnStatus.Complete) { await CompleteAsync(dc); } } else if (intent == DispatchLuis.Intent.l_General) { // If dispatch result is General luis model cognitiveModels.LuisServices.TryGetValue("General", out var luisService); if (luisService == null) { throw new Exception("The General LUIS Model could not be found in your Bot Services configuration."); } else { var result = await luisService.RecognizeAsync <GeneralLuis>(dc.Context, CancellationToken.None); var generalIntent = result?.TopIntent().intent; // switch on general intents switch (generalIntent) { case GeneralLuis.Intent.Escalate: { // start escalate dialog await dc.BeginDialogAsync(nameof(EscalateDialog)); break; } case GeneralLuis.Intent.None: default: { // No intent was identified, send confused message await _responder.ReplyWith(dc.Context, MainResponses.ResponseIds.Confused); break; } } } } else if (intent == DispatchLuis.Intent.q_Faq) { cognitiveModels.QnAServices.TryGetValue("Faq", out var qnaService); if (qnaService == null) { throw new Exception("The specified QnA Maker Service could not be found in your Bot Services configuration."); } else { var answers = await qnaService.GetAnswersAsync(dc.Context, null, null); if (answers != null && answers.Count() > 0) { await dc.Context.SendActivityAsync(answers[0].Answer, speak : answers[0].Answer); } else { await _responder.ReplyWith(dc.Context, MainResponses.ResponseIds.Confused); } } } else if (intent == DispatchLuis.Intent.q_Chitchat) { cognitiveModels.QnAServices.TryGetValue("Chitchat", out var qnaService); if (qnaService == null) { throw new Exception("The specified QnA Maker Service could not be found in your Bot Services configuration."); } else { var answers = await qnaService.GetAnswersAsync(dc.Context, null, null); if (answers != null && answers.Count() > 0) { await dc.Context.SendActivityAsync(answers[0].Answer, speak : answers[0].Answer); } else { await _responder.ReplyWith(dc.Context, MainResponses.ResponseIds.Confused); } } } else { // If dispatch intent does not map to configured models, send "confused" response. // Alternatively as a form of backup you can try QnAMaker for anything not understood by dispatch. await _responder.ReplyWith(dc.Context, MainResponses.ResponseIds.Confused); } }
// Runs on every turn of the conversation to check if the conversation should be interrupted. protected override async Task <InterruptionAction> OnInterruptDialogAsync(DialogContext dc, CancellationToken cancellationToken) { var activity = dc.Context.Activity; var userProfile = await _userProfileState.GetAsync(dc.Context, () => new UserProfileState()); var dialog = dc.ActiveDialog?.Id != null?dc.FindDialog(dc.ActiveDialog?.Id) : null; if (activity.Type == ActivityTypes.Message && !string.IsNullOrEmpty(activity.Text)) { // Check if the active dialog is a skill for conditional interruption. var isSkill = dialog is SkillDialog; // Get Dispatch LUIS result from turn state. var dispatchResult = dc.Context.TurnState.Get <DispatchLuis>(StateProperties.DispatchResult); (var dispatchIntent, var dispatchScore) = dispatchResult.TopIntent(); // Check if we need to switch skills. if (isSkill) { if (dispatchIntent.ToString() != dialog.Id && dispatchScore > 0.9) { var identifiedSkill = SkillRouter.IsSkill(_settings.Skills, dispatchResult.TopIntent().intent.ToString()); if (identifiedSkill != null) { var prompt = _templateEngine.GenerateActivityForLocale("SkillSwitchPrompt", new { Skill = identifiedSkill.Name }); await dc.BeginDialogAsync(_switchSkillDialog.Id, new SwitchSkillDialogOptions(prompt, identifiedSkill)); return(InterruptionAction.Waiting); } } } if (dispatchIntent == DispatchLuis.Intent.l_General) { // Get connected LUIS result from turn state. var generalResult = dc.Context.TurnState.Get <GeneralLuis>(StateProperties.GeneralResult); (var generalIntent, var generalScore) = generalResult.TopIntent(); if (generalScore > 0.5) { switch (generalIntent) { case GeneralLuis.Intent.Cancel: { // Suppress completion message for utility functions. dc.SuppressCompletionMessage(true); await dc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("CancelledMessage", userProfile)); await dc.CancelAllDialogsAsync(); return(InterruptionAction.End); } case GeneralLuis.Intent.Escalate: { await dc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("EscalateMessage", userProfile)); return(InterruptionAction.Resume); } case GeneralLuis.Intent.Help: { // Suppress completion message for utility functions. dc.SuppressCompletionMessage(true); if (isSkill) { // If current dialog is a skill, allow it to handle its own help intent. await dc.ContinueDialogAsync(cancellationToken); break; } else { await dc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("HelpCard", userProfile)); return(InterruptionAction.Resume); } } case GeneralLuis.Intent.Logout: { // Suppress completion message for utility functions. dc.SuppressCompletionMessage(true); // Log user out of all accounts. await LogUserOut(dc); await dc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("LogoutMessage", userProfile)); return(InterruptionAction.End); } case GeneralLuis.Intent.Repeat: { // No need to send the usual dialog completion message for utility capabilities such as these. dc.SuppressCompletionMessage(true); // Sends the activities since the last user message again. var previousResponse = await _previousResponseAccessor.GetAsync(dc.Context, () => new List <Activity>()); foreach (var response in previousResponse) { // Reset id of original activity so it can be processed by the channel. response.Id = string.Empty; await dc.Context.SendActivityAsync(response); } return(InterruptionAction.Waiting); } case GeneralLuis.Intent.StartOver: { // Suppresss completion message for utility functions. dc.SuppressCompletionMessage(true); await dc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("StartOverMessage", userProfile)); // Cancel all dialogs on the stack. await dc.CancelAllDialogsAsync(); return(InterruptionAction.End); } case GeneralLuis.Intent.Stop: { // Use this intent to send an event to your device that can turn off the microphone in speech scenarios. break; } } } } } return(InterruptionAction.NoAction); }