I have a form which asks for a bunch of things that each need different clarification. for example if the form asks for a suburb an postcode it needs to check if they are a valid match before proceeding.
Currently in validate() i can do this check, set those slots to None again so the form knows to fill them, and issue the appropriate utterance. However, this will result in the form asking the original question again, on top of the situation-specific validation i already sent.
For example, in the validate() function I have python code which checks against a database and if not matches are found, it utters a message listing the top 3 matches. As it stands, Rasa will then ask again for ‘please enter address’ after this, which i would like to avoid.
I have played with UserUtteranceReverted() and so on but nothing seems to work, the FormAction will always replay that question no matter what.
I understand maybe i should do this in stories, but I feel like this would be quite complicated, as i would need stories showing how the form was interrupted, then have some custom actions maybe to do the parsing, and maybe a checkpoint to make sure it is looped over until complete.
I guess I could also just define a custom action that is called from within the form and which calls itself and sets/unsets slots until the condition is satisfied, but that seems a little wonky.
Do you mean something like when the third slot value in the sequence of slots is wrong, then the validate() is asking for all the slot values again? It should not ask the original question again, since that slot is already validated.
Ah no not like that. What i’m talking about goes on here
which will always utter_ask_slot regardless of context, i.e. if it is the first time the user has been asked this, or if the form is asking again because of clarification. One would expect these to be different, and so reasonably should be different paths in the stories which i’m still working on.
To your point about the example, the same thing i’m talking about would happen with just a single slot. I want it such that if you give it an input that fails validation, that the utter_failed message gives useful information, and isn’t immediately followed by utter_ask_slot, which is what the FormAction will always do you if you set the slot to None. It kinda makes sense that the validation step doesn’t allow crazy branching to happen under the hood otherwise Core would get confused, but it’d be neat for my current task =)
Oh I see. I get it now.
I have a simpler case where I have 3-4 required slots and I validate each slot. You can specify different utterances in required and validate method.
A simple example is a date slot. The first time the bot says please specify the date and if the user selects a wrong date, then in the validate I ask please choose an alternative date. But this is a simple scenario which still sticks to the same story. I’m not sure branching is possible with forms but yes, it will be very useful if we could do that.
Some sort of branching should be possible by specifiying conditions in required slots, although not very complicated.
Oh i see, you mean you have a ‘is_validated’ flag/slot? Nice idea. I had thought about using that but to enable story branching, perhaps I was over-complicating things! I think the trick is still to return a custom message after the utter_ask, not before.
EDIT: A possible way around this is to have a slot that stashes the message i want to send and have the utter_validate print that out
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))
if not slot_values:
raise ActionExecutionRejection(self.name(),
"Failed to validate slot {0} "
"with action {1}"
"".format(slot_to_fill,
self.name()))
for slot, value in slot_values.items():
if slot == 'DATE':
# put your validations here
dispatcher.utter_message('Sorry, please select a different date.')
slot_values[slot] = None
return [SlotSet(slot, value) for slot, value in slot_values.items()]
def request_next_slot():
Ah okay but this comes back to the original problem (unless you also redefined request next slot, the body of which is cut off in your post)
In your case, if Date is what i want to fill then it will utter your ‘sorry’ message but then also utter ask_date afterwards because date is None, right?
What I have done which seems to work for now is have an is_address_valid slot and a stashed_address slot. So when i do the validation, if the validation fails i set the valid slot to None so it goes into a loop there and at each step in the loop i put the suggested valid addresses in stashed_address so that i can give single utterance that is dynamic (basically a way to save information from python into the next loop of the form).
Probably I am overcomplicating things but i was wary of storing things in any other way because i expect multiple users each with their own trackers so i don’t want to store anywhere else i think
Hey, so it doesn not repeat the next slot again. Only the first time, it utters the statement in request_next_slot. If it fails, it will utter the sorry message and not the one in req_next_msg. Even though the slot is set to null, it won’t take the message from next_slot
Interesting, mine always re-asks the ‘ask_slot’ before getting to validate, I will investigate. To clarify: did you mean to include the code snippet of def request_next-slot() at the bottom?
So I was thinking, maybe you can have a slot (not a required slot, something like True/False) which is set the first time you enter validate() and then override the next_slot() to check for this slot and if True then do not utter the message in next_slot().
Yep, that’s what i’ve got going at the moment It’s a solid workaround but does make things a little clunky, as i have several forms that need it, adds to slot proliferation.