Cris
Cris

Reputation: 557

LUIS / Bot Framework multiple dialog, move intent handling to another dialog

my goal is to implement both dialogs and LUIS into a Microsoft Bot Framework application using their C# SDK. I tried to follow this thread https://github.com/Microsoft/BotBuilder/issues/127 and their related posts (referenced at the end) but couldn't get my code working in practice. This is my RootDialog class. Note that I created a method which handles the "GetProduct" intent, when it gets this intent, it should forward the LuisResult to ProductsDialog using context.Forward() method, but instead what I see is just that it goes straight to the ResumeAfter method, ProductsDialogCompleted. Now, here is probably where I fail but I couldn't find an example showing multiple LUIS dialogs.

public class RootDialog : LuisDialog<object>
{
    [LuisIntent("GetProduct")]
    private async Task GetProduct(IDialogContext context, LuisResult result)
    {
        await context.PostAsync("Calling ProductsDialog...");
        await context.Forward(Chain.From(() => new ProductsDialog()), ProductsDialogCompleted, context.Activity, CancellationToken.None);
    }

    private async Task ProductsDialogCompleted(IDialogContext context, IAwaitable<object> result)
    {
        var res = await result;
        context.PostAsync("ProductsDialogCompleted" + result);
        context.Wait(this.MessageReceived);
    }
}
public class ProductsDialog : LuisDialog<object>
{
    public async Task StartAsync(IDialogContext context)
    {
        await context.PostAsync("Entered ProductsDialog");

        context.Wait(this.MessageReceived);
    }
    [LuisIntent("None")]
    private async Task None(IDialogContext context, LuisResult result)
    {
        context.Done(true);
    }
}

The expected behavior is the following

  1. The user triggers the GetProduct intent
  2. The bot creates a new dialog and goes to the StartAsync method, where it waits for another user input
  3. The user triggers the None intent
  4. The dialog closes, returns true and triggers the ProductsDialogCompleted.

It seems like I'm not correctly binding the dialogs. How can I solve this?

EDIT: Added MessageController, version is 3.8.1

[BotAuthentication]
public class MessagesController : ApiController
{
    /// <summary>
    /// POST: api/Messages
    /// Receive a message from a user and reply to it
    /// </summary>
    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        if (activity.Type == ActivityTypes.Message)
        {
            await Conversation.SendAsync(activity, () => new RootDialog());
        }
        else
        {
            HandleSystemMessage(activity);
        }
        var response = Request.CreateResponse(HttpStatusCode.OK);
        return response;
    }

    private Activity HandleSystemMessage(Activity message)
    {
        if (message.Type == ActivityTypes.DeleteUserData)
        {
            // Implement user deletion here
            // If we handle user deletion, return a real message
        }
        else if (message.Type == ActivityTypes.ConversationUpdate)
        {
            // Handle conversation state changes, like members being added and removed
            // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
            // Not available in all channels
        }
        else if (message.Type == ActivityTypes.ContactRelationUpdate)
        {
            // Handle add/remove from contact lists
            // Activity.From + Activity.Action represent what happened
        }
        else if (message.Type == ActivityTypes.Typing)
        {
            // Handle knowing tha the user is typing
        }
        else if (message.Type == ActivityTypes.Ping)
        {
        }

        return null;
    }
}

Upvotes: 2

Views: 2202

Answers (2)

Roy
Roy

Reputation: 69

I had this same question, but I am using a newer version of Bot Framework, more specifically, V4.

Here is what I found:

  • BeginDialogAsync's options parameter takes an array of objects that will then be accessible in your dialog.
// Get skill LUIS model from configuration.
localizedServices.LuisServices.TryGetValue("MySkill", out var luisService);

if (luisService != null)
{
    // Get the Luis result. 
    var result = innerDc.Context.TurnState.Get<MySkillLuis>(StateProperties.SkillLuisResult);
    var intent = result?.TopIntent().intent;

    // Behavior switched on intent. 
    switch (intent)
    {
        case MySkillLuis.Intent.MyIntent:
            {
                // result is passed on to my dialog through the Options parameter. 
                await innerDc.BeginDialogAsync(_myDialog.Id, result);
                break;
            }

        case MySkillLuis.Intent.None:
        default:
            {
                // intent was identified but not yet implemented
                await innerDc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("UnsupportedMessage"));
                break;
            }
    }
}

From our second dialog, we can access the object through the context and perform any casting, etc. as necessary. In my case, it was a waterfall dialog, so I used stepContext.options.

Upvotes: 0

Ezequiel Jadib
Ezequiel Jadib

Reputation: 14787

Try removing the Chain.From(() from the context.Forward call. Not sure why you are adding it, but it shouldn't be there at all.

Try with:

await context.Forward(new ProductsDialog(), ProductsDialogCompleted, context.Activity, CancellationToken.None);

And btw, if the message you forward hits the None intent the ProductsDialogCompleted method will be hit because you are doing context.Done, which basically ends the ProductsDialog.

Also, have in mind the StartAsync method is present in the LuisDialog<T> base class, so you need to add the override keyword.

Upvotes: 4

Related Questions