RASA form validation

I have created a form in rasa in which i want to validate the slots for that i want to create generic validate method which can handle any number of slots. I have the slot details in an excel sheet. I am picking the slot names from there but i don’t know how to keep track of which slots are filled and which are not and how to ask for the next slot… Can anyone please help me.

Hi @indranil180! Can you show me what your stories look like? Are you trying to create a form with a dynamic number of slots?

yes i am looking to create a form with dynamic number of slots.

@indranil180 What do your stories look like? Can you tell me more about why you want to do this?

Stories.md

## story 01
* greet
    - utter_greet

## story 02
* goodbye
    - utter_goodbye

## story 03
* intent_add
    - action_static

## happy path
* greet
    - utter_greet
* request_restaurant
    - dynamic_form
    - form{"name": "dynamic_form"}
    - form{"name": null}
    - utter_slots_values

This is how my stories look. We want our bot to be ready to use so rather than coding for specific purpose we want it to handle dynamic slots. Maybe in some scenario slots are more than one in some scenarios we have just one slot. So to handle that thing we want to create a generic Form class which can handle everything.

Hey @indranil180, in order to create a form with a custom set of slots, your domain file needs to list each of those slots, and also contain a utter_ask_{slot} action for each slot. These elements cannot be set up after training your model, so they will need to be in your domain file before you train it. The closest you can get to a form with dynamic list of slots is, for the moment, to generate your domain file using a script, and then training the model. If the slot list changes, you will need to re-generate the domain and re-train your model.

If your Excel file only changes every now and then, this could be a viable solution to your problem.

I have written code which set these elements up before training model. The challenge i am facing is while validating the dynamic slots. Can you help me in the validation step. Plus i have one more question do i need template for form or i can send those validation messages through utter_message rather than using utter_template.

Hey @indranil180, you can have a generic validate() method in your custom FormAction where you can take in the slot values and validate the same.

For your second question, you can have an utter_message instead of an utter_template to send messages. Both will work fine.

Thanks for your response. I am following below code to achieve generic validation.

class insuranceform(FormAction):
def name(self):
	return 'insurance_form'

@staticmethod
def required_slots(tracker):
	return ["firstname","lastname","gender","dob","email","contact","address","city","state","zipcode","ssn"]

