How can I delegate the LaunchRequest to an IntentRequest using the alexa-sdk from npm - node.js

I am building an Alexa skill in node using the alexa-sdk. I am using a dialog model to handle the user interaction. I am having some trouble passing the flow along to new request types, such as from the launch request to an intent request.
Below is an example of my handlers and what I want ideally. My specific usecase is that I would like to ask some questions of the user and then send them to different intents based on what they answer. In the intents I would like to have access to the request objects, as if they entered that intent originally, so the dialog model can do its work.
const handlers = {
'LaunchRequest': function () {
this.emit('Entry'); // this does not do what I want
},
'Entry': function () {
let request = this.event.request; // this is the launch request object.
// I would like to get the request object for Entry, like if the user started here
// ask some questions, potentially passing the torch to a new intent based on the answers
}
};
So, is there any way to "call" an intent like the user originally made a request to that intent? Sorry if I missed something obvious in the documentation, I searched around pretty thoroughly I think, but there is A LOT of documentation. ps: I could manually construct the request object of course, but I really should not have to I feel.

I am pretty sure there is no way yet to call on an intent as you are asking.
If you go through the syntax description of dialog directieves here, it says:
Note that you cannot change intents when returning a Dialog directive, so the intent name and set of slots must match the intent sent to your skill.
With returning a dialog directive you are able to 'elicit' or 'confirm' slots or intents, or even let a delegate handle your dialog for you, with prompts and reprompts set in the Skill Builder.
As far as i know, the only solution to trigger a specific intent is to make the user invoke it. You can guide the user into saying a specific utternace to trigger your intent.
As for saving older requests, you can use session attributes. Just build a response after your Launch with a session attribute containing the whole LaunchRequest.
"sessionAttributes": {
"oldRequest": this.event.request
}

Related

Skill host generating new conversation IDs every turn, breaking waterfall dialogs

I'm working on converting some existing bots to skills so that we can create a Skill Host bot that can call multiple "child bots". I used the skills-simple-bot-to-bot sample, and it kind of works. I can call the skill bot from the host and it works...for a single turn. But the host is creating a new conversation ID every turn, clearing the conversation state which makes my multi-turn waterfall dialogs not work. It is also causing any other values stored in conversation state to be cleared (e.g. I capture email address in conversation state so I don't have to reprompt it). The skill bot works fine when invoked directly (i.e. not through the skill host), so it's definitely something with the way the skill host works. I found this issue on GitHub which seems to be the same thing (that's for .NET but there are links to the other SDKs including js), though it seems to have been resolved years ago yet I'm still having the issue so I'm not sure it's the same.
So in short, how do I set up the skill host so that it doesn't generate new conversation IDs every turn?
Not sure this will help as it's almost exactly the same as the sample, but here's a snippet of the code where it appears conversation ID is being generated by the host. It seems maybe I need some way to NOT call createSkillConversationIdWithOptions, but if that's what is required I'm not sure what I need to do to generate the skillConversationId other than what is currently in the sample.
async sendToSkill(context, targetSkill) {
// NOTE: Always SaveChanges() before calling a skill so that any activity generated by the skill
// will have access to current accurate state.
await this.conversationState.saveChanges(context, true);
// Create a conversationId to interact with the skill and send the activity
const skillConversationId = await this.conversationIdFactory.createSkillConversationIdWithOptions({
fromBotOAuthScope: context.turnState.get(context.adapter.OAuthScopeKey),
fromBotId: this.botId,
activity: context.activity,
botFrameworkSkill: this.targetSkill
});
// route the activity to the skill
const response = await this.skillClient.postActivity(this.botId, targetSkill.appId, targetSkill.skillEndpoint, this.skillsConfig.skillHostEndpoint, skillConversationId, context.activity);
// Check response status
if (!(response.status >= 200 && response.status <= 299)) {
throw new Error(`[RootBot]: Error invoking the skill id: "${ targetSkill.id }" at "${ targetSkill.skillEndpoint }" (status is ${ response.status }). \r\n ${ JSON.stringify(response.body) }`);
}
}

