Amazon Lex slot won't save value in it. Returns empty

1k views Asked by At

I am trying to implement a check that gives the user an option to update the value of a specific slot within an intent after user Denies at confirmIntent step.

I have a newUser intent that has the following slots:

Slots defined in intent

The updateNewUser is a custom slot type (expand values) with following entries:

enter image description here

Now, when a user Denies at confirmIntent step, I ask the user the slot which he/she wants to update/change, and store the user input value in updateNewUser slot using Lambda function elicitSlot. But here comes the problem.

  • Bot plays confirmation prompt with all captured values
  • User says "No" at confirmIntent
  • Bot elicits the updateNewUser slot by asking "What value do you want to update?"
  • If user says a word that is an exact match of the entries of updateNewUser custom slot type, then it works fine
  • If user says a similar (not exact match) word, like "name" or "email" or "number", no value is stored inside updateNewUser and the bot continues to confimation prompt.

Following is my Lambda code:

def newUser(intent_request):

slot_details = intent_request['currentIntent']['slotDetails']
slots = intent_request['currentIntent']['slots']
f_name = slots['f_name']
l_name = slots['l_name']
cellNum = slots['cellNum']
emailAdd = slots['emailAdd']
updateSlot = slots['updateNewUser']
session_attributes = intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {}
confirmation_status = intent_request['currentIntent']['confirmationStatus']


if intent_request['invocationSource'] == 'DialogCodeHook':
    validation_result = validate_newusr(intent_request['currentIntent']['slots'], intent_request['currentIntent']['slotDetails'])
    if not validation_result['isValid']:
        slots = intent_request['currentIntent']['slots']
        slots[validation_result['violatedSlot']] = None
        slots['updateNewUser'] = None
        return elicit_slot(
            session_attributes,
            intent_request['currentIntent']['name'],
            slots,
            validation_result['violatedSlot'],
            validation_result['message']
        )
    
    if confirmation_status == 'Denied':
        slots = intent_request['currentIntent']['slots']
        
        return elicit_slot(
            session_attributes,
            intent_request['currentIntent']['name'],
            slots,
            'updateNewUser',
            {
                'contentType': 'SSML',
                'content': '<speak>Which value do you want to change?</speak>'
            }
        )
    
    
    return delegate(
        session_attributes,
        intent_request['currentIntent']['slots']
    )
    
return close(
        session_attributes,
        'Fulfilled',
        {
            'contentType': 'SSML',
            'content': '<speak>Your account has been created, and you will shortly receive an email to set up your password. Now, would you like to make a reservation for yourself, or for somone else?</speak>'
        }
    )

And following is the validation code:

def validate_newusr(slots, slot_details):

f_name = slots['f_name']
l_name = slots['l_name']
cellNum = slots['cellNum']
emailAdd = slots['emailAdd']
updateSlot = slots['updateNewUser']


if f_name:
    if ' ' in f_name:
        f_name = f_name.replace(' ', '')
        slots['f_name'] = f_name
            
if l_name:
    if ' ' in l_name:
        l_name = l_name.replace(' ', '')
        slots['l_name'] = l_name

if cellNum and (len(cellNum) != 10):
    return build_validation_result(
        False,
        'cellNum',
        '<speak>Please provide a valid, 10 digit phone number.</speak>'
    )
    
    
return build_validation_result(     #I use this to check whether updateNewUser stores something or not. But of course I only paste this code here at confirmIntent step; not before.
            False,
            'updateNewUser',
            str(updateSlot)
        )


    
if updateSlot:                      # this code won't run because updateSlot is empty
    
    return build_validation_result(     
            False,
            'updateNewUser',
            str(updateSlot)
        )
    
    if len(slot_details['updateNewUser']['resolutions']) == 1 and str(list(slot_details['updateNewUser']['resolutions'][0].values())[0]).lower() in ['last name','first name', 'cell phone number', 'email address']:
        slots['updateNewUser'] = list(slot_details['updateNewUser']['resolutions'][0].values())[0]
    
    if len(slot_details['updateNewUser']['resolutions']) == 0 or str(list(slot_details['updateNewUser']['resolutions'][0].values())[0]).lower() not in ['last name','first name', 'cell phone number', 'email address']:
        return build_validation_result(
            False,
            'updateNewUser',
            '<speak>Sorry, I could not understand. Please say first name, last name, phone number, or email address.</speak>'
        )
        
    if slots['updateNewUser'] == 'first name':
        return build_validation_result(
            False,
            'f_name',
            '<speak>Please say your updated first name.</speak>'
        )
        
    if slots['updateNewUser'] == 'last name':
        return build_validation_result(
            False,
            'l_name',
            '<speak>Please say your updated last name.</speak>'
        )
        
    if slots['updateNewUser'] == 'cell phone number':
        return build_validation_result(
            False,
            'cellNum',
            '<speak>Please say, or type your updated phone number.</speak>'
        )
        
    if slots['updateNewUser'] == 'email address':
        return build_validation_result(
            False,
            'emailAdd',
            '<speak>Please say your updated email address.</speak>'
        )
        
return {'isValid': True}

Here are some screenshots of the chat:

  1. User declines at confirmIntent, bot asks which value to update:

enter image description here

  1. User says "name", bot jumps to confirmation prompt

enter image description here

  1. If exact matching word is provided to Lex, it accepts it:

enter image description here

  1. Returning what value bot stored in updateNewUser in case of "name" as input:

enter image description here

  1. Another example:

enter image description here

It only accepts exact match in my case. But if I make updateNewUser slot as mandatory/required, it starts to accept similar words too and works fine. But I need this slot as optional. My goal is to update a single slot instead of repeating the whole intent. What am I doing wrong?

1

There are 1 answers

0
Reegz On

At first glance, I would suggest changing the resolution of the updateNewUser slot type to restricted values and synonyms. That will enable you to add synonyms that match with your base value.

Ensure that your valueSelectionStrategy is TOP_RESOLUTION so that you get the base value rather than the user's raw input.

This simplifies your lambda as you would only need to cater for a single value per line.

Custom Slot Types