2

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;
    }
}
Cris
  • 549
  • 5
  • 21

2 Answers2

4

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.

Ezequiel Jadib
  • 14,767
  • 2
  • 38
  • 43
  • It's not working. I want ProductsDialog to be the active dialog so that it can catch the incoming messages just like RootDialog is doing when the program starts, so it should accept another message triggering none before going back to RootDialog, I can't understand how to enable this behavior. – Cris Jun 19 '17 at 08:27
  • Replace the context.Done with context.Wait in the None method . context.Done ends the products dialog. – Ezequiel Jadib Jun 19 '17 at 09:37
  • Yes, I did that when you told me but it's not working anyway, I tried to put also a context.PostAsync before the context.Wait but it didn't return a message. – Cris Jun 19 '17 at 10:05
  • Show the MessageController please. These are just dialogs. What version of bot builder are u using? – Ezequiel Jadib Jun 19 '17 at 10:12
  • Also, the StartAsync method is part of the base LuisDialog class. You should add the override keyword on it. – Ezequiel Jadib Jun 19 '17 at 10:14
  • Ok now it enters the StartAsync method but still not handling intents inside of ProductsDialog, also updated question – Cris Jun 19 '17 at 10:21
  • So you removed the chain, the. Done and you are still hitting your ProductsDialogCompleted method? Can you share the whole solution? Github? – Ezequiel Jadib Jun 19 '17 at 10:37
  • I found it! I was removing the keys before publishing the code and then I noticed that I didn't specify the auth credentials in ProductsDialog. – Cris Jun 19 '17 at 10:44
  • Ok. Anyways all the other changes I pointed out are required. Pleas mark it as answered and upvote. – Ezequiel Jadib Jun 19 '17 at 10:47
  • was this solved? I've tried to use what is suggested here but it hits the callback as soon it hits start async – user6083088 Jul 18 '17 at 20:55
  • @Cris I've been experiencing the same problem, but no avail. Can you please tell me how you solved it. I have no problems with keys as its working locally and LUIS ID & Key are all correct in code in both Dialog classes. – user6083088 Jul 24 '17 at 15:11
  • Difficult to say without seeing any code, try posting a new question and send me the link so I can take a look – Cris Jul 24 '17 at 16:06
0

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.

Roy
  • 69
  • 7