How to add additional dialog in bot framework - node.js

How can I have 2 conversations going concurrently? I'm currently using TextBot and LuisDialog to build a bot. I start off by having a conversation with the user to obtain data. Then while doing some processing in a different method, I discover that I need additional information from the user. How can I create a new conversation with the user just to get that additional information? I have some code below that attempts to show what I want to do. Thanks for your suggestions.
File 1: foo.js
var dialog = new builder.LuisDialog(model);
var sonnyBot = new builder.TextBot();
sonnyBot.add('/', dialog);
dialog.on('intent_1', [
function(session, args, next) {
name = builder.Prompts.text(session,"What is your name?");
},
function(session, result) {
session.dialogData.name= results.response;
getFamilyTree(session.dialogData.name);
}
]);
File 2: getFamilyTree.js
function getFamilyTree(name) {
find family tree for name
if (need place of birth) {
begin new dialog
prompt user place of birth
get place of birth from user
end dialog
}
finish getting the family tree
}

i guess you could pass session object and then use that object to start a new dialog .
Edit 1
can't you use some thing like
session.beginDialog('/getFamilyTree',{name:result.response});
and then you can access name like
args.name
inside 'getFamilyTree' dialog

I posted the same question on GitHub and received the answer from Steven Ickman, who is involved in the development of the node.js SDK. The link to the answer is https://github.com/Microsoft/BotBuilder/issues/394#issuecomment-223127365

Related

Alexa voice with Jovo Framework: Is this possible to get IntentName by utterance? How can I decide which intent to jump to based on user utterance?

I have built an Alexa skill with the following flow:
LAUNCH -> AccountLinkingIntent -> CampaignIntent
In AccountLinkingIntent, presently I am routing to CampaignIntent if Account is already linked.
Up to this everything is working fine. Now I have to add another Intent ActiveContactIntent so that the flow becomes:
LAUNCH -> AccountLinkingIntent -> CampaignIntent / ActiveContactIntent
i.e, From AccountLinking I need to decide which Intent to route to.
The invocation goes like this (CampaignIntent):
Alexa, ask <invocation_name> to get my latest campaign result
OR (ActiveContactIntent)
Alexa, ask <invocation_name> who is my most active contact
Based on the utterance, I need to tell Alexa where to go. So far I have the following in AccountLinkingIntent
...
return this.toIntent("CampaignIntent");
...
But now I need to decide the same as this:
...
if( ... ) {
return this.toIntent("CampaignIntent");
} else {
return this.toIntent("ActiveContactIntent");
}
...
Is there any way to get the IntentName by the utterance so that I can check by the same such as:
...
if( intent_name_by_utterance === "CampaignIntent" ) {
return this.toIntent("CampaignIntent");
} else {
return this.toIntent("ActiveContactIntent");
}
...
Or probably, if it is possible to get intent_name_by_utterance I may also pass the value as the argument of toIntent method if it is allowed to pass a variable!
return this.toIntent(intent_name_by_utterance);
UPDATE:
I have tried the following to see whether the intent name is being returned:
LAUNCH() {
return this.toIntent("LinkAccountIntent");
},
async LinkAccountIntent() {
const intent_name = this.$request.getIntentName();
this.tell(Current intent is: ${intent_name});
},
Invoked the skill in following two fashions:
Alexa, ask <invocation-name> to give me my latest campaign results
Alexa, ask <invocation-name> who is my most active contact
give me my latest campaign results AND who is my most active contact are the utterances for respective intents.
I am using Alexa Test console for testing. In both cases, I was expecting the name of the intent (this.$request.getIntentName()), ending up with
Hmm, I don't know that one.
My intention is to call an intent by its utterance directly by waking up the skill using its invocation name.
Any suggestion?
I think you should treat both intends separately and not through the launch, because for example in the case
"Alexa, ask who is my most active contact"
Alexa skips the launch and jumps directly to resolve the intend ActiveContactIntent.
So just as you have the LAUNCH(){} function you must also have CampaignIntent(){} and ActiveContactIntent(){}.
That way you will avoid Alexa answering
Hmm, I don't know that one.
To verify that the user has already linked his account, you would have to enter a code like the one below:
if (!this.$request.getAccessToken()) {
this.$alexaSkill.showAccountLinkingCard();
this.tell('Please link you Account');
} else {
//code for your respective action
}
I recommend you to check the documentation about "routing with jovo" to have a little more clarity about this topic. You can review it at the following link:
https://www.jovo.tech/docs/routing

