SlotSet not working in FormAction

Hi everyone.

I have come across to this problem when I can’t return SlotSet in the form action and continue having the form on its own.

Suppose I have 2 slots, “A”, and “B”.

  1. I have to call an api and set the slot “A” with the response value.

  2. Check the value of slot “A”.

    • If “A” is None: No need of the form. Nothing should be executed.
    • If “A” is not None: I need to run a form action asking slot “B”, which should ask smth like "I have counted {"A"} for you. If you don't agree with it, type your suggestion below".
  3. Then I extract slot “B” from user answer and set my slot “B”. (If applicable)

So the question is how to do SlotSet inside form class? Without using separate Action for the slot filling step (step 1.) outside the form.

I have tried all of the following:

  • Defined a run(self, dispatcher, tracker, domain) method in my form class, where I called the api and returned SlotSet (imported from rasa_sdk.events) in that method. In the required_slots(tracker) method I checked the value of the slot “A” with tracker.get_slot("A") and returned “B” only if the slot “A” was filled. Eventually, SlotSet succeeded, but my form finished running after that (Action Server logs were like DEBUG rasa_sdk.executor - Finished running 'my_form'
  • The same as above, just changed the name run into something else. Even SlotSet didn’t work in this case. Not surprising. I didn’t call it anywhere and it wasn’t supposed to be run automatically. I didn’t call it in the required slots because the arguments are different in base class and the methods can’t be overwritten if arguments don’t match.
  • I tried the above mentioned case without giving dispatcher and domain as arguments. And called that method in required_slots method. I really hoped this would work, but it didn’t.
  • Instead of using the SlotSet from rasa_sdk.events I used SlotSet from rasa.core.events in a seperate method where I called the api and returned this SlotSet. Then called this method in required_slots. Didn’t work either.

So here are my inferences. SlotSet is only applicable in the methods named run and having run method in FormAction prevents the form of executing properly :(:disappointed:

I hope there is still a way. Looking forward to your suggestions.

P.S. Please don’t suggest to have a separate action for that and write good stories to always predict that action before the form. That is what I’m doing now :upside_down_face:

Try the pro tip in the forms tutorial.

Thanks for responding @Gehova.

However I have covered that part. My question is regarding to the 1st step: when I have to set a slot value. What I ask is how to do that inside my form class.

Hello @taguhi,

I might have a solution if you don’t mind overriding a form method. There is a request_next_slot(), it’s described like this:

Request the next slot and utter template if needed, else return None

By overriding this method, we can inject our logic right before the bot ask for the required_slot, like this:

def request_next_slot(
    self,
    dispatcher,  # type: CollectingDispatcher
    tracker,  # type: Tracker
    domain,  # type: Dict[Text, Any]
    ):
    # type: (...) -> Optional[List[Dict]]
        """
            Request the next slot and utter template if needed,
            else return None
            This function is override in order to check if the user is guest and 
            is asking about their own infos. The form will be deactivate if that is the case.
        """
        
        for slot in self.required_slots(tracker):
            logger = logging.getLogger(__name__)

            if self._should_request_slot(tracker, slot):
                // Call the API and get your value here
                // If your value is None, return self.deactivate() to deactivate the form    
            
               // If your value is not None, the bot ask for slot B normally like below
                logger.debug("Request next slot '{}'".format(slot))
                dispatcher.utter_template(
                    "utter_ask_{}".format(slot),
                    tracker,
                    // Add your value here so you don't need to set slot A
                    A=value,
                    silent_fail=False,
                    **tracker.slots
                )
                return [SlotSet(REQUESTED_SLOT, slot)]

        # no more required slots to fill
        return None

This is just from the top of my head, so maybe you have to twitch it a little bit, but i’ll think the idea is possible.

2 Likes

Thanks @fuih very much. I will try that.