def validate(self, dispatcher, tracker, domain):
	slot_values = self.extract_other_slots(dispatcher, tracker, domain)

	
	slot_to_fill = tracker.get_slot(REQUESTED_SLOT)
	
	if slot_to_fill:
		slot_values.update(self.extract_requested_slot(dispatcher, tracker, domain))
	#response1 = "HELLOHELLO "
	#dispatcher.utter_message(response1)
	for slot, value in slot_values.items():
		#dispatcher.utter_message("line3")

		if slot == 'firstname':
			if value.replace(" ","").isalpha() == False:
				dispatcher.utter_template('utter_wrong_firstname', tracker)
				slot_values[slot] = None
			else:
				dispatcher.utter_message("Hi {}".format(value))
		elif slot == 'lastname':
			if value.replace(" ","").isalpha() == False:
				dispatcher.utter_template('utter_wrong_lastname', tracker)
				slot_values[slot] = None
		elif slot == 'gender':
			if value.lower() not in ["male","female"]:
				dispatcher.utter_template('utter_wrong_gender', tracker)
				slot_values[slot] = None
		elif slot == 'dob':
			try:
				datetime.strptime(value, '%d-%m-%Y')
			except ValueError:
				dispatcher.utter_template('utter_wrong_dob', tracker)
				slot_values[slot] = None
		elif slot == 'email':
			email = re.compile(r'(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)')
			mo = email.search(value)
			if mo == None:
				dispatcher.utter_template('utter_wrong_email', tracker)
				slot_values[slot] = None
		elif slot == 'contact':
			if len(value) != 10 or value.isdigit() == False:
				dispatcher.utter_template('utter_wrong_contact', tracker)
				slot_values[slot] = None
		elif slot == 'city':
			if value.replace(" ","").isalpha() == False:
				dispatcher.utter_template('utter_wrong_city', tracker)
				slot_values[slot] = None
		elif slot == 'state':
			if value.replace(" ","").isalpha() == False:
				dispatcher.utter_template('utter_wrong_state', tracker)
				slot_values[slot] = None
		elif slot == 'zipcode':
			if len(value) != 5 or value.isdigit() == False:
				dispatcher.utter_template('utter_wrong_zipcode', tracker)
				slot_values[slot] = None
		elif slot == 'ssn':
			ssn = re.compile(r'^\d{3}-\d{2}-\d{4}$')
			mo = ssn.search(value)
			if mo == None:
				dispatcher.utter_template('utter_wrong_ssn', tracker)
				slot_values[slot] = None


	return [SlotSet(slot, value) for slot, value in slot_values.items()]

  	def submit(self, dispatcher, tracker, domain):
	#response1 = "HELLOHELLOaa "	
	#dispatcher.utter_message(response1)
	firstname=tracker.get_slot("firstname")
	lastname=tracker.get_slot("lastname")
	gender=tracker.get_slot("gender")
	dob=tracker.get_slot("dob")
	email=tracker.get_slot("email")
	contact=tracker.get_slot("contact")
	address=tracker.get_slot("address")
	city=tracker.get_slot("city")
	state=tracker.get_slot("state")
	zipcode=tracker.get_slot("zipcode")
	ssn=tracker.get_slot("ssn")
	SlotSet('zipcode', None)
	SlotSet('zipcode', "11111")
	dispatcher.utter_message("Here is the details you have entered.Please review")
	dispatcher.utter_message('''First Name : {}
							 \nLast Name : {} 
							 \nGender : {} 
							 \nDOB : {} 
							 \nEmail : {} 
							 \nContact : {} 
							 \nAddress : {} 
							 \nCity : {}  
							 \nstate : {} 
							 \nZip code : {} 
							 \nSSN : {}'''.format(firstname,lastname,gender,dob,email,contact,address,city,state,zipcode,ssn))
	
	dispatcher.utter_button_message("Do you want to change any field", [{"title": "Yes", "payload": "/affirm"},{"title": "No", "payload": "/deny"}])
	return []

I don’t know how i can pass the dynamic slot name in if condition if slot == 'firstname': I know it sounds noob but i don’t know how to do that. Please help.

Sorry, I’m not sure I understand the question: as you iterate through each element of slot_values.items(), shouldn’t the variable slot take the value of each slot name?

i solved that issue. Thanks for the help. I have just one question. Every time the form gets called it will ask for the required slots. I have not created any templates to i didn’t got the ask question. I tried to specify the question from utter_message but it seems it is not working. So will it only work with a template or i can do some work around with utter_message ?

For every slot defined, you should define a utter_ask_{slot} action as well. This action will be used to ask the user for the value of its slot. For example:

templates:
  utter_ask_cuisine:
    - text: "what cuisine?"
  utter_ask_num_people:
    - text: "how many people?"
  utter_ask_outdoor_seating:
    - text: "do you want to seat outside?"

This blog post gives some great examples of how to use forms and slots. Hope it’s useful!

can’t i make these template as generic so that i can call them from my validate method.

Expectation:

templates:
  utter_ask_{slots}:
    - text: what is {slots} 

And from the code i can call dispatcher.utter_message("utter_ask_{}".format(slot),tracker)

is this possible ?

I don’t think that’s possible, sorry. You’ll need to have a utter_ask_{slot} on your domain file for each slot before you train your model.

Thanks actually i am struggling quite a lot because we want to make the chatbot to handle dynamic data.thats why i want this approach. I am using tracker.get_slot(REQUESTED_SLOT) and self.extract_other_slots(dispatcher, tracker, domain) to do some work around but it turns out the values are empty during the first time form is called. Once i enter something in input then only these values are getting updated. So i am looking for some alternative.

