Lazysusan application structure¶
The Lazysusan framework mostly revolves around a yaml file where you configure how your Alexa skill should behave. As noted in the Introduction section Lazysusan will inspect the user’s current state, current Intent and then lookup how to respond to the request.
There are different classes of responses which Lazysusan supports:
Static responses¶
In a very simple Alexa skill responses will be static, meaning they are predetermined and never change. An example of this would be our scrambled eggs example:
user: Alexa, ask recipe helper how to make scrambled eggs
Alexa: Welcome to simple recipe helper. Would you like to make some scrambled eggs?
In this example the steps to make scrambled eggs will never change.
To build an app such as this we simply define the responses in a file names states.yml
. This
file may be named anything really...eventually you will put the name and full path to this file in
your application code so Lazysusan can read it.
Let’s take a look at an example file and walk through the details
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | initialState:
response:
shouldEndSession: false
outputSpeech:
type: SSML
ssml: >
<speak>
Welcome to simple recipe helper. Would you like to make some scrambled
eggs?
</speak>
reprompt:
type: SSML
ssml: >
<speak>
Would you like to make some scrambled eggs?
</speak>
branches:
AMAZON.YesIntent: ingredientsScrambledEggs
default: goodBye
|
Note
Every states.yml
file must contain an initialState
block which is the entry point to
your application
A states.yml
file will contain multiple blocks where each block has a unique name and
corresponds to an Alexa request.
Defining States¶
Lazysusan doesn’t add any syntactic sugar or do any checking of the responses defined in your
states.yml
file. The structure defined in your file is sent back as a response (mostly)
unadulterated. Therefore, it’s your responsibility to make sure the response is valid and structured
properly according to Amazon’s latest specs.
States can be declared using this format:
stateName:
response:
...
is_state: [True|False]
branches:
...
The stateName
is required as it is how each state is identified. These names
are also used when defining branches
.
Underneath the stateName
there are three possible key choices: response
,
is_state
, and branches
.
response¶
The response
key is the response that is returned to the Alexa device.
Lazysusan will not perform any major alterations to this construct other than
converting it to JSON to be returned to the requesting device. Therefore, it is
your responsibility to make sure your responses adhere to the Alexa platform
schema for defining a response. This is extremely powerful because you are able
to use the latest API functionality from Amazon without needing to update
Lazysusan.
is_state¶
This key will default to True
so you only need to set it if you need value
to be False
. When is_state
is set to False
, the response will be
returned, but the cookie or DynamoDB session will not be updated and state
transition will not occur. This is primarily used when dealing with long form
audio callbacks.
branches¶
Underneath the branches key will be a list of intent - state pairs. These are
used to say that when I am in state stateName
and the user has invoked
intent A
, transition to the state for intent A
or fallback to the
default
. It is recommended to always provide a default
branch unless
skill execution is terminated at this state.
Dynamic responses¶
There are two types of dynamic responses:
Both types of callbacks are refenced the same way in your states.yml
file.
In the branches
section of the desired state, for the desired intent, you
will reference your callback using the following format:
!!python/name:callbacks.name_of_function
This format assumes that you have a callbacks
folder for all of your dynamic
responses and that all of them can be retrieved from the __init__.py
file.
Dynamically Returning a State¶
The easiest class of dynamic responses is taking slot value input and then returning the desired static state for that input. For example you could have a callback function defined as:
1 2 3 4 5 6 7 8 9 | def choose_recipe(request, *args, **kwargs):
slot = (request.get_slot_value("Recipes")).lower()
if "scrambled" in slot:
return "ingredientsScrambledEggs"
if ("fried" in slot or "fry" in slot):
return "ingredientsFriedEggs"
return "invalidChoosePath"
|
This assumes that if you will have ingredientsScrambledEggs
and
ingredientsFriedEggs
states defined in your states.yml
file and will
tell the framework to transition to that state.
Return a Computed Response¶
For more complex responses, you can write a callback function that will modify a
predefined shell state, which you will need to define in your states.yml
file, for computed responses. An example callback function may look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import yaml
from lazysusan.logger import get_logger
from lazysusan.response import build_response_payload
def compute_recipe(offset=0, **kwargs):
request = kwargs["request"]
session = kwargs["session"]
state_machine = kwargs["state_machine"]
log = get_logger()
log.debug("Set Computed Recipe")
response = state_machine["preDefined"]["response"]
response["outputSpeech"]["SSML"] = """
<speak>
This is some dummy content to show that you can return a dynamic
response.
</speak>
"""
session.update_state("goodBye")
return build_response_payload(response, session.get_state_params())
|
While this is a fairly simple example, there are a few things to take note of.
First, since this is a Python function, you can perform any calculations, api calls, etc. that you need to make in or to generate a response.
Second, state_machine["preDefined"]
is defined in your states.yml
file
and you have full access to it. This way you can get some boilerplate definition
out of the way, or you can do it the hard way and define the dict within this
function.
Third, you are responsible for updating the state. This allows you to perform more fine tuned state transitions.
Finally, you are responsible for building the response payload. We have written a helper function build this for you.