Django-Alexa

Amazon Alexa Skills Kit integration for Django

Kyle Rockman

http://slides.com/rocktavious/django-alexa/live

Who am I?

  • Kyle Rockman
  • Under Armour Connected Fitness
  • Build & Release Engineer
  • Working with python for 5+ years
  • https://www.linkedin.com/in/kylerockman
  • @rocktavious (github, twitter)

Overview

  • ASK 101 (request lifecycle, voice interface, etc)
  • Getting familiar with the Echo (Demo)
  • Django-Alexa's benefits for the developer
  • More Demos
  • Designing for the Voice
  • Q & A

Terminology

Alexa Skills Kit (ASK)

Alexa Voice Service

(AVS)

Utterance

Intent

Amazon service that interacts with your service to provide extra functionality to the Amazon Echo

Amazon service that you can interact with to provide Amazon Echo capabilities to your device

A definintion of a specific command your skill can act on

A phrase that is mapped to an Intent which aids ASK in speech recognition for your skill

ASK 101

How end users interact with your skill

Ask or Tell

Ask <invocation name> to <do some action>

Ask recipes how do i make an omelet?

Tell <invocation name> that <some command>

Tell scorekeeper that Stephen has ten points.

Ask <invocation name> for <some information>

Ask daily horoscopes for Taurus

Other Phrases

  • Play
  • Talk to
  • Open
  • Launch
  • Start
  • Use
  • Resume
  • Run
  • Load
  • Begin

Play Jeopardy

Start Seven Minute Workout

Load BART Times

Intents

Full Intent

Partial Intent

No Intent

A spoken request in which the user expresses everything required in a single utterance

A spoken request where only a subset of the needed slots were expressed leaving it up to your skill to have extract more information from the user

An open ended request in which the user is expecting the skill to report back its capabilities

Amazon Echo

Demo

Django-Alexa

Why?

  • Standardized REST endpoint
  • Request routing
  • Request validation
  • Response creation
  • Response validation
  • Skill Configuration / Setup

I just want to write my skill...man.

Django-Alexa

  • Validates Requests
    • App ID
    • Header Certificate Chain
    • Hash of body
    • Timestamp
  • Validates Responses
    • Required Content
    • Response Size (24 KB)
    • Character Lengths (8000)

Django-Alexa

pip install django-alexa
INSTALLED_APPS = [
    'django-alexa',
    ...
]
urlpatterns = [
    url(r'^', include('django_alexa.urls')),
    ...
]

https://example.com/alexa/ask/

Django-Alexa

export ALEXA_APP_ID_my_app=amzn1.echo-sdk-ams.app.e18a3326-4775-45f2-83n0-5eeg03832401
export ALEXA_APP_ID_preso=amzn1.echo-sdk-ams.app.e17a3326-4885-44f3-85a0-5eef03832401
export ALEXA_REQUEST_VERIFICATON=True  # Default = True
ALEXA_APP_IDS = dict(
    [(str(os.environ[envvar]), envvar.replace("ALEXA_APP_ID_", "")) \
     for envvar in os.environ.keys() \
    if envvar.startswith('ALEXA_APP_ID_')]
)

...

def validate_app_ids(value):
    """
    value - an alexa app id
    """
    if value not in ALEXA_APP_IDS.keys():
        msg = "{0} is not one of the valid " \
        "alexa skills application ids for this service".format(value)
        raise InternalError(msg)

Django-Alexa

.
├── __init__.py
├── core
│   ├── __init__.py
│   ├── alexa.py
│   ├── gunicorn.py
│   ├── models.py
│   ├── settings.py
│   ├── urls.py
│   ├── views.py
│   ├── wsgi.py
└── presentation
    ├── __init__.py
    └── alexa.py
INSTALLED_APPS = (
    ...
    'django_alexa',
    'myproject.core',
    'myproject.presentation',
)
from django_alexa.api import intent, ResponseBuilder

@intent
def LaunchRequest(session):
    """
    Default Start Session Intent
    """
    message = "Welcome."
    reprompt = "What would you like to do next?"
    return ResponseBuilder.create_response(message=message,
                                           reprompt=reprompt,
                                           end_session=False)

@intent
def HelpIntent(session):
    """
    Default Help Intent
    ---
    help
    info
    information
    """
    message = "No help was configured!"
    return ResponseBuilder.create_response(message=message)
from django_alexa.api import fields, intent, Slots, ResponseBuilder

HOUSES = ("gryffindor", "hufflepuff", "ravenclaw", "slytherin")


class PointsForHouseSlots(Slots):
    house = fields.AmazonCustom(label="HOUSE_LIST", choices=HOUSES)
    points = fields.AmazonNumber()

Slot Field Types

  • AmazonLiteral - Deprecated
  • AmazonNumber
  • AmazonDate
  • AmazonTime
  • AmazonDuration
  • AmazonFirstName
  • AmazonUSCity
  • AmazonUSState
  • AmazonFourDigitNumber
  • AmazonCustom - Requires predefined choices