Prevent new context after responding with new ones on Dialogflow

I have an intent with webhook and slotfilling enabled for validation and it have 4 parameters marked as required, so my server can validade the parameter value.
The strategy that I'm trying to use is: reset the context for invalid parameter value, so the dialogflow can ask it again.
Here is an example entering an invalid value:
On responding "Brasília" the webhook makes a request to my server. My server knows that is an invalid value and respond with the context presented on the previous image.
This is the result:
Notice that the first 4 context match with the previous image, its everything ok here. But dialogflow adds another context. That context is a request for the next parameter, called "motivo" and if I respond back, the response will be stored on this param. After this, dialogflow prompts back for the "local" param.
The conversation runs like this:
User: I want to register a call
Dialogflow: From where you want to register? Aracaju, CAB, Itabuna or Salvador?
User: Brasília
Dialogflow: Please, describe the reason
User: My network wireless is not working
Dialogflow: From where you want to register? Aracaju, CAB, Itabuna or Salvador?
...
What was supose to be:
User: I want to register a call
Dialogflow: From where you want to register? Aracaju, CAB, Itabuna or Salvador?
User: Brasília
Dialogflow: From where you want to register? Aracaju, CAB, Itabuna or Salvador?
...
What I need to know: Am'I responding with the correct contexts? Is there a way to prevent dialogflow creating this new "registrar_dialog_params_motivo" after responding with a new contexts?
Obs.: I'm using a Nodejs server, responding the webhook as the docs suggests.
Edit 1: I know that I can use an entity for this situation. However there is some cases that I need to make multiple validations on backend to procede, something like an user id, etc.
If the intent “Intent_Name” has some input_context defined already, then you need to set that context again as output_context and reply something like you have entered an invalid location, and please enter again.
If the intent does not have any input_context, then you can simply reply you have entered an invalid location. Please enter again, and your intent should be able to catch that too. However, this can cause problems as well.
What I would recommend is having another intent “Intent_Name_followup” with an input_context as location_validation_failed in the logic. If the location is incorrect, you can set this in the output_context to reply to the user.

How to pass to an intent if Permission request is denied

I have an Action that integrates with Dialogflow which as part of the conversation requests access to the user's location.
This is fulfilled via a webhook:
app.intent('actions_intent_PERMISSION', async (conv, params, permissionGranted) => {
if (!permissionGranted) {
app.intent('actions_intent_PERMISSION - no', (conv, params) => {
conv.ask('sad face, you said no to my permission request!');
});
// conv.ask(`Ok, no worries. I'll have to figure out how to get your postcode. follow-up intent I suppose`);
} else {
conv.data.postcode = conv.device.location.zipCode;
conv.ask(`Ok great - please give me a minute, I have to get data from a few different places.`);
//use postcode to make some other API calls
}
});
Everything is fine when the user gives permission but when they don't give permission I would like to pass off to an intent that asks for their location manually ('what is your postcode/zipcode?').
As per the screenshot I tried creating a followup intent to actions_intent_PERMISSION called actions_intent_PERMISSION - no but this causes the app to crash.
What is the best way to pass the conversation to another intent is the value of permissionGranted is false?
Your question doesn't quite make sense the way you've asked it. Intents represent what the user has said and not what you do with it - that is what your webhook does in an Intent Handler. There is nothing (technically) stopping you from replying to the user asking for their zip code by just replying differently.
If you want users to be able to trigger some Intents if they have given permissions, and other Intents if they have not, you can set a different Context for each. Then you would set some Intents to only be triggered if the "permitted" context was set, and others only if the "notpermitted" context was set.
However, you have a non-technical problem to consider. If they aren't giving you permission to get their location, why would they tell you their location? It also is likely that the reviewers would reject it, saying that you should be getting location information through the provided API.

Alexa SDK Redirect to Set Session Attributes

