How to call LUIS Dialog inside LUIS Dialog?
Asked Answered
N

2

7

My bot has LUIS dialog with a couple of intents. I call LUIS dialog from my MessageController. If the intent is detected, I start a child dialog. When the child dialog is done, I call context.Done("response from user"). After that ChlildDialogDone task is called.

Inside ChildDialogDone task I want to call the LUIS dialog again to detect the intent of user's message (which comes to ChildDialogDone ). Now inside ChildDialogDone I have context.Wait(MessageReceived). When this line of code is executed, nothing happens, my bot is waiting for the next message from user.

Here is the code:

    [Serializable]
        public partial class DefiningIntentDialog : LuisDialog<object>
        {

            [LuisIntent("")]
            public async Task NoIntent(IDialogContext context, LuisResult result)
            {        
                var dialog = new GreetingsDialog();
                dialog.InitialMessage = result.Query;
                context.Call(dialog, GreetingDialogDone);      
            }

            [LuisIntent("Email")]
            public virtual async Task ConfirmationEmail(IDialogContext context, LuisResult result)
            {
                await context.Forward(new EmailDialog, EmailDialogDone, "message", CancellationToken.None);
            }

            private async Task EmailDialogDone(IDialogContext context, IAwaitable<string> argument)
            {
                var messageHandled = await argument;

                context.Wait(MessageReceived);
            }
      }

So inside EmailDialogDone I have some message from user and I want to execute DefiningIntent dialog with this message again. How can I do it?

Neille answered 29/9, 2016 at 23:24 Comment(1)
As a wild guess and I may be wrong, it looks to me that the Done code should reside in another class or dialog class, and I would just call the API endpoint directly to get the intent, parsing the result manually. I'm probably wrong though.Outguard
C
4

You could repeat the logic that is on the MessegaReceived of the LUIS dialog, to achieve what you want to do. Basically, this code should be pretty aligned to what you need:

var tasks = this.services.Select(s => s.QueryAsync(messageHandled, CancellationToken.None)).ToArray();
var winner = this.BestResultFrom(await Task.WhenAll(tasks));

IntentActivityHandler handler = null;
if (winner == null || !this.handlerByIntent.TryGetValue(winner.BestIntent.Intent, out handler))
{
    handler = this.handlerByIntent[string.Empty];
}

if (handler != null)
{
    await handler(context, null, winner?.Result);
}

The pieces of the code that are referring to things with "this" are part of the LUIS Dialog you are inherited from.

  • services, are the collection of LuisServices instantiated based on your LuisModel attributes.
  • IntentActivityHandler is the handler that the LuisIntent decorated method are "implementing" with the method signature.
  • handlerbyIntent is a Dictionary with the key being the intent names of your dialog and the handler being the method that needs to be called for that intent.

Check this for more details and make sure you are using the latest version of the BotBuilder SDK (at the moment of this post: v3.2.1)

Cadet answered 30/9, 2016 at 17:14 Comment(17)
Thanks @ejadib . Could you explain what are 'services' and 'IntentActivityHandler ' mean in your code?Neille
I just added the details in the post. Does this worked for you? If so, please accept the answer.Cadet
but I don't understand from where do I need to execute the code you provided? Should I create a MessageReceived Task inside inside LUIS Dialog? I don't have this method there.Neille
Put the code on the EmailDialogDone and remove the context.Wait from there.Cadet
When I put it to my code inside EmailDialogDone I see 'services' and 'IntentActivityHandler' are undeclared: postimg.org/image/8414f8f33Neille
What version of the BotBuilder SDK are you using? Make sure to update the nuget to the latest version. You might be missing a namespace also.Cadet
That was the trick. I had the SDK 3.0.0. I've updated to 3.2.1 and now 'this.services' and 'IntentActivityHandler' are accessible. But there is another problem: messageHandled and item arguments can't be used. I see the error: 1) for messageHandled : cannot convert from string to System.Uri 2) for item: cannot convert from Microsoft.BotBuilder.Dialogs.IAwaitable<string> to Microsoft.BotBuilder.Dialogs.IAwaitable<Microsoft.Bot.Connector.IMessageActivity>. Probably because MessageReceived and EmailDialogDone has different signatures: postimg.org/image/jtqxticqtNeille
The messageHandled issue is odd. The queryAsync method expects and string and the messageHandled it's an string so that shouldn't happen. The item issue is valid and I forgot about it. In your scenario, I would pass null: await handler(context, null, winner?.Result);Cadet
If I go to QueryAsync definition, I see the following: s21.postimg.org/lc9s53hcn/Capture.png . I need to have uri but instead I have a string.Neille
There is an extension method that receives an string!. Make sure you have the correct usings github.com/Microsoft/BotBuilder/blob/…Cadet
Thank you, I passed null. When I try to test my bot I see the exception: "Exception: System.NullReferenceException: Object reference not set to an instance of an object.\r\n at T1Bot.Dialogs.Intents.DefiningIntentDialog.<EmailDialogDone>d__12.MoveNext()Neille
And my bot crashed at this point: if (winner == null || !this.handlerByIntent.TryGetValue(winner.BestIntent.Intent, out handler)) { handler = this.handlerByIntent[string.Empty]; }Neille
When I debug I have some winner for intent but still have that exceptionNeille
Is BestIntent or BestIntent.Intent null? I just tried myself on a solution and it's working fine.Cadet
Winner.BestIntent.Intent and winner.BestIntent are not null, winner.result is not null. this.handlerByIntent is a null, handler is also a nullNeille
Mmmm it seems the handlerByItem was not generated... weird as it should be generated as soon as you enter to any of your LuisIntent methods. Try checking if the handlerByIntent is null; if it's call this.GetHandlersByIntent() and assign the result to this.handlerByIntent ==> if (this.handlerByIntent == null) { this.handlerByIntent = new Dictionary<string, IntentActivityHandler>(GetHandlersByIntent()); }Cadet
Finally, it works!! this.handlerByIntent was null. I added if (this.handlerByIntent == null) { this.handlerByIntent = new Dictionary<string, IntentActivityHandler>(GetHandlersByIntent()); } and now everything is working. LUIS model has been called again! Thank you a lot! I appreciate your help!Neille
F
1

There's no need to copy logic from MessegaReceived. You can just call MessegaReceived:

private async Task EmailDialogDone(IDialogContext context, IAwaitable<string> argument)
{
   await MessageReceived(context, new AwaitableFromItem<IMessageActivity>((IMessageActivity)context.Activity));
}
Felder answered 26/7, 2017 at 23:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.