1

I'm building a bot connected to LUIS to get intents and with some dialogs that make other things.

I have the following dialogs:

  • MainDialog: that calls LUIS to get intents
  • DialogOne: with some steps
  • DialogTwo: with some steps
  • ScoreDialog: with a survey to get opinion from user after other dialogs. this dialog can be called from others.

MainDialog is the entry point and this is the code:

public class MainDialog : ComponentDialog
{
    public MainDialog() : base($"{nameof(MainDialog)}.main")
    {
        var waterfallSteps = new WaterfallStep[]
        {
            RedirectStepAsync
        };

        AddDialog(new WaterfallDialog($"{nameof(MainDialog)}.main", waterfallSteps));

        AddDialog(new DialogOne($"{nameof(MainDialog)}.dialogOne"));
        AddDialog(new DialogTwo($"{nameof(MainDialog)}.dialogTwo"));

        InitialDialogId = $"{nameof(MainDialog)}.main";
    }

    private async Task<DialogTurnResult> RedirectStepAsync(WaterfallStepContext waterfallStepContext, CancellationToken cancellationToken)
    {
        await waterfallStepContext.Context.SendActivityAsync("What you want?");

        var result = await _myLuisService.RecognizeAsync(waterfallStepContext.Context, cancellationToken);
         
        var topIntent = result.GetTopScoringIntent();

        switch (topIntent.intent)
        {
            case "None": break;
            case "IntentOne": 
                await waterfallStepContext.BeginDialogAsync($"{nameof(MainDialog)}.dialogOne", null, cancellationToken);
            break;
            case "IntentTwo": 
                await waterfallStepContext.BeginDialogAsync($"{nameof(MainDialog)}.dialogTwo", null, cancellationToken);
            break;
        }

        return await waterfallStepContext.EndDialogAsync(null, cancellationToken);
    }
}

Note that MainDialog only has one step that calls to LUIS in order to get user intent.

When user intent is received I switch to another specific dialog with 'BeginDialogAsync' method

For example, we are switching to DialogOne.

This is the code:

public class DialogOne : ComponentDialog
{
    public DialogOne(string dialogId) : base(dialogId)
    {
        var waterfallSteps = new WaterfallStep[]
        {
            StepOne,
            StepTwo,
            StepThree
        };

        AddDialog(new WaterfallDialog($"{nameof(DialogOne)}.main", waterfallSteps));

        AddDialog(new TextPrompt($"{nameof(ScoreDialog)}.score"));

        InitialDialogId = $"{nameof(DialogOne)}.main";
    }

    private async Task<DialogTurnResult> StepOne(WaterfallStepContext waterfallStepContext, CancellationToken cancellationToken)
    {
        // do something...
    }

    private async Task<DialogTurnResult> StepTwo(WaterfallStepContext waterfallStepContext, CancellationToken cancellationToken)
    {
        // do something...
    }

    private async Task<DialogTurnResult> StepThree(WaterfallStepContext waterfallStepContext, CancellationToken cancellationToken)
    {
        await waterfallStepContext.BeginDialogAsync($"{nameof(DialogOne)}.score", null, cancellationToken);
    }
}

This DialogOne contains some steps, and when I finish, I want to show a new one dialog to ask their opinion about the process. It's a voting process.

Note that I'm using

await waterfallStepContext.BeginDialogAsync($"{nameof(DialogOne)}.score", null, cancellationToken);

to call ScoreDialog.

Finally, the ScoreDialog prompt two options to vote and then, in the next steps we capture and process the response, we thank the user for their vote...

public class ScoreDialog : ComponentDialog
{
    public ScoreDialog(string dialogId) : base(dialogId)
    {
        var waterfallSteps = new WaterfallStep[]
        {
            AskForScoreStepAsync,
            GratitudeForScoreStepAsync
        };

        AddDialog(new WaterfallDialog($"{nameof(ScoreDialog)}.main", waterfallSteps));

        AddDialog(new TextPrompt($"{nameof(ScoreDialog)}.ask"));

        InitialDialogId = $"{nameof(ScoreDialog)}.main";
    }

    private async Task<DialogTurnResult> AskForScoreStepAsync(WaterfallStepContext waterfallStepContext, CancellationToken cancellationToken)
    {
        await SaveConversationAsync(waterfallStepContext, data);

        return await waterfallStepContext.PromptAsync(
            $"{nameof(ScoreDialog)}.ask",
            new PromptOptions()
            {
                Prompt = CreateScoreButtons()
            },
            cancellationToken);
    }

    private Activity CreateScoreButtons()
    {
        var reply = MessageFactory.Text(string.Empty);
        
        reply.SuggestedActions = new SuggestedActions()
        {
            Actions = new List<CardAction>()
            {
                new() { Title = "I like", Value = "I like", Type = ActionTypes.ImBack },
                new() { Title = "I dislike", Value = "I like", Type = ActionTypes.ImBack },
            }
        };

        return reply;
    }

    private async Task<DialogTurnResult> GratitudeForScoreStepAsync(WaterfallStepContext waterfallStepContext, CancellationToken cancellationToken)
    {
        await waterfallStepContext.Context.SendActivityAsync("Thanks for voting!");
        
        //TODO: do something with the vote

        return await waterfallStepContext.EndDialogAsync(null, cancellationToken);
    } 
}

But, the issue is when I redirect to ScoreDialog, this dialog only shows the first step (AskForScoreStepAsync), but does not continue to the last step (AskForScoreStepAsync).

What is does is return to MainDialog with the value "I like" or "I dislike" from previous step... this "I like" or "I dislike" text is passed again to LUIS with wrong result (because isn't a valid utterance)

So, what's wrong?

How can I redirect the control from dialog to another dialog, and on this new one dialog, complete all steps?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Sergio
  • 175
  • 5
  • 21

0 Answers0