@intent(slots=PointsForHouseSlots)
def AddPointsToHouse(session, house, points):
    """
    Direct response to add points to a house
    ---
    {house}
    {points}
    {points} {house}
    {points} points {house}
    add {points} points to {house}
    give {points} points to {house}
    """
    kwargs = {}
    kwargs['points'] = points or session.get('points')
    kwargs['house'] = house or session.get('house')
    if points is None:
        kwargs['message'] = "How many points?".format(house)
        kwargs["end_session"] = False
        return ResponseBuilder.create_response(**kwargs)
    if house is None:
        kwargs['message'] = "Which house?".format(points)
        kwargs["end_session"] = False
        return ResponseBuilder.create_response(**kwargs)
    else:
        ... do points adding work here ...
        kwargs['message'] = "{0} points added to house {1}.".format(points,
                                                                    house)
    return ResponseBuilder.create_response(**kwargs)
class ResponseBuilder(object):
    """
    Simple class to help users to build alexa response data
    """
    @classmethod
    def create_response(cls,
                        message=None, message_is_ssml=False,
                        reprompt=None, reprompt_is_ssml=False, reprompt_append=True,
                        title=None, content=None, card_type=None,
                        end_session=True, **kwargs):
        """
        Shortcut to create the data structure for an alexa response

        Output Speech:
        message - text message to be spoken out by the Echo
        message_is_ssml - If true the "message" is ssml formated and should be treated as such

        Reprompt Speech:
        reprompt - text message to be spoken out by the Echo
        reprompt_is_ssml - If true the "reprompt" is ssml formated and should be treated as such
        reprompt_append - If true the "reprompt" is append to the end of "message"

        Card:
        card_type - A string describing the type of card to render. ("Simple", "LinkAccount")
        title - A string containing the title of the card. (n/a for cards of type LinkAccount).
        content - A string containing the contents of the card (n/a for cards of type LinkAccount).
                  Note that you can include line breaks in the content for a card of type Simple.

        end_session - flag to determine whether this interaction should end the session

        kwargs - Anything added here will be persisted across requests if end_session is False
        """
>>> python manage.py alexa_intents

{
    "intents": [
        {
            "intent": "HelpIntent", 
            "slots": []
        }, 
        {
            "intent": "LaunchRequest", 
            "slots": []
        }, 
        {
            "intent": "AddPointsToHouse", 
            "slots": [
                {
                    "name": "house", 
                    "type": "HOUSE_LIST"
                }, 
                {
                    "name": "points", 
                    "type": "AMAZON.NUMBER"
                }
            ]
        }
    ]
}
>>> python manage.py alexa_custom_slots

HOUSE_LIST:
  gryffindor
  hufflepuff
  ravenclaw
  slytherin

>>> python manage.py alexa_utterances

HelpIntent help
HelpIntent info
HelpIntent information
AddPointsToHouse {points} {house}
AddPointsToHouse {points} points {house}
AddPointsToHouse add {points} points to {house}
AddPointsToHouse give {points} points to {house}

Define your skill, where it's hosted, intents, custom slot types and utterances in the amazon developer portal

Demo skill

Demo

@intent(slots=PointsForHouseSlots)
def AddPointsToHouse(session, house, points):
    ...
    kwargs = {}
    kwargs['launched'] = launched = session.get('launched')
    kwargs['marauder'] = marauder = session.get('marauder')
    kwargs['points'] = points = points or session.get('points')
    kwargs['house'] = house = house or session.get('house')
    if points is None:
        kwargs['message'] = "How many points?".format(house)
        kwargs["end_session"] = False
        return ResponseBuilder.create_response(**kwargs)
    if house is None:
        kwargs['message'] = "Which house?".format(points)
        kwargs["end_session"] = False
        return ResponseBuilder.create_response(**kwargs)
    if marauder:
        kwargs['message'] = "messers can not give points to houses, we lose them in the name of mischief!"
        kwargs['message'] += " {0} points removed from house {1}.".format(randint(1,10), house or HOUSES[randint(0, 3)])
        kwargs['reprompt'] = "What mischief brings you here?"
        kwargs['end_session'] = False
    else:
        if launched:
            kwargs['reprompt'] = "What house would you like to give points to?"
            kwargs['end_session'] = False
        kwargs['message'] = "{0} points added to house {1}.".format(points, house)
        kwargs.pop("house")
        kwargs.pop("points")
    return ResponseBuilder.create_response(**kwargs)

Designing for the Voice

  • Make it clear that the user needs to respond
  • Don't assume the user knows what to say or do
  • Keep it brief
  • Avoid overwhelming users with to many choices
  • Only ask necessary questions
  • Refrain from using confirmation
  • Obtain one piece of information at a time
  • Use your reponse card for delivering complex information
  • Pageinate responses into small chunks
  • Avoid jargon in your responses
  • Utilize reprompts for errors or provideing guidance
  • Expect the unexpected

Q & A

Django-Alexa

By Kyle Rockman

Django-Alexa

Amazon Alexa Skills Kit integration for Django

  • 3,041