Cant trigger message after reloadaction

After I enter "reset", I does not received any message "Welcome Back" and restart the conversation.
bot.dialog('reset', [
function(session) {
session.send("Welcome Back");
session.beginDialog('/');
}]
)
.reloadAction('reset', 'Ok, starting over.', {
matches: /^reset/i,
});
The reloadAction only works for dialogs that have already been added to the stack. In order to use it correctly, the reloadAction needs to be attached to a dialog that has already been called/used previously.
For example, if a user is responding to a series of user profile questions across multiple dialogs (personal info, then address/previous address, followed by education) and the user types 'reset' while in the education dialog, then the reloadAction would trigger having been attached to the first user profile dialog (that collects personal info). Thus, the user would be brought back to the beginning of the user profile dialogs to start over.
If you are intent on calling a new dialog, then you will likely want to use triggerAction. This does clear the dialog stack entirely but also allows you to redirect back to dialog x. In this way, you achieve the same result as in your ask with just a little bit of extra code:
bot.dialog('reset', [
function(session, args, next) {
session.send("Ok, starting over.");
next();
},
function(session) {
session.send("Welcome Back");
session.beginDialog('/');
}]
).triggerAction({
matches: /^reset/i
});
Hope of help!

botframework choice invalid response typed

I am using nodejs SDK for creating my bot with MSFT botframework.
The code snippet is as follows:
function(session, args, next){
builder.Prompts.choice(session, "Please select one of the options:", ['AAA', 'BBB','CCC'], {retryPrompt: "Invalid choice, Please pick below listed choices",
listStyle: builder.ListStyle.button,
maxRetries: 1
});
},
function(session,results){
if (results.response) {
//Do something
}
}
I have 2 questions:
I would like to navigate to a different dialog Flow in case the user types anything other then the options("AAA","BBB","CCC"). Is that possible?
I would like to change the retryPrompt everytime lets say pick the utterances from a list. Is that possible?
I would like to navigate to a different dialog Flow in case the user types anything other then the options("AAA","BBB","CCC"). Is that possible?
Yes, it's possible. You can define several dialogs with required waterfall steps related to the choices. Like:
bot.dialog('AAA',[...])
And leverage replaceDialog to redirect your user to new dialog flows:
(session,result)=>{
//result.response.entity should be one of string `AAA`,`BBB`,`CCC`
session.replaceDialog(result.response.entity);
}
I would like to change the retryPrompt everytime lets say pick the utterances from a list. Is that possible?
Yes, it's possible. According the choice definition, we can set options extends IPromptChoiceOptions which extends [IPromptOptions][2], we can find that retryPrompt?: TextOrMessageType;, dig into the source code for the definition of TextOrMessageType:
/**
* Flexible range of possible prompts that can be sent to a user.
* * _{string}_ - A simple message to send the user.
* * _{string[]}_ - Array of possible messages to send the user. One will be chosen at random.
...
...
*/
export type TextOrMessageType = string|string[]|IMessage|IIsMessage;
So we can set a string list for retryPrompt, bot builder will pick one randomly. Please try following:
builder.Prompts.choice(session, "Please select one of the options:", ['AAA', 'BBB', 'CCC'], {
listStyle: builder.ListStyle.button,
maxRetries: 1,
retryPrompt:['utterance 1','utterance 2','utterance 3','utterance 4']
});
As you can call a function in retryPrompt you can do both of them:
builder.Prompts.choice(
session,
'This is just a question?',
'Yes|No',
{ retryPrompt: particularRetry() }
);
In the above, you can do what you want in particularRetry function.
For example for the second question, you can call the new propmt with new choices.
You can see more details in this post.

Microsoft Bot Framework LUIS in waterfall conversation

