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?
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
isTOP_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