dialogs in botframework using LUIS.ai - node.js

I have a piece of code that works perfectly on my local bot, and weird when integrated with LUIS. It's a simple dialogue that redirects to another dialog:
// Modelo de datos cargado en luis.ai
var recognizer = new builder.LuisRecognizer(process.env.LUIS_MODEL_URL);
bot.recognizer(recognizer);
//first intent, greeting intent to say hi to the user
bot.dialog('greetings', [
function (session) {
var greetings = ["¡Hola!", "Bonjour amigo!", "zdravstvuyte! (Así se dice hola en ruso)"];
var pickAGreeting = function () {
var rnd_greeting = greetings[Math.floor(Math.random() * 4)];
return rnd_greeting;
};
session.send(pickAGreeting(), session.message.text);
builder.Prompts.text(session, 'What can I do for you about Office?');
},
function (session, results) {
var user_response = results.response;
session.beginDialog('getProductoOffice', user_response);
}
]).triggerAction({
matches: 'greetings',
onInterrupted: function (session) {
session.send('Can I help you with something?');
}
});
bot.dialog('getProductoOffice', [
function (session, args) {
session.send('Welcome to O365 help!', session.message.text);
...
And the bot does this:
[me]: Hola
[bot]: ¡Hola!
[bot]: What can I do for you about Office?
[me]: Tell me about Skype
[bot]: Can I help you with something?
The weird thing is that after prompting me about Office, it never enters the "function (session, results)" and goes directly into the interrupted dialogue code.
This piece of code works perfectly without LUIS integration and moves correctly between dialogues.

The dialog was interrupted every time I entered an utterance recognized by LUIS because of the triggerAction behavior.
To disable the recognizer when a task is running I had to use the method onEnabled in the recognizer as follows:
var recognizer = new builder.LuisRecognizer('<model>').onEnabled(function (context, callback) {
var enabled = context.dialogStack().length == 0;
callback(null, enabled);
});
This won't interrupt the dialog stack and the recognizer will work only when there is no conversation running.

Related

How to tell Alexa to jump to a specific intent from LaunchRequest based on user input

I am quite new in Alexa development so please excuse my ignorance. The Alexa skill I am developing requires the following:
Users will awake the skill along with a question, e.g.
Alexa, ask marketing platform about result of last campaign
I am referring to https://developer.amazon.com/docs/custom-skills/understanding-how-users-invoke-custom-skills.html#cert-invoke-specific-request but not quite understand how to jump to a specific intent from LaunchRequest.
Where marketing platform is the skill invocation and result of last campaign is the utterance for skill intent named CampaignIntent.
There are more intents like this, which I want to call based on user's question, e.g.
Alexa, ask marketing platform to give me messaging details
I am using Lambda for the skill. At the moment it looks like the following:
exports.handler = (event, context, callback) => {
try {
if (event.request.type === 'LaunchRequest') {
var welcomeMessage = '<speak>';
welcomeMessage = welcomeMessage + 'Welcome to XYZ agency.';
welcomeMessage = welcomeMessage + '</speak>';
callback(null, buildResponse(welcomeMessage, false));
//How can I tell Alexa to jump to CampaignIntent?
}
else if (event.request.type === 'IntentRequest') {
const intentName = event.request.intent.name;
if (intentName === 'CampaignIntent') {
var ssmlConfirm = "<speak>";
ssmlConfirm = ssmlConfirm + 'Hello Auto.';
ssmlConfirm = ssmlConfirm + "</speak>";
callback(null, buildResponse(ssmlConfirm, true));
}
}
}
catch (e) {
context.fail(`Exception: ${e}`);
}
};
function buildResponse(response, shouldEndSession) {
return {
version: '1.0',
response: {
outputSpeech: {
type: 'SSML',
ssml: response,
},
shouldEndSession: shouldEndSession,
},
sessionAttributes: {},
};
}
CampaignIntent does not have any slot. It simply fetches records from a third party platform API.
I also referred https://stackoverflow.com/a/48032367/1496518 but did not understand how to achieve ...has a WHEN slot to elicit part.
The documentation you linked say, "Users can combine your invocation name with an action, command or question. This sends the service for your skill an IntentRequest with the specific intent that corresponds to the user's request."
If a user invokes your skill in this way, the intent you find in the first request of that user's session will be CampaignIntent (the IntentRequest you've defined) instead of LaunchRequest. There isn't any "jumping" you need to do on your end. The behavior will be the same with or without slot values.

disable qna recognizer when inside a dialog

I am using Luis and QnA maker, qna maker is now interupting a waterfall prompt. I have disabled the Luis prompt with code below, how can I to do same for the qna recognizer?
var recognizer = new
builder.LuisRecognizer(LuisModelUrl).onEnabled(function (context,
callback) {
var enabled = context.dialogStack().length == 0;
callback(null, enabled);
});
bot.recognizer(recognizer);
bot.recognizer(qnaRecognizer);
console.log(recognizer);
eg: What part of the toilet is broken? (1. Cistern, 2. Pipe, or 3. Seat)
Anything except an exact match gets picked up by qna sentiment which replaces the dialog stack
Thanks
You shouldn't have to disable either one for the code to work. I suspect the problem is in your dialog flow. Below is an example of how to construct the dialog portion of your bot. When I ran this, the logger middleware shows QnA is matching on the inputs I feed, but the bot is dictating the conversation because of the code.
var luisrecognizer = new builder.LuisRecognizer(LuisModelUrl);
var qnarecognizer = new cognitiveservices.QnAMakerRecognizer({
knowledgeBaseId: process.env.QnAKnowledgebaseId,
authKey: process.env.QnAAuthKey || process.env.QnASubscriptionKey,
endpointHostName: process.env.QnAEndpointHostName
});
var basicQnAMakerDialog = new cognitiveservices.QnAMakerDialog({
recognizers: [qnarecognizer],
defaultMessage: 'No match! Try changing the query terms!',
qnaThreshold: 0.3
});
bot.recognizer(luisrecognizer);
bot.recognizer(basicQnAMakerDialog);
bot.dialog('/', basicQnAMakerDialog);
bot.dialog('GreetingDialog',[
(session) => {
session.send('You reached the Greeting intent. You said \'%s\'.',
session.message.text);
builder.Prompts.text(session, "What is your name?");
},
(session, results) => {
session.userData.name = results.response;
session.send("Glad you could make it, " + session.userData.name);
builder.Prompts.text(session, "Ask me something!");
},
(session, results) => {
session.conversationData.question = results.response;
session.send(session.conversationData.question + " is an interesting topic!")
session.endDialog();
}
]).triggerAction({
matches: 'Greeting'
})
In the following image, LUIS brings me to the Greeting intent when I type "I'm happy [to be here]" which I have trained in the LUIS app. The bot dialog takes over asking me questions and storing the answers. Even though I'm making statements that QnA or LUIS should respond to neither is doing so. The conversation follows the code.
Had Qna taken over it would have responded to "What are you" with some text about QnA Maker. Similarly, "help" would have produced responses from either QnA or LUIS as I have topics/intents for both, respectively.

How do I handle "cancel" intent when already in a dialog?

In Microsoft Bot Framework, I already started a conversation and have some intent running, let's say "login", but then while I ask the user for username or password, he might say "cancel that" or "cancel login", how do I get this intent: "cancel" and how do I handle it by cancelling out of the current dialog?
// Login Dialog
bot.dialog('login', [
(session:Builder.Session) => {
Builder.Prompts.text(session, 'What\'s your account email?');
},
(session:Builder.Session, results: any) => {
session.dialogData.email = results.response;
Builder.Prompts.text(session, 'What\'s your password?');
},
(session:Builder.Session, results: any) => {
session.dialogData.passWord = results.response;
CheckAccountLogin(session, session.dialogData.email,session.dialogData.passWord)
.then((result:boolean)=>{
if(result === true){
session.send('Login Successful');
session.userData.loginSuccessful = true;
session.userData.email = session.dialogData.email;
}else{
session.userData.loginSuccessful = false;
}
session.endDialog();
});
},
]);
Refer to this article: https://learn.microsoft.com/en-us/azure/bot-service/nodejs/bot-builder-nodejs-recognize-intent-messages?view=azure-bot-service-3.0
You can basically register a CancelIntent recognizer that will cause the conversation to forwarded the dialog you want then triggered. For example:
bot.recognizer(new builder.RegExpRecognizer(
"CancelIntent",
{ en_us: /^(cancel|nevermind)/i, ja_jp: /^(キャンセル)/ })
);
This uses a regular expression to detect when the user wants to cancel, and one way to cancel the current dialog would be just to end the conversation:
bot.dialog('CancelDialog', function (session) {
session.endConversation("Ok, cancelling loggin.");
}).triggerAction({ matches: 'CancelIntent' });
Notice that the code above tells the bot framework to end the conversation, so the login dialog will end thus cancelling the action.
Here are some useful samples too: https://github.com/Microsoft/BotBuilder-Samples/blob/master/Node/intelligence-LUIS/app.js
Edit
To set up a Luis intent recognizer you first need to remove the previously registered RegexRecognizer and then create a new instance of the LuisRecognizer by doing:
// Make sure you add code to validate these fields
var luisAppId = process.env.LuisAppId;
var luisAPIKey = process.env.LuisAPIKey;
var luisAPIHostName = process.env.LuisAPIHostName || 'westus.api.cognitive.microsoft.com';
const LuisModelUrl = 'https://' + luisAPIHostName + '/luis/v2.0/apps/' + luisAppId + '?subscription-key=' + luisAPIKey;
// Create a recognizer that gets intents from LUIS, and add it to the bot
var recognizer = new builder.LuisRecognizer(LuisModelUrl);
And then register the Luis recognizer with:
bot.recognizer(recognizer);
Your CancelDialog should look the same, it will match the intent with the triggerAction.matches:
bot.dialog('CancelDialog', function (session) {
session.endConversation("Ok, cancelling loggin.");
}).triggerAction({ matches: 'CancelIntent' });
Once you have that setup create the CancelIntent in your Luis App, add some sample utterances to it (at least three) and the conversation should automatically forward to the CancelDialog once the CancelIntent is detected.
There's more info about that here: https://learn.microsoft.com/en-us/azure/bot-service/nodejs/bot-builder-nodejs-recognize-intent-luis?view=azure-bot-service-3.0
Another way to do it is to use the built in cancel event. Luis would still work. This is based on Javier's answer:
// Setup Luis Recognizer first:
const LuisModelUrl = 'https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/' + LuisID + '?subscription-key=' + LuisKey;
var recognizer = new Builder.LuisRecognizer(LuisModelUrl);
bot.recognizer(recognizer);
// Dialog definition
bot.dialog('login', [
(session:Builder.Session) => {
// Your dialog stuff here
}
]).cancelAction(
"Cancel", "What would you like to do next?",
{
matches: "Cancel",
confirmPrompt: "This will cancel your dialog. Are you ure?"
}
);

Use multiple QnA services in one bot

I have multiple QnA services running, each with it's own knowledgeBaseId and subscriptionKey. I want to use them both in a single chatbot and I've written a piece of code which takes the user input and assigns the correct Knowledgebase details. However, I'm unable to get the 2nd QnA service to become active and the bot is linking only to the first service. What might be going wrong here?
Sample code:
var knowledgeId = "FIRST_QNA_SERVICE_KNOWLEDGE_ID";
var subscriptionId = "FIRST_QNA_SERVICE_SUBSCRIPTION_ID";
var bot = new builder.UniversalBot(connector);
var qnarecognizer = new cognitiveservices.QnAMakerRecognizer({
knowledgeBaseId: knowledgeId,
subscriptionKey: subscriptionId,
qnaThreshold:0.3,
top:1});
var intentrecognizer = new builder.IntentDialog();
var intents = new builder.IntentDialog({ recognizers: [intentrecognizer, qnarecognizer] });
bot.dialog('/', intents);
intents.matches('qna', [
function (session, args, next) {
args.entities.forEach(function(element) {
session.send(element.entity);
}, this);
}
]);
intents.matchesAny([/hi/i, /main menu/i], [
function (session) {
builder.Prompts.choice(session, "Hi, What would you like to ask me about?", ["Service1","Service2"],{ listStyle: builder.ListStyle.button});
},
function (session, result) {
var selection = result.response.entity;
switch (selection) {
case "Service1":
knowledgeId = "FIRST_QNA_SERVICE_KNOWLEDGE_ID";
subscriptionId = "FIRST_QNA_SERVICE_SUBSCRIPTION_ID";
session.send('You can now ask me anything about '+selection+'. Type \'main menu\' anytime to choose a different topic.')
session.endConversation();
return
case "Service2":
knowledgeId = "SECOND_QNA_SERVICE_KNOWLEDGE_ID";
subscriptionId = "SECOND_QNA_SERVICE_SUBSCRIPTION_ID";
session.send('You can now ask me anything about '+selection+'. Type \'main menu\' anytime to choose a different topic.')
session.endConversation();
return
}
}
])
You never updated the knowledgeId/subscriptionId on the QnAMakerRecognizer... that's the reason you are always seeing the first QnA service being called.
See if there is a way to update those values in the QnAMakerRecognizer.
Check out if my answer is valid to your purpose on this question:
Multiple QnA Maker services for a single bot
Hope you find it useful.

How can I integrate my MS text bot program written in NodeJS to skype bot?

I'd like to develop a skype bot that would take user name as input and say hello username in the opposite char case based on the user input. In brief, if the user types his name as james, my bot would respond to him as Hello JAMES. The program runs fine, however I am finding it ambiguous to integrate my textbot program to skype bot.
Here's my code:
var builder = require('botbuilder');
var helloBot = new builder.TextBot();
helloBot.add('/', [
function (session, args, next) {
if (!session.userData.name) {
session.beginDialog('/profile');
} else {
next();
}
},
function (session, results) {
session.send('Hello %s!', session.userData.name);
}
]);
helloBot.add('/profile', [
function (session) {
builder.Prompts.text(session, 'Hi! What is your name?');
},
function (session, results) {
if(results.response == results.response.toUpperCase())
{
//console.log("in if");
session.userData.name = results.response.toLowerCase();
}
else
{
//console.log("else");
session.userData.name = results.response.toUpperCase();
}
session.endDialog();
}
]);
console.log("Hi!");
helloBot.listenStdin();
The output would be like :
bot : Hi
user: Hello.
bot : What is your name?
user: james.
bot : Hello JAMES.
To create a chat bot compatible with Skype, use the UniversalBot type instead of TextBot. You can find sample code that demonstrates how to send different card types in BotBuilder-Samples/Node/cards-RichCards.
To configure your bot to work with Skype, login to the Bot Portal at https://dev.botframework.com and register your bot. After your bot is registered, go to 'My bots', click on your bot name, and you will see the 'Channels' section with Skype and WebChat enabled by default. Under 'Test link', click the 'Add to Skype' button. This will redirect you to the Skype website where it will ask you to confirm that you want to add the Skype bot to your Skype contacts.
For more information on Skype bots check out the Getting Started Guide.

Resources