I have an existing waterfall conversation. I want to adapt it so that it can extract data from more complex user responses to the bot's questions.
In my LUIS app I have created an intent called GetLocation which is trained to find an entity called Location. An example of this is the user typing "I am looking in Bristol" which would match the entity "Bristol". This is what I currently have:
function(session) {
builder.Prompts.text(session, "Hello... Which city are you looking in?");
},
function(session, results) {
session.privateConversationData.city = results.response;
builder.Prompts.number(session, "Ok, you are looking in " + results.response + ", How many bedrooms are you looking for?");
},
etc...
Instead of simply storing the response string, I want to send the response string off to LUIS and extract the city location from it. All of the LUIS examples I've found are for matching and going to new Intents however I simply want to keep the waterfall conversation going. How would I utilise LUIS to do this?
I think you can do this by having two different dialogs setup:
Dialog 1:
This is the dialog you have above, your normal Waterfall dialog that drives the conversation.
Dialog 2:
This dialog will be created with a LUIS Intent recognizer using your LUIS model. Dialog 1 will issue the prompt, then pass the user to this dialog and parse the text entered by the user. Since your Model is already trained to recognize location, all you need to do now is extract the entity.
After dialog 2 has parsed the location information using LUIS, and extracted the entity, you will end the dialog and return the entity (location) back to dialog 1, which will still be on the Dialog Stack.
Code
//create intent recognizer based on LUIS model
var luisModel = "<Your LUIS Model URL>";
var recognizer = new botbuilder.LuisRecognizer(luisModel);
//create dialog handler for info to be parsed by LUIS
var dialog = new botbuilder.IntentDialog({ recognizers: [recognizer] });
//root dialog
bot.dialog("/", [
function(session){
//prompt user and pop LUIS intent dialog onto dialog stack
session.send("Hello, which city are you looking in?");
session.beginDialog("/begin_loc_parse");
},
//this will be resumed after our location has been extracted
function(session, results){
//check for extracted location
if(results.entity){
//got location successfully
session.send("Got city from user: " + results.entity);
//resume normal waterfall with location.....
} else {
//start over
session.beginDialog("/");
}
}
]);
//LUIS intent dialog
dialog.matches("input_location", function(session, args){
//grab location entity
var city = botbuilder.EntityRecognizer.findEntity(args.entities, "builtin.geography.city");
if(city){
//pop the LUIS dialog off of the dialog stack
//and return the extracted location back to waterfall
session.endDialogWithResult(city);
} else session.endDialog("Couldn't extract city entity.");
});
//called if user doesn't enter something like "I am looking in [city]"
dialog.onDefault(function(session, args){
session.send("I'm sorry, I didn't quite catch that. In which city are you looking?");
});
So basically, in the root dialog, when you prompt the user for the location, and then call session.beginDialog("/begin_loc_parse") you will have passed the conversation to your LUIS intent dialog.
Any text entered by the user after this point will be interpreted by your LUIS model. This allows you to use your model to recognize and extract the location information from the user.
Then, the key is to use session.endDialogWithResult()to pop the LUIS dialog off the stack, and to go back to your original waterfall with your newly extracted location.

How to make Hubot understand chat context?

Is there any way to make Hubot understand the context of conversation between messages? Such that he could ask me clarifying questions?
For example:
me: hey, create a branch plz
Hubot: How should I name it?
me: super-duper
Hubot: Branch 'super-duper' created
Should I use some kind of state machine? Any advices on that?
You can use robot's brain to persist state.
robot.respond /hey, create a branch plz/i, (res) ->
res.reply "Ok, lets start"
user = {stage: 1}
name = res.message.user.name.toLowerCase()
robot.brain.set name, user
robot.hear /(\w+)\s(\w+)/i, (msg) ->
name = msg.message.user.name.toLowerCase()
user = robot.brain.get(name) or null
if user != null
answer = msg.match[2]
switch user.stage
when 1
msg.reply "How should I name it?"
when 2
user.name = answer
msg.reply "Are you sure (y/n) ?"
when 3
user.confimation=answer
user.stage += 1
robot.brain.set name, user
if user.stage > 3 #End of process
if /y/i.test(user.confimation)
msg.reply "Branch #{user.name} created."
else
msg.reply "Branch creation aborted"
robot.brain.remove name
You could assign something like a session to it.
We are doing this for logins. When I tell him login it is bound to the calling user. Advantage is you can store this in the brain. Disadvantage is that one user can only have one session. (you could overcome this through letting them specifying a id.)
This can also be done with the Hubot Conversation plugin. This adds a dialog object which you can interact with. The dialog is scripted, not "natural", but it can be used to create chatbot pathways to execute simple workflows.
Your example might work as follows:
var Conversation = require("hubot-conversation");
module.exports = function(robot) {
var switchBoard = new Conversation(robot);
robot.respond(/create a branch/, function(msg) {
var dialog = switchBoard.startDialog(msg);
msg.reply("How should I name it");
dialog.addChoice(/[a-z]+/i, function(msg2) {
msg2.reply("Branch #{msg2.match[1]} created");
});
dialog.addChoice(/bathroom/i, function(msg2) {
msg.reply("Do I really have to?");
dialog.addChoice(/yes/, function(msg3) {
msg3.reply("Fine, Mom!");
});
});
});

Resources