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.