Activate a form inside custom action and also trigger the form submit action

Hi, I have a rule where I trigger an action if the intent is identified. That action in turn uses “FollowUpAction” to trigger a form. Now I am unable to trigger the form submit action where I need to perform some operations.

I know I can directly activate the form and put it in an active loop in rules.yml but here is the thing: I need the form to run multiple times and each time user submits the form, the submit action should get triggered, where some operations are performed and the bot asks the user if he is satisfied. If the user is satisfied the story ends but if the user is not satisfied, then the bot activates the form again and asks for new input. This goes on till I either explicitly kill the loop via a condition or the user gets satisfied.

Can someone help me with:

  1. Triggering form submit action from within custom action in this case
  2. How should I write my rule.yml in this case?

Here is my code:

Top-level action which should be triggered in the rule:

class Action(Action):
    def name(self) -> Text:
        return "top_level_action"
    def run(
            self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict
    ) -> List[EventType]:

        #dispatcher.utter_message(text = "top level")

        platform_bargain_percentage = 10
        vendor_bargain_percentage = 10
        min_bargain = 2
        max_bargain = platform_bargain_percentage + vendor_bargain_percentage

        deal_status = tracker.get_slot('deal')

        for i in [*range(min_bargain,max_bargain)]:
            if deal_status != 'true':
                current_bargain = [*range(min_bargain,max_bargain)][i]
                SlotSet(key = "current_bargain", value = float(current_bargain))
                return [FollowupAction('user_offer_form')]

Here is the form action:

class UserOfferForm(Action):
    def name(self) -> Text:
        return "user_offer_form"

    def run(
            self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict
    ) -> List[EventType]:
        required_slots = ["quantity", "price", "deal"]

        for slot_name in required_slots:
            if tracker.slots.get(slot_name) is None:
                # The slot is not filled yet. Request the user to fill this slot next.
                return [SlotSet("requested_slot", slot_name)]

        # All slots are filled.
        return [SlotSet("requested_slot", None)]

Here is the form submit action:

class ActionGetOffer(Action):
    def name(self) -> Text:
        return "action_get_offer"

    def run(self,dispatcher,tracker: Tracker,domain: "DomainDict",) -> List[Dict[Text, Any]]:

        dispatcher.utter_message(text = "Ok! Let me see what I can do!")

        product_price = 500
        offer_price = int(tracker.get_slot("price"))
        quantity = int(tracker.get_slot("quantity"))

        user_asked_percentage = (offer_price / product_price) * 100
        current_bargain = tracker.get_slot('current_bargain')
        #deal = "false"

        if user_asked_percentage > current_bargain and deal != "true":       

            dispatcher.utter_message(text = "Okay so I just checked and sorry but we can't give away for that price!")

            dispatcher.utter_message(text = f"How about {str( product_price - (((current_bargain)/100) * product_price))}?")

            dispatcher.utter_message(response = 'utter_ask_deal')
           #Here the slot 'deal' gets set via a button message response
           #from the user. this slot will again be used in the top level
           #action to trigger the form again.

Any suggestion would be of great help as I have been stuck with this for many hours now. Thanks!!

I wouldn’t use FollowUp action for this. You can do this using featurized slots and rule conditions.

Hi, shouldn’t SlotSet be inside the return?

I just didn’t understand what the advantage of using the form within the custom action would be.