I want to create develop a chatbot using Lex's interface, as a first step i found a ScheduleAppointment
default bot so i decided to do adjustments to it. The default prompts in this bot are date
, time
and appointmenttype
. As a first step i went to using blueprint lex-make-appointment-python
https://console.aws.amazon.com/lambda/home?region=us-east-1#/create/new?bp=lex-make-appointment-python and had to change lots of the default stuff there, for example it has an exact appointment time and days etc while the version i want to work on before developing it any further in lambda
is one that would take ANY TIME
and ANY DAY
for example i can't get an error if i ask to schedule an appointment tomorrow
but if the bot asks for date and i put something like hfujdhfu
or banana
i should be asked what is the date?
again.
All that being said and done this is my version of that code after adjusting it :
import json
import dateutil.parser
import datetime
import time
import os
import math
import random
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
""" --- Helpers to build responses which match the structure of the necessary dialog actions --- """
def elicit_slot(session_attributes, intent_name, slots, slot_to_elicit, message, response_card):
return {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'ElicitSlot',
'intentName': intent_name,
'slots': slots,
'slotToElicit': slot_to_elicit,
'message': message,
'responseCard': response_card
}
}
def confirm_intent(session_attributes, intent_name, slots, message, response_card):
return {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'ConfirmIntent',
'intentName': intent_name,
'slots': slots,
'message': message,
'responseCard': response_card
}
}
def close(session_attributes, fulfillment_state, message):
response = {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'Close',
'fulfillmentState': fulfillment_state,
'message': message
}
}
return response
def delegate(session_attributes, slots):
return {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'Delegate',
'slots': slots
}
}
def build_response_card(title, subtitle, options):
"""
Build a responseCard with a title, subtitle, and an optional set of options which should be displayed as buttons.
"""
buttons = None
if options is not None:
buttons = []
for i in range(min(5, len(options))):
buttons.append(options[i])
return {
'contentType': 'application/vnd.amazonaws.card.generic',
'version': 1,
'genericAttachments': [{
'title': title,
'subTitle': subtitle,
'buttons': buttons
}]
}
""" --- Helper Functions --- """
def parse_int(n):
try:
return int(n)
except ValueError:
return float('nan')
def try_ex(func):
"""
Call passed in function in try block. If KeyError is encountered return None.
This function is intended to be used to safely access dictionary.
Note that this function would have negative impact on performance.
"""
try:
return func()
except KeyError:
return None
def isvalid_date(date):
try:
dateutil.parser.parse(date)
return True
except ValueError:
return False
def build_time_output_string(appointment_time):
hour, minute = appointment_time.split(':') # no conversion to int in order to have original string form. for eg) 10:00 instead of 10:0
if int(hour) > 12:
return '{}:{} p.m.'.format((int(hour) - 12), minute)
elif int(hour) == 12:
return '12:{} p.m.'.format(minute)
elif int(hour) == 0:
return '12:{} a.m.'.format(minute)
return '{}:{} a.m.'.format(hour, minute)
def make_appointment(intent_request):
"""
Performs dialog management and fulfillment for booking a dentists appointment.
Beyond fulfillment, the implementation for this intent demonstrates the following:
1) Use of elicitSlot in slot validation and re-prompting
2) Use of confirmIntent to support the confirmation of inferred slot values, when confirmation is required
on the bot model and the inferred slot values fully specify the intent.
"""
appointment_type = intent_request['currentIntent']['slots']['AppointmentType']
date = intent_request['currentIntent']['slots']['Date']
appointment_time = intent_request['currentIntent']['slots']['Time']
source = intent_request['invocationSource']
output_session_attributes = intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {}
booking_map = json.loads(try_ex(lambda: output_session_attributes['bookingMap']) or '{}')
if source == 'DialogCodeHook':
# Perform basic validation on the supplied input slots.
slots = intent_request['currentIntent']['slots']
if not appointment_type:
return elicit_slot(
output_session_attributes,
intent_request['currentIntent']['name'],
intent_request['currentIntent']['slots'],
'AppointmentType',
{'contentType': 'PlainText', 'content': 'What type of appointment would you like to schedule?'},
build_response_card(
'Specify Appointment Type', 'What type of appointment would you like to schedule?',
build_options('AppointmentType', appointment_type, date, None)
)
)
if appointment_type and not date:
return elicit_slot(
output_session_attributes,
intent_request['currentIntent']['name'],
intent_request['currentIntent']['slots'],
'Date',
{'contentType': 'PlainText', 'content': 'When would you like to schedule your {}?'.format(appointment_type)},
build_response_card(
'Specify Date',
'When would you like to schedule your {}?'.format(appointment_type),
build_options('Date', appointment_type, date, None)
)
)
)
message_content = 'What time on {} works for you? '.format(date)
if appointment_time:
output_session_attributes['formattedTime'] = build_time_output_string(appointment_time)
)
available_time_string = build_available_time_string(appointment_type_availabilities)
return elicit_slot(
output_session_attributes,
intent_request['currentIntent']['name'],
slots,
'Time',
{'contentType': 'PlainText', 'content': '{}{}'.format(message_content, available_time_string)},
build_response_card(
'Specify Time',
'What time works best for you?',
build_options('Time', appointment_type, date, booking_map)
)
)
return delegate(output_session_attributes, slots)
duration = get_duration(appointment_type)
""" --- Intents --- """
def dispatch(intent_request):
"""
Called when the user specifies an intent for this bot.
"""
logger.debug('dispatch userId={}, intentName={}'.format(intent_request['userId'], intent_request['currentIntent']['name']))
intent_name = intent_request['currentIntent']['name']
# Dispatch to your bot's intent handlers
if intent_name == 'MakeAppointment':
return make_appointment(intent_request)
raise Exception('Intent with name ' + intent_name + ' not supported')
""" --- Main handler --- """
def lambda_handler(event, context):
"""
Route the incoming request based on intent.
The JSON body of the request is provided in the event slot.
"""
# By default, treat the user request as coming from the America/New_York time zone.
os.environ['TZ'] = 'America/New_York'
time.tzset()
logger.debug('event.bot.name={}'.format(event['bot']['name']))
return dispatch(event)
There is )
at line 171 and line 175 without any (
which must be causing syntax error. many code lines are unreachable because you are coding return
before them but those won't cause syntax error.
You can watch the logs on Cloudwatch.
The blueprint which you are using is very complex and not beginner friendly. You really should be using this blueprint for starting. That is my suggestion.
Also, since you are using response_card
so please be aware that the response cards won't be shown in the Lex Console window. It will work in Facebook and Slack though.