Hi, can you please help me make this validation dynamic? I’m facing issues. particularly, in running the action file I’m facing an indentation error. I have created slots in the domain file and my action file has a code similar to yours

class InsuranceForm(FormAction): def name(self): return “insurance_form”

def required_slots(self,tracker) -> List[Text]:
    return ["name","email","dateofbirth","number","address","zip"]
def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
    return {
            "name": [
                self.from_text(),
            ],
            "email": [
                self.from_text(),
            ],
            "dateofbirth": [
                self.from_text(),
            ],
            "number": [
                self.from_text(),
            ],
            "address": [
                self.from_text(),
            ],
            "zip": [
                self.from_text(),
            ],
        }

def validate(self, dispatcher, tracker, domain):
    slot_values = self.extract_other_slots(dispatcher, tracker, domain)


    slot_to_fill = tracker.get_slot(REQUESTED_SLOT)

    if slot_to_fill:
	    slot_values.update(self.extract_requested_slot(dispatcher, tracker, domain))

    for slot, value in slot_values.items():
	    if slot == 'name':
		    if value.replace(" ","").isalpha() == False:
			    dispatcher.utter_template('utter_wrong_name', tracker)
			    slot_values[slot] = None
		    else:
			    dispatcher.utter_message("Hi {}".format(value))     
        elif slot == 'address':
		    if value.replace(" ","").isalpha() == False:
			    dispatcher.utter_template('utter_wrong_address', tracker)
			    slot_values[slot] = None
	    elif slot == 'dateofbirth':
		    try:
			    datetime.strptime(value, '%d-%m-%Y')
		    except ValueError:
		    	dispatcher.utter_template('utter_wrong_dob', tracker)
		    	slot_values[slot] = None
	    elif slot == 'email':
		    email = re.compile(r'(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)')
		    mo = email.search(value)
		    if mo == None:
			    dispatcher.utter_template('utter_wrong_email', tracker)
			    slot_values[slot] = None
	    elif slot == 'number':
		    if len(value) != 10 or value.isdigit() == False:
			    dispatcher.utter_template('utter_wrong_contact', tracker)
			    slot_values[slot] = None
	    elif slot == 'zip':
	    	if len(value) != 5 or value.isdigit() == False:
		    	dispatcher.utter_template('utter_wrong_zipcode', tracker)
		    	slot_values[slot] = None

return [SlotSet(slot, value) for slot, value in slot_values.items()]

def submit( self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any], ) -> List[Dict]:

	name=tracker.get_slot("name")
    dateofbirth=tracker.get_slot("dateofbirth")
    email=tracker.get_slot("email")
    contact=tracker.get_slot("number")
    address=tracker.get_slot("address")
    zipcode=tracker.get_slot("zipcode")
    dispatcher.utter_template("utter_insurance", tracker)    
    return []

Can you please be more specific about the error. Can you share the logs ?

My stories file is this

happy path

  • greet

    • utter_greet

insurance path

  • insurance_form

  • form{“name”:“insurance_form”}

  • form{“name”:“null”}

say personal

  • insurance

    • insurance_form

    • form{“name”:“insurance_form”}

    • form{“name”:“null”}

    • utter_insurance

insurance path 1

  • greet

    • utter_greet
  • insurance

  • insurance_form

  • form{“name”:“insurance_form”}

  • form{“name”:“null”}

  • affirm

    • utter_happy

And the error I’m getting when I run actions server is this:

