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:
The updateNewUser is a custom slot type (expand values) with following entries:
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:
- User declines at confirmIntent, bot asks which value to update:
- User says "name", bot jumps to confirmation prompt
- If exact matching word is provided to Lex, it accepts it:
- Returning what value bot stored in updateNewUser in case of "name" as input:
- Another example:
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?