Is there a way to temporarily redirect from one intent to another in order to fill slots or session attributes and then return to the original intent to be responded to or fulfilled?
My use-case is for asking for an account PIN as a secondary authentication after account linking. For instance, if someone asks "What are my account details?", I want that intent to change in the session attributes if it exists and if not, temporarily redirect to an intent that will prompt them for the PIN and set it...then come back and answer their account details request. Similar to the example below:
const IntentHandler = {
canHandle(input) {
return (
input.requestEnvelope.request.type === 'IntentRequest' &&
input.requestEnvelope.request.intent.name === 'MyIntent'
},
handle(input) {
const { accessToken } = input.requestEnvelope.context.System.user
// ... do stuff with accessToken
if (!input.attributesManager.getSessionAttributes().pin) {
// redirect to other intent to set the pin session attribute
}
// ...response to intent request
}
}
I can get this working with a single intent using slot filling prompts but this is a common task for several intents and would like to separate it out so it doesn't have to be configured in the console for all that require it.
FYI: using the ask-sdk for Node.js
Is this possible with the current version of the ask-sdk?
Yes, Alexa makes it easy now to switch between intents and jump right into eliciting a specific slot for the new intent.
I would use the ElicitSlot Dialog Directive and include the updatedIntent object for the new intent.
updatedIntent
Use this to change intents during the dialog or set slot values and confirmation status. See Change the intent or update slot values during the dialog. If you don't need to change the intent, slot values, or confirmation statuses, you can leave this property out of your response.
When you switch intents with this parameter, Alexa attempts to elicit the specified slot value on the new intent. The next IntentRequest to your skill will be the new intent, not the original.
Example:
Intent A is missing access token.
Intent A directs Alexa to elicitSlot "pin" from Intent B. (include info in sessionAttributes such as "previousIntent" or "nextIntent" to use as direction to Intent B)
Alexa responds to user with Intent B's elicit prompt for slot "pin".
User provides pin.
Alexa provides user input to Intent B (not A) with slot "pin" filled and Intent B sets the access token in sessionAttribtues.
Then Intent B checks sessionAttribtues for direction of which intent to return to and which slot to elicit from that intent. So you can either confirmIntent or elicitSlot for Intent A.
Intent A will receive the next user input and now detects access token.
Notice that you will have to address the user with something between switching intents. If you are using confirmIntent then it can be a simple Yes/No confirmation question such as, "Thank you for your pin, would you like me to retrieve your account details now?". The user says "Yes", and that is sent back to Intent A to complete what it started.
If you don't want this extra back and forth with the user, then as far as I know, you'll have to build the pin slot into each intent you may need to elicit it. That way, you just stay within Intent A. Less efficient code, but more efficient conversation, so it's a bit of a trade off.

In a chatbot conversation using dialogflow, Is there a way to make the bot speak first?

Is it possible to format a conversation so that the bot initiates conversation using dialogflow in a web demo integration?
The objective is to say something like “Hi, I’m a bot, I can do x” to establish that it’s a chatbot rather than a human.
Can anyone suggest any idea for this?
You can set a welcome intent, then send a /query request containing an event parameter. Set the event parameter to WELCOME and your chatbot will respond with whatever conversation opening you set.
More info here: https://dialogflow.com/docs/events
If you are using something other than the API for interacting with your Dialogflow agent (Slack, Facebook Messenger, etc.) you will need to add an appropriate event under "intents" in your console (such as the "Facebook Welcome" event).
For interacting with your Dialogflow agent via the API, see below.
In the API interaction quickstart documentation, Dialogflow gives you the SessionClient's detectIntent method for sharing messages with your bot.
Each language has a different solution. But on an abstract level, you want to change the request object that you send to Dialogflow to include a "Welcome" event (no input message required), as Omegastick described.
For example, in Node.js, your request object would look like this:
// The text query request.
const request = {
session: sessionPath,
queryInput: {
event: {
name: "Welcome",
languageCode: languageCode
}
},
};
This assumes you have an appropriate intent set up in your Dialogflow console to handle Welcome events. One is provided by default that you can observe.
You can also add contexts, so that your agent gives a different greeting message based on some condition.

Resources