File “c:\users\asus\anaconda3\envs\rasa\lib\runpy.py”, line 85, in run_code exec(code, run_globals) File "C:\Users\ASUS\Anaconda3\envs\rasa\Scripts\rasa.exe_main.py", line 7, in File “c:\users\asus\anaconda3\envs\rasa\lib\site-packages\rasa_main_.py”, line 92, in main cmdline_arguments.func(cmdline_arguments) File “c:\users\asus\anaconda3\envs\rasa\lib\site-packages\rasa\cli\run.py”, line 52, in run_actions sdk.main_from_args(args) File “c:\users\asus\anaconda3\envs\rasa\lib\site-packages\rasa_sdk_main_.py”, line 21, in main_from_args args.auto_reload, File “c:\users\asus\anaconda3\envs\rasa\lib\site-packages\rasa_sdk\endpoint.py”, line 137, in run action_package_name, cors_origins=cors_origins, auto_reload=auto_reload File “c:\users\asus\anaconda3\envs\rasa\lib\site-packages\rasa_sdk\endpoint.py”, line 80, in create_app executor.register_package(action_package_name) File “c:\users\asus\anaconda3\envs\rasa\lib\site-packages\rasa_sdk\executor.py”, line 250, in register_package self._import_submodules(package) File “c:\users\asus\anaconda3\envs\rasa\lib\site-packages\rasa_sdk\executor.py”, line 206, in _import_submodules package = self._import_module(package) File “c:\users\asus\anaconda3\envs\rasa\lib\site-packages\rasa_sdk\executor.py”, line 227, in import_module module = importlib.import_module(name) File "c:\users\asus\anaconda3\envs\rasa\lib\importlib_init.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File “”, line 1006, in _gcd_import File “”, line 983, in _find_and_load File “”, line 967, in _find_and_load_unlocked File “”, line 677, in _load_unlocked File “”, line 724, in exec_module File “”, line 860, in get_code File “”, line 791, in source_to_code File “”, line 219, in _call_with_frames_removed File “C:\Users\ASUS\Desktop\QuantumIT Innovation\template_data_finance_data_create_by_me\actions.py”, line 80 elif slot == ‘address’: ^ TabError: inconsistent use of tabs and spaces in indentation

domain.yml:

session_config:

session_expiration_time: 60

carry_over_slots_to_new_session: true

intents:

  • greet

  • goodbye

  • affirm

  • deny

  • mood_great

  • mood_unhappy

  • bot_challenge

  • insurance

slots:

address:

auto_fill: false

type: unfeaturized

email:

auto_fill: false

type: unfeaturized

name:

auto_fill: false

type: unfeaturized

dateofbirth:

auto_fill: false

type: unfeaturized

number:

auto_fill: false

type: unfeaturized

zip:

auto_fill: false

type: unfeaturized

responses:

utter_ask_address:

- text: "Enter your address?"

utter_ask_email:

- text: "Enter your Email?"

utter_ask_name:

- text: "Enter your name?"

utter_ask_dateofbirth:

- text: "Enter your date of birth"

utter_ask_number:

- text: "Enter your Number?"

utter_ask_zip:

- text: "Enter your zip code?"

utter_greet:

  • text: Hi, thanks for reaching out to us at ABC Insurance Company. What type of insurance are you looking for today?

utter_cheer_up:

utter_did_that_help:

  • text: Did that help you?

utter_happy:

  • text: Great, carry on!

utter_goodbye:

  • text: Bye

utter_iamabot:

  • text: I am a bot, powered by Rasa.

utter_insurance:

- text: Help us get you free quotes from top insurers with the lowest premiums.

utter_wrong_name:

- text: The name you entered is not alphabetical. Please enter a valid name.

utter_wrong_email:

- text: The email you entered is invalid. Please enter a valid email.

utter_wrong_dob:

- text: The date of birth is invalid. Please enter a valid date of birth.

utter_wrong_zipcode:

- text: Wrong zip code. Please enter a valid zipcode

utter_wrong_contact:

- text: Wrong contact number. Please enter a valid number

actions:

  • utter_greet

  • utter_goodbye

  • utter_iamabot

  • utter_insurance

forms:

  • insurance_form