1

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)
aL_eX
  • 1,453
  • 2
  • 15
  • 30
user9127040
  • 71
  • 1
  • 7
  • are you getting any errors? is it working as expected? – sid8491 Jan 09 '18 at 12:22
  • It didn't, i got ` "Syntax error in module 'lambda_function'"` when i saved. – user9127040 Jan 09 '18 at 12:28
  • debug it and find the errors, you can see the log in Cloudwatch. i see `)` 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. – sid8491 Jan 09 '18 at 12:30
  • @sid8491 correct these were adjusted, do you think you can make find the issues stopping it from working properly and adjusting – user9127040 Jan 09 '18 at 13:50
  • as i previously said, you are trying to jump to advance things before understanding basics. thats why you are facing problems. – sid8491 Jan 09 '18 at 13:53
  • @sid8491 Hi i think you could help there https://stackoverflow.com/questions/48206412/how-can-i-import-and-properly-run-pip-imported-libraries-on-lambda thanks. – user9127040 Jan 11 '18 at 11:55

1 Answers1

1

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.

sid8491
  • 6,622
  • 6
  • 38
  • 64
  • I corrected these 2 mistakes and when i ran it in with lex i got the following error `An error has occurred: Invalid Lambda Response: Received error response from Lambda: Unhandled` – user9127040 Jan 09 '18 at 13:30
  • no one can help you without the logs. cloudwatch will be your savior. – sid8491 Jan 09 '18 at 13:51
  • not fully done but your debugging advice and link were very helpful. – user9127040 Jan 11 '18 at 09:06
  • cool, try to understand the CLoudwatch logs and add log statements where you have doubt using `logger.debug(object)`. upvote and mark accept if found useful. :) – sid8491 Jan 11 '18 at 09:07