Zero to Hero, Part 4: Persistence

– Hey and welcome back. My name is Andrea and I’m a tech evangelist
on the Amazon Alexa team. In this video, we’re gonna continue our skill-building journey and look at persistence. What is persistence? Persistence is how do you save information in a more permanent way. There is two types of persistence, short-term and long-term persistence, and this will allow our
skill to remember things so when users use it again, our skill can adapt to them. So without further ado, let’s get started. Okay, so now let’s augment our skill with persistence. That is, how do we keep track of what users have said, how do we keep track of
important information. Our happy birthday skill, for now, just says back what our birthday is but when we say stop or we reopen it after a couple hours, it has forgotten everything we’ve said. So how do we change that? First of all, we’re gonna change our skill so that when we ask it
to remember our birthday, it will not only remember it but it’ll also keep track of it. And here we introduce the concept of attributes, both session attributes and persistent attributes. Most of this video will be dedicated to the code, to the backend. But we do have to make
a couple adjustments in the front end as well. That is, we want to have an intent whose sole purpose is to read back our date of birth and specifically how many days are left until our birthday. So, let’s update this JSON Editor with our updated interaction model, which I have right here. And it’s exactly the same as before. There’s only one new intent which doesn’t have any slots. So let’s update it and I’ll show you what has changed. Let’s go up, save and you’ll see it’ll update here with
a Say Birthday Intent. And simply we have a couple utterances that say my next birthday, when is my birthday,
my birthday, et cetera. Let’s build this model and start focusing on the backend because persistence
happens in the backend. Alexa is completely stateless. Any time the user speaks to Alexa your backend is sent a brand-new request, so you can’t rely on Alexa to keep track of things in the long term. What you can do though, is rely on Alexa to keep track of short-term memory. And this is where we start
talking about session attributes and then persistent attributes
are long-term memory, which you have to handle yourself. So let’s go into the Code tab and see how to do this. Before we move on, our code is getting a little bit long and it’s not a best practice to have everything in one file. So, before we continue I’m actually going to create a new file in our lambda folder called localization.js, and this is where we will put
all of our language strings. That way our strings and our code logic is completely independent. For now it’s empty and I have them ready here to copy and paste. And of course we’ll need to remove them from our index.js file. So let’s go to our index.js and delete them. I’ll also be updating this code here and walking you through what has changed. Let’s do that now. Right here. Let’s update the code and override our index.js. So, a lot of things have changed but I’ll walk you through
them step by step. The first thing is we have this new persistence adapter. What does that mean? Well, it’s a utility function that’s available in the SDK that deals with databases. When we’re using Alexa-hosted skills, we have a database or a sort of database ready for us to use on S3. S3 is a AWS service that stores blobs of information, objects is what we call them. And Alexa-hosted skills uses S3 to store our key value pairs of any data we write using the persistence adapter of S3. However, we can also have
DynamoDB for example, and you can write your own adaptor to work with your database. And we’ll see what this
adapter looks like. Then we have a Moment.js library because we want to calculate how many days are left between today and our birthday. So we’ll have a new dependency called moment, in this case moment-timezone. And here we have our usual i18n, which is used for localization so that our skill can
support multiple languages. And then, as you can
see, we moved our strings away from the index.js file and into this localization file that we created just now. And right here we have
our persistence adapter. All we have to do is
specify the table name and the persistence
adapter helper function, which you can reuse in
whatever skill you create, is automatically compatible with both Alexa-hosted
and self-hosted solutions. So if you have your own lambda function and your own AWS account this will work right out of the box, just make sure that your
lambda has permissions to access DynamoDB. We will be using the S3 part because we are using Alexa-hosted. And in our case it will read a private variable that
is used under the hood, which is called a process
environment variable. What else has changed? Well, once we have this
persistence adapter we have to register it. You’ll see that if we
scroll to the bottom, we have a with persistence adapter where we pipe in our persistence adapter, and this is telling the
Alexa skill builder object, “Hey, we are using databases “and this is the adapter
that you need to use “to save, when we use the right function, “to save persistent attributes “or to read persistent attributes.” We also have two new interceptors. You’ll see here we have our request and response interceptors, and we have this load
attributes request interceptor and save attributes response interceptor. So any time we open the skill, the load attributes request interceptor will take anything that’s written to disk and load it into our session attributes. And any time we output a
response from our skill, the save attributes response interceptor will take that information
in them session attributes and save them to our database. But what are session attributes? Well, session attributes are, let’s compare them to RAM. Session attributes are RAM memory, so short-term and volatile. They persist as long as
the session is ongoing. Any time you quit the skill the session attributes disappear. To save those session attributes we need persistent attributes. So we have these interceptors that read from the attributes manager. The attributes manager is an object that has utility
functions for both reading and writing session attributes and persistent attributes. So in our load attributes
request interceptor we check whether there’s anything written in the database for that user ID and we load them, and we assign them to the session attributes via set session attributes. Same thing for the save attributes. We read anything that’s
in the session attributes, right here, and we set and save the persistent attributes. Here we do attributes
manager.set persistent attributes and we pass in the session attributes. Why are session attributes
short-term memory? That’s because anything you write to the session attributes is passed along with your response to the Alexa service and that is the only
thing that Alexa will keep for the next request as long as the session is open. Whereas with persistent attributes, we can quit the skill
and come back in two days or one year and it’ll still be there. So we have these two interceptors, the load attributes and
the save attributes, and in our handlers, we also read and write from these attributes. So for example, let’s go in order, in our launch request, we get the session attributes by using handler input.attributes manager .get session attributes, and we assign the values
of day, month and year to a couple variables. And then we do something interesting here. We saw in the previous video that in the launch
request we were chaining from one intent to the other. Here we’re using the handle function of another handler. That is to prevent code redundancy. We only write a specific
piece of code in one handler and we can reuse it in other handlers. Likewise, if we don’t have a birthday set we will chain to the
register birthday intent, which the intent that will
ask us when our birthday is. So, our launch request will both handle in the case that there is a birthday set, as well as in the case
there is no birthday set. In the register birthday intent handler, this is where we actually save the birthday that the user is giving us, and you’ll see that once
the intent is confirmed, meaning Alexa got the right date, we are reading the values and we are using the say birthday intent. So we’re saving them to the
session attributes here. You’ll see. We are calling the attributes manager and getting the session attributes and we are writing to that object the day, month and year, and then we are delegating the rest to the say birthday intent handler. And the save birthday intent handler is the one that does all
of the heavy lifting. It’ll read the birthday. It’ll calculate how many days are left from today until that birthday using Moment.js, which
is a JavaScript library and Node.js library
for dealing with dates. Very useful one. And you will see it’ll do some differences and then it will output a response. If there is no date, it’ll instead chain to the register birthday intent to ask for when we were born. Now, we did everything
except, if you remember, we have new dependencies. So, we also have to
update our package.json. So let’s go to our package.json, right here, and update our dependencies. You’ll see we have a new moment-timezone. A ask SDK S3 persistence adapter and a DynamoDB persistence adapter. So let’s deploy all these changes and if you remember, when we first updated our package.json a couple of videos ago, we had to wait a little
bit longer than usual because Alexa is running npm install in the backend and updating all of our dependencies. While that’s running we’ll just review that our code is fine. We have our persistence adapter, which will check whether
it’s Alexa-hosted or not. We have our launch request, which will either delegate to register birthday intent because it’s a dialog management intent, so we can chain to it. Or if the birthday is set, it’ll delegate the handling to the say birthday intent handler, which again does most
of the heavy lifting. In the register birthday intent handler we do more or less what we did before, which is ask for the birthday, but we also save that information to the session attributes. And in the say birthday intent handler, we read from those session attributes, right here, and we do some date calculations to check how many days are
left until our birthday. And if the birthday is missing we go back to the register birthday intent and we ask for the birthday again. Not only that. But on every request, we are populating the session attributes with our interceptor by the load attributes request interceptor and on every response, we are saving those session attributes
to memory, to disk, using the save attributes
response interceptor. You’ll also notice if you’re not familiar with JavaScript that we’re using async await. So even though interceptors
have a process function we also need to put async await because the save persistent attributes and the read persistent attributes are async, right? They require an API call. So we need to use async await, or you can use promises, or whatever method you
want to handle async code. All right, so let’s test it out and see how it works. We deployed it, let’s go to Test. And we’ll do open happy birthday. – [Alexa] Welcome back! It looks like you haven’t told me your birthday yet. When is your birthday? – [Andrea] 22nd of April, 1990. – [Alexa] Are you sure
you want to register April 22nd, 1990? – [Andrea] Yes. – [Alexa] There are 246 days left until you’re 30 years old. If you want to change the date try saying, register my birthday. Or just say the date directly. What would you like to do next? – [Andrea] And that’s it. You’ll see that it’s reading. Now that we’ve set the date let’s try and close
the skill and reopen it and see if Alexa remembers. Let’s do stop. – [Alexa] Goodbye! – [Andrea] And now let’s open happy birthday again. – [Alexa] There are 246 days left until you’re 30 years old. If you want to change the date, try saying, Register my birthday. Or just say the date directly. What would you like to do next? – And you’ll see that even across sessions Alexa has remembered our birthday. And if you ever want to see what Alexa has saved on S3, you can always go to your Code tab and click on S3 here, down here. Click on your object and you’ll have an object with your user ID. For each user ID there will be an object with any data that has been stored, in our case the session
attributes for that user. So before we end the video, I also want to show you how to delete information that has been saved because sometimes you
want to test your skill without any saved information. So all you have to do is go to the user ID you want to delete the information for, Actions, and click on Delete. Now all information has been deleted. If we go back to our Testing tab, we can try the skill again, and you’ll see it hasn’t
remembered our birthday. Open happy birthday. – [Alexa] Welcome to Happy Birthday! Let’s have some fun with your birthday! It looks like you haven’t
told me your birthday yet. – [Andrea] Which is expected. – [Alexa] Which day is your birthday? – [Andrea] Now let’s
give it the day of today and see what happens. 20. 20th. First I have to learn
how to write twentieth. – [Alexa] Okay, the day is 20, but in which month? – [Andrea] August. – [Alexa] Okay, August 20th, but in which year? – [Andrea] 1990. – [Alexa] Are you sure
you want to register August 20th, 1990? – [Andrea] Yes. – [Alexa] Happy birthday! You’re now 29 years old. If you want to change the date, try saying, register my birthday. Or just say the date directly. What would you like to do next? – And that’s it. Thank you for watching and see you in the next video.

One Comment

Add a Comment

Your email address will not be published. Required fields are marked *