Botframework-sdk: [LUIS] How could call Luis inside a new dialog?

Created on 11 May 2017  路  14Comments  路  Source: microsoft/botframework-sdk

System Information (Required)

  • SDK language (Node)
  • *Development environment - localhost *
  • Node.js + npm version
    6.9.5 + 4.5.0

Issue Description

How could call Luis inside a sub dialog?
I have a app.js which will match the intents of the user's request, then it will begin a new dialog while matched. however in the new dialog, it will ask user more questions, and I need LUIS to match and recognize the user's inputting on the new dialog, how could i do that?

Example Code

// this code inside app.js, it will match the intent of the keyword when user want to search something.

intents.matches('search', [
    function(session,result){
        session.beginDialog('procgosh:GoShDialog', session.message.text);
        },
    function(session,result){
        session.send('Thanks for your information and do you have any other request?');
    }    
]);

// when it match the search, it will redirect to GoShDialog (procgosh.js), and its a new waterfall question dialog, and I need same Luis to match and recognize more user's inputting on that dialog, how could i do?

Most helpful comment

So I've been asked before how to build a prompt based on a LUIS model and with v3.8 that we released this week it's now easier then ever. Here's how to use the new prompt customization system to build a custom LUIS prompt:

// Create a recognizer for your LUIS model
var recognizer = new builder.LuisRecognizer('<model>');

// Create a custom prompt
var prompt = new builder.Prompt({ defaultRetryPrompt: "I'm sorry. I didn't recognize your search." })
    .onRecognize(function (context, callback) {
        // Call prompts recognizer
        recognizer.recognize(context, function (err, result) {
            // If the intent returned isn't the 'None' intent return it
            // as the prompts response.
            if (result && result.intent !== 'None') {
                callback(null, result.score, result);
            } else {
                callback(null, 0.0);
            }
        });
    });

// Add your prompt as a dialog to your bot
bot.dialog('myLuisPrompt', prompt);

// Add function for calling your prompt from anywhere
builder.Prompts.myLuisPrompt = function (session, prompt, options) {
    var args = options || {};
    args.prompt = prompt || options.prompt;
    session.beginDialog('myLuisPrompt', args);
}

Now to use your custom prompt you can call it like any other prompt:

bot.dialog('foo', [
     function (session) {
          builder.Prompts.myLuisPrompt(session, "Please say something I recognize");
     },
     function (session, results) {
          switch (results.response.intent) {
               case 'Bar':
                    break;
          }
     }
]);

Anytime the prompt recognizes an intent from your model (other than None) it will return the matched intent.

Is that what you're wanting?

All 14 comments

If you're redirecting from the first waterfall step, I would say to just send the args object from your 'search' intent. This object will have been ran through LUIS and have what you're looking for. I've rewritten parts of your code below.

intents.matches('search', [
    function(session, args) {                               // Convention has the second parameter in the initial waterfall step named 'args'
        session.beginDialog('procgosh:GoShDialog', args);   // If you send session.message.text, it'll just send the string, which isn't everything you're asking for
     },
    function(session, results){                             // Likewise, convention is to call it results, not result here.
        session.send('Thanks for your information and do you have any other request?');
        session.endDialog();                                // Even when testing, it is important to end your dialogs, otherwise you will be stuck here.
    }    
]);

So I've been asked before how to build a prompt based on a LUIS model and with v3.8 that we released this week it's now easier then ever. Here's how to use the new prompt customization system to build a custom LUIS prompt:

// Create a recognizer for your LUIS model
var recognizer = new builder.LuisRecognizer('<model>');

// Create a custom prompt
var prompt = new builder.Prompt({ defaultRetryPrompt: "I'm sorry. I didn't recognize your search." })
    .onRecognize(function (context, callback) {
        // Call prompts recognizer
        recognizer.recognize(context, function (err, result) {
            // If the intent returned isn't the 'None' intent return it
            // as the prompts response.
            if (result && result.intent !== 'None') {
                callback(null, result.score, result);
            } else {
                callback(null, 0.0);
            }
        });
    });

// Add your prompt as a dialog to your bot
bot.dialog('myLuisPrompt', prompt);

// Add function for calling your prompt from anywhere
builder.Prompts.myLuisPrompt = function (session, prompt, options) {
    var args = options || {};
    args.prompt = prompt || options.prompt;
    session.beginDialog('myLuisPrompt', args);
}

Now to use your custom prompt you can call it like any other prompt:

bot.dialog('foo', [
     function (session) {
          builder.Prompts.myLuisPrompt(session, "Please say something I recognize");
     },
     function (session, results) {
          switch (results.response.intent) {
               case 'Bar':
                    break;
          }
     }
]);

Anytime the prompt recognizes an intent from your model (other than None) it will return the matched intent.

Is that what you're wanting?

emm... actually, its not what I want...let me clarify again.

