Deactivate a form when a slot is set to a special value by rule

Hello, everyone. I want to deactivate a form when I detect a slot is set to a special value. This is my rule:

  - rule: 
    condition:
      - active_loop: form_xx
    steps:
      - slot_was_set:
        - slot_xx: true
      - action: action_deactivate_loop
      - active_loop: null

I want to finish form_xx when slot_xx is set to true. The rule is not work, I do not know why.

Can you try the following?

stories:
- story: stop when slot_xx is true
  steps:
  - intent: activate_form
  - action: form_xx
  - active_loop: form_xx
    - slot_was_set:
      - slot_xx: true
  - action: action_deactivate_loop
  - active_loop: null

It seems ok, but when I train, it conflict with rule of submitting the form as this doc:

Hmm…

What I’d do is use Dynamic Form Behavior for this.

from rasa_sdk.forms import FormValidationAction

class ValidateFormXX(FormValidationAction):
    def name(self):
        return "validate_form_xx"

    async def required_slots(self, domain_slots, dispatcher, tracker, domain):
        if tracker.slots.get("slot_xx") is True:
            return []
        return domain_slots

Thanks for you suggestion, this works. I just have another problem. I have a slot named agree_invite, when user agree, it will ok and go next slot, but when user deny, the robot should try to persuade twice and wait the user to agree, it the user deny for three times, the form will finish. So I define a custom extract method but I find it can not dispatch utter message, so how to solve this? The extract function is like this:

   async def extract_agree_invite(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict) -> Dict[
        Text, Any]:
        if tracker.sender_id not in self.deny_cnt_map:
            self.deny_cnt_map[tracker.sender_id] = 0
        intent = tracker.get_intent_of_latest_message()
        
        if intent == "agree_invite":
            self.deny_cnt_map[tracker.sender_id] = 0
            return {"agree_invite": True}
        elif intent == "deny_invite":
            self.deny_cnt_map[tracker.sender_id] = self.deny_cnt_map[tracker.sender_id] + 1
            if self.deny_cnt_map[tracker.sender_id] == 1:
                dispatcher.utter_message(template="utter_invite_deny1")
                return {}
            elif self.deny_cnt_map[tracker.sender_id] == 2:
                dispatcher.utter_message(template="utter_invite_deny2")
                return {}
            else:
                self.deny_cnt_map[tracker.sender_id] = 0
                return {"agree_invite": False}
        else:
            return {}

agree_invite is the slot, and when we catch agree intent, we will set it to true and go to next slot, when we catch deny intent, send two utter message to persuade users, when user deny three times, we set agree_invite to false and finish the form. I use rasa 2.8.15, because rasa 3.0 can not connect to socket io.

Your logic looks good.

Try to implement the utterances in a separate action action_ask_agree_invite though.


Extra suggestion: Instead of using a dictionary deny_cnt_map, why not just set a slot? :slight_smile:

I implemented something very similar to count how many times the user tried to enter his password, but found that it’s easier, cleaner, more independent, and maybe faster to use slots.

Thanks for your help, I have solve this problem.

1 Like