Lab 5: Add a Second API to Your Dialog
Welcome to lab 5 Add a second API to your dialog. In this lab, you'll learn how answer a follow-up question with an API after providing a recommendation. Let's say Pet Match recommends a Great Pyrenees and your customer wants to know more about the dog breed. With Alexa Conversations, you can easily update your dialog and provide a response from your skill code to support this. Since Alexa Conversations keeps track of the conversational context, your customer will not only be able to ask for more information, they'll be able to change the size and ask for more detail and the skill will give the right detailed description based upon the updated recommendation.
Time required: 50 minutes
What you'll learn:
- How to pass the result of one API to another API
Providing follow-up information
Utterance and intent based custom skills do not automatically share data between requests to and from our skill code. To keep track of conversational context, you need to keep that information somewhere. For small amounts of short-term data, you can pass information back and forth via session attributes. When the session ends the session attributes are discarded. For more long-term memory that persists between sessions, you can use a datastore like Amazon DynamoDB. Alexa Conversations only calls your backend once all the necessary slots for an API have been collected.
Alexa Conversations Vs. Dialog Management
To fully understand that benefits of using Alexa Conversations to manage your dialog state, consider the work you'd have to take on to build this feature with Dialog Management.
Dialog Management
Since the collected slots don't persist once the Dialog Management dialogState is COMPLETED, to support any follow-up questions you have to store that information somewhere and recall it for each request. Take a look at the steps outlined below.
- When a follow-up question is asked
- Check session attributes for an existing recommendation
- If none exits
- Start asking questions to find a recommendation
- When the dialogState is complete, store recommendation in session attributes
- Start asking questions to find a recommendation
- If one exists
- Provide the answer to the follow-up question
- If none exits
- Check session attributes for an existing recommendation
That's quite a lot of work just to recall the previous recommendation, which you need in order to provide an answer to any follow-up questions. What if you wanted to support comparisons? For each new feature, you have to write skill code to implement it as well as handle all of the edge conditions. Wouldn't it be great if the Alexa Service kept track of this information?
Alexa Conversations
Since Alexa Conversations tracks the conversational context, the steps listed above are unnecessary. Your skill code will receive an API request for more information about the recommendation. To train Alexa Conversations to do this work on your behalf, you will:
- Update your dialog
- Create a new utterance set
- Create a new API definition
- Create a new response template
- Annotate your dialog
Now that you understand the process, it's time to update your skill to handle a follow-up for more information.
Step 1: Update the Sample Dialog
For this exercise you'll update dialog1 with the following lines:
U: What is that?
A: A chihuahua is …
The updated dialog will look like:
U: I want a large dog with high energy.
A: Do you want a dog that is good with family or better at guarding?
U: family
A: Okay, in that case I recommend a chihuahua.
U: What is that?
A: A chihuahua is …
Since you'll be updating an existing dialog. You'll want to be on the build tab.
-
Click on Dialogs.
-
Locate dialog1 and click on Edit.
-
Click on the **User says ** button.
-
In the box that says, "What might a user say" enter What is that?
-
Click on Alexa says and enter A chihuahua is …
Now that you've updated your dialog, you'll need to define:
- A slot type
- An utterance set
- An API definition
- A response template
You'll define the new slot type from the Slot Types page and all the rest you'll create in-line from the dialog edit screen.
Step 2: Create the getDescriptionResult slot type
In lab 4, you imported PetMatch.json into your skill code to serve as your "database". Each recommendation includes a description property. Your skill code will return the description and Alexa Conversations will store it as a property called description in the getDescriptionResult slot. So you need to create a slot with one property.
- Click on Slot Types.
- Click Add Slot Type.
Leave the "Create a custom slot type with properties" radio button selected.
- Enter getDescriptionResult into the textbox and click Next
getDescriptionResult represents the data structure that your skill code will return when the getDescription API is invoked, so you need to define a property for the description.
- Click on Add a new property.
-
Replace Enter Type Name with description.
-
Select stringLiteral from the SLOT TYPE drop-down.
You now have a slot to capture the value returned from your skill code. Now you can return the dialog editor for dialog1 and do the rest of your updates in-line!
Step 3: Create the utterance set.
-
Click on Dialogs
-
Edit dialog1.
- Click on the user dialog that says, "what is that?".
The User Input panel will appear.
- Select Invoke APIs from the drop-down.
A drop-drop to specify the API to invoke will appear. At this point you haven't created your getDescription API definition yet, so leave it blank. You'll come back to this later. It's time to define the sample utterances.
-
From the drop-down select Create New Utterance Set.
-
In the Utterance Set Name field enter invoke_getDescription_recommendationResult.
To provide the description to our customer, our skill code needs to look up it up and provide the response to Alexa Conversations, so the dialog act will be Invoke API.
- Add each utterance below to the Sample Utterances. (You can copy and paste the items below into the Bulk Edit tool.)
what's that
what is that
what is it
what
tell me about that
- Click Save.
Now that you have an utterance set that needs an API, it's time to create the getDescription API definition.
Step 4: Create the getDescription API
- Click on the line of dialogue that you added earlier that says, A chihuahua is…
The Alexa Response panel will appear. Since this response happens after the getDescription API returns a successful response, you'll set the dialog act to API Success.
- From the Dialog Act drop-down select API Success.
Now you need to define the getDescription API.
- Click the + button next to API to Invoke(Required).
The drop-down to select an API definition will appear. You also have the option to create a new API definition which you'll use to create the getDescription API definition.
- Select Create New API Definition from the drop-down.
The Create New API Definition modal will appear.
- In the NewAPIName field, enter getDescription.
In order to provide a description your skill code needs to know what dog to provide a description for. Since Alexa Conversations is tracking the conversational context, you can have it pass the recommendation to getDescription as an argument.
- Under Arguments click, Add Argument.
An argument called Event Argument Name will populate the list of arguments. You'll want to name your argument recommendationResult and bind it to the getRecommendationResult slot.
- Replace arg0 with recommendationResult and select getRecommendationResult from the slot type drop-down.
The next step is to select a Response Template but you haven't created it yet, so for now skip it and define what slot type to return. Since the getDescription API will return a description, you need to select the getDescriptionResult slot type you created in step 2.
-
Select getDescriptionResult from the Return dropdown.
-
Click Save.
The modal will disappear and your newly created getDescription API Definition will be selected. Now you need to tell Alexa Conversations what to pass to it. The getDescription takes one argument which is a getRecommendationResult slot. In lab 4, you created the variable, getRecommendationResult to store the response from getRecommendation, so you can pass it to getDescription.
- After clicking save, the section under API to Invoke(Required) will automatically expand.
The UI will open up and you'll see the Variable → Arguments control that allows you to map variables to arguments. You'll need to map getRecommendationResult to recommendationResult.
- Click on the variable drop-down next to recommendationResult and select getRecommendationResult.
Now that you've created the API, all you need to do is create the response template.
Step 5: Create the Response Template
- Click the Alexa Response that says "A chihuahua is …". The Alexa Response panel will appear.
- From the Response drop-down select Create New Response. The Edit Response modal will appear.
-
In the ResponseName field enter, "notifySuccess_getDescription"
-
Scroll down to Arguments.
-
Click Add Argument.
-
Replace Enter Type Name with result.
-
Select getDescriptionResult from the Slot Type drop-down. Now that you've defined the arguments, it's time to define the Audio Response.
- Select "Create a new prompt" from the drop-down under Audio Response(required). A grey box will appear with fields to define the response name and prompt.
-
Type "notifySuccess_getDescription" into the Audio Response Name field.
-
Locate the string that says, "A chihuahua is …" that appears in the Alexa Prompts field.
-
Highlight the whole string. The Select Slot Type menu will appear.
- Click on description.
This will replace the line "A chihuahua is …" with {description}. At run-time Alexa Conversations will use the data it receives from your skill code to fill in the description.
- Click the grey Save button.
- Click the blue Save button.
Now you need to map the variable to the response template arguments. notifySuccess_getDescription has an argument called description, which is a getDescriptionResult so you'll map variable getDescriptionResult to it.
- Under** Variable → Arguments** select getDescriptionResult0 from the drop-down.
That's it you're done with your front-end changes! It's now time to save, build and update your skill code so you can handle the new API request you defined!
- Click Save.
- Click Build Model.
By the time you've finished updating your code, your model should be done building.
Step 6: Update the backend code
When your customer asks to know more about the recommendation, Alexa Conversations will call your backend so you need to create an API handler that will service Dialog.API.Invoked requests for getDescription.
Since you instructed Alexa Conversations, to pass the recommendationResult which we updated to include the description, our handle function will simply access the description and return it as a response entity. Alexa Conversations will pass the description to the notifySuccess_getDescription response template to generate the spoken response.
-
Click on the Code tab.
-
Open the index.js file.
-
Find the line that starts const data = require('./PetMatch.json'); and add a new line.
-
Paste the following code on the new line:
const GetDescriptionAPIHandler = {
canHandle(handlerInput) {
},
handle(handlerInput) {
}
}
Now that you have your handler stubbed out, it's time to make it do something. You'll start by making your canHandle function return true when the incoming request type is Dialog.API.Invoked and the name is getDescription.
- Paste the following code into the canHandle body.
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Dialog.API.Invoked'
&& handlerInput.requestEnvelope.request.apiRequest.name === 'getDescription';
Now you need to write the code that will access build the lookup key from the recommendationResult, fetch it from the data defined in PetMatch.json, wrap it in a response entity and return it.
- Paste the following block of code into the body of the handle function.
const recommendationResult = handlerInput.requestEnvelope.request.apiRequest.arguments.recommendationResult;
// setting the default response.
let databaseResponse = `I don't know much about ${recommendationResult.name}.`;
const energy = recommendationResult.energy;
const size = recommendationResult.size;
const temperament = recommendationResult.temperament;
// setting the actual response if we find a match for their preference
if (energy !== null && size !== null && temperament !== null) {
const key = `${energy}-${size}-${temperament}`;
databaseResponse = data[key];
}
const descriptionEntity = {
description: databaseResponse.description
};
const response = buildSuccessApiResponse(descriptionEntity);
console.log('GetDescriptionAPIHandler', JSON.stringify(response));
return response;
The first line accesses the API request's arguments and stores them into a variable called recommendationResult. The second line sets a default value for the description. The next couple of lines access the energy, size, and temperament, appends them together to build the lookup key, and fetches the entry from the Pet Match data.
Lastly, the code bundles the description into a JSON object called descriptionEntity and passes it to buildSuccessApiResponse before returning the response to Alexa Conversations.
Once you've updated both functions, your code should look like this:
const GetDescriptionAPIHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'Dialog.API.Invoked'
&& handlerInput.requestEnvelope.request.apiRequest.name === 'getDescription';
},
handle(handlerInput) {
const recommendationResult = handlerInput.requestEnvelope.request.apiRequest.arguments.recommendationResult;
// setting the default response.
let databaseResponse = `I don't know much about ${recommendationResult.name}.`;
const energy = recommendationResult.energy;
const size = recommendationResult.size;
const temperament = recommendationResult.temperament;
// setting the actual response if we find a match for their preference
if (energy !== null && size !== null && temperament !== null) {
const key = `${energy}-${size}-${temperament}`;
databaseResponse = data[key];
}
const descriptionEntity = {
description: databaseResponse.description
};
const response = buildSuccessApiResponse(descriptionEntity);
console.log('GetDescriptionAPIHandler', JSON.stringify(response));
return response;
}
};
-
Find the line that starts with, .addRequestHandlers( and add a new line.
-
On the new line add GetDescriptionAPIHandler,
-
Click Save.
-
Click Deploy.
Now it's time to test!
Step 7: Test
Go to the Test tab and interact with the skill. Once you receive a recommendation type, "what is that" into the simulator. The skill should respond by reading the description. Pretty neat. Remember the context carry-over feature we added in lab 5. Try that too by typing, "how about a smaller dog". The skill will give you another recommendation. What happens when you type "what is that" again? The skill will read the description of the newest recommendation.
Alexa Conversations is taking care of the conversational context for us! That's pretty neat.
Wrap-up
You're now equipped with the knowledge to add multiple API definitions per dialog. In this case you didn't collect any additional slots, but if you needed to pass more information to an API definition you can.
Code
If your skill isn't working or you're getting some kind of syntax error, download the code sample in Node.js from the link below. Then, go to the Code tab in the Alexa developer console and copy and paste the code into the index.js file. Be sure to save and deploy the code before testing it.