0

Hello when i prompt the user with a confirm prompt or a yes or no prompt. Luis is detecting "no" as cancel intent which cancel my whole dialog. And then i deleted "no" from the cancel intent but now "no is being detected by luis as a greeting intent. There is not even a "no" word from the greeting intent. As much as possible i do not want to disable luis because a user can cancel anytime. How can i fix this? thanks!

Here is the code.

       public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
    {
        var dc = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
        var activity = turnContext.Activity;

        var userstate = await _basicAccessors.BasicUserStateAccessor.GetAsync(turnContext, () => new BasicUserState(), cancellationToken);
        var state = await _basicAccessors.BasicStateAccessor.GetAsync(turnContext, () => new BasicState(), cancellationToken);

        if (turnContext.Activity.Type == ActivityTypes.Message)
        {
            turnContext.TurnState.Add("BasicAccessors", _basicAccessors);
            string text = string.IsNullOrEmpty(turnContext.Activity.Text) ? string.Empty : turnContext.Activity.Text.ToLower();

            var luisResults = await _services.LuisServices[LuisConfiguration].RecognizeAsync(dc.Context, cancellationToken);

            var topScoringIntent = luisResults?.GetTopScoringIntent();
            var topIntent = topScoringIntent.Value.intent;

            string userName = string.Empty;
            if (activity.From.Name != null)
            {
                userName = activity.From.Name;
            }
            userstate.Name = userName;

            await _basicAccessors.BasicUserStateAccessor.SetAsync(turnContext, userstate);
            await _basicAccessors.BasicStateAccessor.SetAsync(turnContext, state);

            var interrupted = await IsTurnInterruptedAsync(dc, topIntent);
            if (interrupted)
            {
                // Bypass the dialog.
                // Save state before the next turn.
                await _basicAccessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
                await _basicAccessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
                return;
            }

            // Continue the current dialog
            var dialogResult = await dc.ContinueDialogAsync();

            // if no one has responded,
            if (!dc.Context.Responded)
            {
                // examine results from active dialog
                switch (dialogResult.Status)
                {
                    case DialogTurnStatus.Empty:
                        switch (topIntent)
                        {
                            case GreetingIntent:
                                await dc.BeginDialogAsync(MainDialogId);
                                break;

                            case "loan calculator":
                            case "loan calc":
                                await dc.BeginDialogAsync(MainDialogId);
                                break;

                            case NoneIntent:
                            default:

                                await dc.Context.SendActivityAsync("I didn't understand what you just said to me.");
                                break;
                        }

                        break;

                    case DialogTurnStatus.Waiting:
                        // The active dialog is waiting for a response from the user, so do nothing.
                        break;

                    case DialogTurnStatus.Complete:
                        await dc.EndDialogAsync();
                        break;

                    default:
                        await dc.CancelAllDialogsAsync();
                        break;
                }
            }
        }
        else if (activity.Type == ActivityTypes.ConversationUpdate)
        {
            if (activity.MembersAdded != null)
            {
                // Iterate over all new members added to the conversation.
                foreach (var member in activity.MembersAdded)
                {
                    // Greet anyone that was not the target (recipient) of this message.
                    // To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
                    if (member.Id != activity.Recipient.Id)
                    {
                        await SendWelcomeMessageAsync(turnContext, cancellationToken);
                    }
                }
            }
        }

        await _basicAccessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
        await _basicAccessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
    }

 private async Task<bool> IsTurnInterruptedAsync(DialogContext dc, string topIntent)
    {
        // See if there are any conversation interrupts we need to handle.
        if (topIntent.Equals(CancelIntent))
        {
            if (dc.ActiveDialog != null)
            {
                await dc.CancelAllDialogsAsync();
                await dc.Context.SendActivityAsync("Ok. I've canceled our last activity.");
            }
            else
            {
                await dc.Context.SendActivityAsync("I don't have anything to cancel.");
            }

            return true;        // Handled the interrupt.
        }

        if (topIntent.Equals(HelpIntent))
        {
            await dc.Context.SendActivityAsync("Let me try to provide some help.");
            await dc.Context.SendActivityAsync("I understand greetings, being asked for help, or being asked to cancel what I am doing.");
            if (dc.ActiveDialog != null)
            {
                await dc.RepromptDialogAsync();
            }

            return true;        // Handled the interrupt.
        }
user10860402
  • 754
  • 4
  • 22

1 Answers1

0

This seems like something of a design dilemma. My recommendation is to rearrange your bot's priorities so that LUIS is invoked only if your dialog hasn't been properly responded to. This could be accomplished by moving IsTurnInterruptedAsync from OnTurnAsync to a validator method. The validator method can be passed as an argument to your prompt's constructor:

_dialogs.Add(new ConfirmPrompt(CONFIRMPROMPT, ValidateAsync));

You'll want to make sure your top intent can be accessed from anywhere in case your prompt is in a different class. You can do this by adding the top intent to your turn state, which is state that only lasts for the scope of a single turn. Include this code in OnTurnAsync:

var topScoringIntent = luisResults?.GetTopScoringIntent();
var topIntent = topScoringIntent.Value.intent;

// Include this:
turnContext.TurnState.Add("topIntent", topIntent);

Your ValidateAsync method can look like this:

private async Task<bool> ValidateAsync(PromptValidatorContext<bool> promptContext, CancellationToken cancellationToken)
{
    if (promptContext.Recognized.Succeeded)
    {
        return true;
    }

    await IsTurnInterruptedAsync(promptContext.Context);

    return false;
}

Since IsTurnInterruptedAsync might be in a different class now, it will need to access the top intent through your turn state. If _dialogs is out of scope, you could add that to your turn state as well, or just make it a public/static property.

private async Task<bool> IsTurnInterruptedAsync(ITurnContext turnContext)
{
    var dc = await _dialogs.CreateContextAsync(turnContext);
    var topIntent = turnContext.TurnState.Get<string>("topIntent");

    // See if there are any conversation interrupts we need to handle.
    if (topIntent.Equals(CancelIntent))
    {
        // . . .

This way, an interruption would only happen if the utterance isn't valid for the dialog.

You might want to allow interruptions even when an utterance is valid for the dialog. Anything is valid for a text prompt, of course, but you still might not want to allow a user to type "What do I do?" as their name. If you want to send the utterance to LUIS first for certain types of dialogs and send the utterance to the dialog first for other types of dialogs, you can check the type of the dialog that's on top of the stack like this:

var activeDialog = _dialogs.Find(dc.ActiveDialog.Id);
if (activeDialog is ConfirmPrompt || activeDialog is ChoicePrompt)
Kyle Delaney
  • 9,521
  • 4
  • 27
  • 49
  • Thank you sir i will try this as soon as i get home. Thank you. – user10860402 Feb 23 '19 at 11:45
  • Sir can you explain the "turnContext.TurnState.Add("topIntent", topIntent) and "var topIntent = turnContext.TurnState.Get("topIntent")" and also turncontext, waterfall stepcontext, prompt context and turnstate? I want to understand them more if its okay. – user10860402 Feb 24 '19 at 09:36
  • And sir what if the prompt is in another class dialog. How can i call those codes you add up? – user10860402 Feb 24 '19 at 10:17
  • Sir how about this question. https://stackoverflow.com/questions/54885861/stackoverflow-exception-in-a-simple-dialog-in-botframework-v4 – user10860402 Feb 26 '19 at 12:43
  • It looks like it's being taken care of – Kyle Delaney Feb 26 '19 at 19:02
  • I did not understand his explation so much though – user10860402 Feb 26 '19 at 20:34
  • You have two answers now and you accepted one of them – Kyle Delaney Feb 27 '19 at 17:56
  • Sir @kyle can i ask about webview on messenger? My understanding is it just normal web page viewed on messenger with your bot thru a link the bot gave. But when i see tutorials on internet the steps are very complicated. I am giving a link now thru my bot and being displayed on messenger so i thought i can already do webview. – user10860402 Feb 28 '19 at 07:02
  • Do some research on your own and if you get stuck for more than two hours go ahead and ask a new question – Kyle Delaney Feb 28 '19 at 19:30
  • Okay sir but my problem now is new luis subscription. I tried everything. Here is the question https://stackoverflow.com/questions/54941992/question-about-changing-to-a-new-luis-key-with-botframework-v4 – user10860402 Mar 02 '19 at 09:35
  • It looks like that question is being taken care of as well – Kyle Delaney Mar 04 '19 at 20:44
  • I can't make it work until now sir. I did everything i think I'm still missing one step. – user10860402 Mar 04 '19 at 22:48
  • The LUIS sir. It says "Operation returned invalid status code Forbidden." – user10860402 Mar 05 '19 at 20:54
  • "Forbidden" means you should check your app ID, app password, LUIS keys, etc. That has nothing to do with this question and you should ask a new question about it if you still need help. – Kyle Delaney Mar 05 '19 at 20:57