Use Session Persistence with Your Alexa-hosted Skill

When you create an Alexa-hosted skill, Alexa stores your code and resources on AWS for you. For more information about hosted skills, see Build a Skill End-to-end Using an Alexa-hosted Skill.

When you create an Alexa-hosted skill, you get access to an Amazon S3-backed key-value table for managing session persistence. Your Amazon S3 usage is limited to the free tier: 5 GB of storage, 20,000 get requests, 2,000 put requests, and 15 GB of data transfer out per month. Alexa-hosted skills use the encryption methods supported by AWS for tabular data.

You can use Node.js or Python to add persistence to your Alexa-hosted skill. You can see full code examples on GitHub that use persistence for Node.js here and for Python here.

Use session persistence with Node.js

  1. To use session persistence with Node.js, first add the following dependency in the dependencies section of your package.json file:

    "ask-sdk": "^2.6.0"
    "ask-sdk-s3-persistence-adapter": "^2.0.0"
    
  2. Then, in your index.js file, add the code. Import the ask persistence adapter.

    const AWS = require('aws-sdk');
    const persistenceAdapter = require('ask-sdk-s3-persistence-adapter');
    
  3. Add the SesssionEndedRequestHandler request handler to your skill builder request handlers. Don't overwrite other request handlers.

    exports.handler = Alexa.SkillBuilders.custom()
        .addRequestHandlers(
            LaunchRequestHandler,
            //other request handlers,
            SesssionEndedRequestHandler
        )
        .addErrorHandlers(ErrorHandler)
    
        .withPersistenceAdapter(
            new persistenceAdapter.S3PersistenceAdapter({
                bucketName: process.env.S3_PERSISTENCE_BUCKET,
                s3Client: new AWS.S3({apiVersion: 'latest', region: process.env.S3_PERSISTENCE_REGION})
            })
        )
        .lambda();
    
  4. Add a handler to save attributes.

    async handle(handlerInput)
    {
        const attributesManager = handlerInput.attributesManager;
        let s3Attributes = {"counter":10};
    
        attributesManager.setPersistentAttributes(s3Attributes);
        await attributesManager.savePersistentAttributes();
    
        let speechOutput = `Hi there, Hello World! Your saved counter is ${s3Attributes.counter}`;
    
        return handlerInput.responseBuilder
            .speak(speechOutput)
            .getResponse();
    }
    
  5. Add a handler to read attributes.

    async handle(handlerInput){
    
        const attributesManager = handlerInput.attributesManager;
        const s3Attributes = await attributesManager.getPersistentAttributes() || {};
        console.log('s3Attributes is: ', s3Attributes);
    
        const counter = s3Attributes.hasOwnProperty('counter')? s3Attributes.counter : 0;
    
        let speechOutput = `Hi there, Hello World! Your counter is ${counter}`;
    
        return handlerInput.responseBuilder
            .speak(speechOutput)
            .getResponse();
    }
    

Use session persistence with Python

  1. To use session persistence with Python, first add the following dependency in the dependencies section of your requirements.txt file:

    boto3==1.9.216
    ask-sdk-core==1.11.0
    ask-sdk-s3-persistence-adapter==1.0.0
    
  2. Then, in your lambda_function.py file, add the code. Import the ask persistence adapter.

    import os
    import boto3
    
    from ask_sdk_s3.adapter import S3Adapter
    from ask_sdk_core.skill_builder import CustomSkillBuilder
    
  3. Initialize the persistence adapter.

    bucket_region = os.environ.get('S3_PERSISTENCE_REGION') 
    bucket_name = os.environ.get('S3_PERSISTENCE_BUCKET') 
        
    s3_client = boto3.client('s3', region_name = bucket_region)
    s3_adapter = S3Adapter(bucket_name, s3_client = s3_client)
    
  4. Add the SesssionEndedRequestHandler request handler to your skill builder request handlers. Don't overwrite other request handlers.

    sb = CustomSkillBuilder(persistence_adapter = s3_adapter)
    
    sb.add_request_handler(LaunchRequestHandler())
    sb.add_request_handler(CancelOrStopIntentHandler())
    sb.add_request_handler(SessionEndedRequestHandler())
    #
    #other request handlers
    #
    sb.add_exception_handler(CatchAllExceptionHandler())
    
    lambda_handler = sb.lambda_handler()
    
  5. Add a handler to save attributes.

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        attr = handler_input.attributes_manager.persistent_attributes
        if not attr:
            attr['counter'] = 0
            attr['state'] = 'ENDED'
    
        handler_input.attributes_manager.session_attributes = attr
    
        handler_input.attributes_manager.save_persistent_attributes()
        speak_output = ("Welcome back. Your saved counter is {}. You can say Hello or Help?".format(attr["counter"]))
        reprompt = "Say hello or help to start."
        return (
            handler_input.response_builder
                .speak(speak_output)
                .ask(reprompt)
                .response
        )
    
  6. Add a handler to read attributes.

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        session_attr = handler_input.attributes_manager.session_attributes
        session_attr['state'] = "STARTED"
        session_attr["counter"] += 1
        handler_input.attributes_manager.persistent_attributes = session_attr
        handler_input.attributes_manager.save_persistent_attributes()
    
        speak_output = ("Hi there, Hello World! Your saved counter is {}.".format(session_attr["counter"]))
        return (
            handler_input.response_builder
                .speak(speak_output)
                # .ask("add a reprompt if you want to keep the session open for the user to respond")
                .response
        )
    
  7. (Optional) Add a handler to delete attributes.

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        session_attr = handler_input.attributes_manager.session_attributes
        session_attr['state'] = "ENDED"
    
        speak_output = ("Goodbye. Your counter is {}".format(session_attr["counter"]))
    
        handler_input.attributes_manager.delete_persistent_attributes()
        return (
            handler_input.response_builder
                .speak(speak_output)
                .response
        )