on App.js file, I used Luis to match the utterance and if it find the the entity belongs to Type, then begin a new dialog, like what I did in below,
`

intents.matches('search', [
    function(session, args, next) { 
        var request = builder.EntityRecognizer.findEntity(args.entities, 'TYPE'); 
        if (!request) {                    
            session.beginDialog('procgosh:GoShDialog', args);  
        }
        else{
            next({response:request.entity});
       }
     },
    function(session, results){                            
        session.send('Thanks for your information and do you have any other request?');
        session.endDialog();                               
    }    
]);

however, when I in procgosh.js file, I have a waterfall to ask user input more requirement fields, such as color, size, price etc..and there's one more filed is called "special", it allows user to input something, I want the procgosh.js is able to recognize the user's inputting to another intent of LUIS, right now, I have to use HTTP Request to query the Json file back and parse it, such as below, so I want to know if there's any way to reuse builder.EntityRecognizer.findEntity instead of below callback function. tkx.

// this code is on procgosh.js
                    var LUISURL = process.env.LUIS_MODEL_URL;
                    var query = results.response;
                    request(LUISURL + query, function(error, response, body) {
                        let jbody = JSON.parse(body);
                        console.log('---------request body-------------');
                        console.log(jbody.entities[0].type);
                    });  

`

@stevengum97 I tried to renew a new LUIS instance on the procgosh.js file, in one of waterfall steps, however this doesnt work as return Null, or will error to me with "module has been used" so, how can i use LUIS to recognize the result.response?

                    var request = builder.EntityRecognizer.findEntity(result.response, 'TYPE');
                    console.log('---------request body-------------');
                    console.log(request);

@wolf0x you can call LUIS manually using builder.LuisRecognizer.recognize() so something like:

```
builder.LuisRecognizer.recognize(session.message.text, '', function (err, intents, entities) {
if (entities) {
var entity = builder.EntityRecognizer.findEntity(entities, 'TYPE');
// do something with entity...
}
});

@Stevenic great and thanks, thats what I want.. tkx again.

Closing issue

So I'd thought I'd leave a comment here as I was in need of calling multiple LUIS models in dialog's. I just used a part of your custom prompt @Stevenic to use the session at the start of a dialog instead:

bot.dialog('foo', [
    function (session) {
        // As long as the message is being passed here with the session we can:
        recognizer.recognize(session, function (err, result) {
            console.log(result);
            if (result && result.intent !== 'None') {
                // Match
            } else {
                // No match
            }
        });
    }
]);

This way I get the matched intent too, as opposed to just getting and array of intents and entities by manually using builder.LuisRecognizer.recognize() as you mentioned above. 馃憤

ADDED:
Also, in my instance I wanted to store the original utterance so I can check it in a later dialog using the code from above.

I'm not sure if this is the best way but I stored the original message like so:

function (session, results) {
    // Store the utterance message object
    session.userData.utterance = {};
    session.userData.utterance.message = session.message;
}

Then used it in a later dialog:

function (session) {
    // On passing the 'session.userData.utterance'
    // the recognizer uses the stored message object
    recognizer.recognize(session.userData.utterance, function (err, result) {
        if (result && result.intent !== 'None') {
            // Match
        } else {
            // No match
        }
    });

},

I am facing similar problem
how to call luis dialog from general dialog

dialog.matches('morningUpdate',[    
    function (session, args, next) {
  }
]);

bot.dialog('/work', [
  function (session, args) {
    //how to call here "morningUpdate" dialog
  }
]);


I'm not sure your issue relates to this, would this not work in your case:

bot.dialog('/morningUpdate', [
    function (session, args, next) {
        // DO SOMETHING
    }
]).triggerAction({
    matches: 'morningUpdate'
});

bot.dialog('/work', [
    function (session, args) {
        // DO SOMETHING
    session.replaceDialog('/morningUpdate');
    // OR IF YOU WANT TO RETURN TO THIS DIALOG AFTERWARDS
    session.beginDialog('/morningUpdate');
    },
    ...
]);

https://github.com/Microsoft/BotBuilder/issues/2741#issuecomment-301888868

dialog.matches('morningUpdate', 'morningUpdate');

bot.dialog('morningUpdate', [    
    function (session, args, next) {

    }
]);

bot.dialog('/work', [
  function (session, args) {
    builder.LuisRecognizer.recognize(session.message.text, '<model url>', function (err, intents, entities) {
      var result = {};
      result.intents = intents;
      result.entities = entities;
      intents.forEach(function (intent) {
        if (intent.intent == 'morningUpdate') {
          session.beginDialog('morningUpdate', result);
        } else {
          // Do other stuff
        } 
      }
    });
  }
]);

@aakashkag Is this what you're asking for?

yes exactly . btw thanks so much

@wolf0x you can call LUIS manually using builder.LuisRecognizer.recognize() so something like:

Add this to the documentation please! Spent too many time to find this.

@akontsevich This is a closed issue. Please open a new issue for your request.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sebsylvester picture sebsylvester  路  3Comments

peterbozso picture peterbozso  路  3Comments

hailiang-wang picture hailiang-wang  路  3Comments

Vigneshramkumar picture Vigneshramkumar  路  3Comments

verdysh picture verdysh  路  3Comments