Dynamic form

Hi there.

I’ve got a form which has 7 slots to be filled in maximum. That is, if 1st slot is false, it adds 2nd slot, then if 2nd slot is false is adds the 3rd slot and so on.

The problem is that, after 1st slot is filled, it asks well for the 2nd, but after yes/no answer, I get an error “action_execution_rejected”

This is my action file:

# -*- coding: utf-8 -*-
from rasa_sdk.types import DomainDict
from typing import Dict, Text, Any, List, Union, Optional
from rasa_sdk import Tracker, Action
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.forms import FormValidationAction
from typing import Any, Text, Dict, List
from rasa_sdk.events import EventType, Restarted, ConversationPaused, ConversationResumed, ReminderScheduled, ReminderCancelled, SlotSet
import logging
from datetime import datetime
import datetime as dt
import pytz


logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
enable_no_user_input = True

#-------------------------------Validate IRS dispensa form ----------------------------------   


class ValidateIRSDispensaForm(FormValidationAction):
    """Example of a form validation action."""

    def name(self) -> Text:
        print("Form - dispensa IRS")
        return "validate_IRS_dispensa_form"


    async def required_slots(
        self,
        slots_mapped_in_domain: List[Text],
        dispatcher: "CollectingDispatcher",
        tracker: "Tracker",
        domain: "DomainDict",
    ) -> Optional[List[Text]]:
    
        _dispensa_casado_trib_conjunta = tracker.slots.get("dispensa_casado_trib_conjunta")
        if _dispensa_casado_trib_conjunta is not None:
            if (_dispensa_casado_trib_conjunta == False):
                return ["dispensa_rendas_vitalícias"] + slots_mapped_in_domain
        return slots_mapped_in_domain

        _dispensa_rendas_vitalícias = tracker.slots.get("dispensa_rendas_vitalícias")
        if _dispensa_rendas_vitalícias is not None:
            if (_dispensa_rendas_vitalícias == False):
                return ["dispensa_rend_espécie"] + slots_mapped_in_domain
        return slots_mapped_in_domain

        _dispensa_rend_espécie = tracker.slots.get("dispensa_rend_espécie")
        if _dispensa_rend_espécie is not None:
            if (_dispensa_rend_espécie == False):
                return ["dispensa_pensões_alim"] + slots_mapped_in_domain
        return slots_mapped_in_domain

        _dispensa_pensões_alim = tracker.slots.get("dispensa_pensões_alim")
        if _dispensa_pensões_alim is not None:
            if (_dispensa_pensões_alim == False):
                return ["dispensa_rend_pensões"] + slots_mapped_in_domain
        return slots_mapped_in_domain

        _dispensa_rend_pensões = tracker.slots.get("dispensa_rend_pensões")
        if _dispensa_rend_pensões is not None:
            if (_dispensa_rend_pensões == False):
                return ["dispensa_tx_liberatórias_englobar"] + slots_mapped_in_domain
            else:
                return ["dispensa_rend_pensões_sup"] + slots_mapped_in_domain
        return slots_mapped_in_domain

        _dispensa_rend_pensões_sup = tracker.slots.get("dispensa_rend_pensões_sup")
        if _dispensa_rend_pensões_sup is not None:
            if (_dispensa_rend_pensões_sup == False):
                return ["dispensa_tx_liberatórias_englobar"] + slots_mapped_in_domain
        return slots_mapped_in_domain

        _dispensa_tx_liberatórias_englobar = tracker.slots.get("dispensa_tx_liberatórias_englobar")
        if _dispensa_tx_liberatórias_englobar is not None:
            if (_dispensa_tx_liberatórias_englobar == False):
                return ["dispensa_subsídios_PAC"] + slots_mapped_in_domain
        return slots_mapped_in_domain

        _dispensa_subsídios_PAC = tracker.slots.get("dispensa_subsídios_PAC")
        if _dispensa_subsídios_PAC is not None:
            if (_dispensa_subsídios_PAC == False):
                return ["dispensa_atos_isolados"] + slots_mapped_in_domain
        return slots_mapped_in_domain

#----------------------validação de slots
    def validate_dispensa_casado_trib_conjunta(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `dispensa_casado_trib_conjunta` value."""

        afirma_nega = str(tracker.latest_message["intent"].get("name"))

        if (afirma_nega == "afirmar"):
            return [SlotSet("dispensa_casado_trib_conjunta",True)]        
        elif (afirma_nega == "negar"):    
            return [SlotSet("dispensa_casado_trib_conjunta", False)]        
        else:
            return [SlotSet("dispensa_casado_trib_conjunta", None)]

    def validate_dispensa_rendas_vitalícias(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `dispensa_rendas_vitalícias` value."""

        afirma_nega = str(tracker.latest_message["intent"].get("name"))

        if (afirma_nega == "afirmar"):
            return [SlotSet("dispensa_rendas_vitalícias",True)]        


        elif (afirma_nega == "negar"):    
            return [SlotSet("dispensa_rendas_vitalícias", False)]
        
        else:
            return [SlotSet("dispensa_rendas_vitalícias", None)]

    def validate_dispensa_rend_espécie(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `dispensa_rend_espécie` value."""

        afirma_nega = str(tracker.latest_message["intent"].get("name"))

        if (afirma_nega == "afirmar"):
            return [SlotSet("dispensa_rend_espécie",True)]        


        elif (afirma_nega == "negar"):    
            return [SlotSet("dispensa_rend_espécie", False)]
        
        else:
            return [SlotSet("dispensa_rend_espécie", None)]

    def validate_dispensa_pensões_alim(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `dispensa_pensões_alim` value."""

        afirma_nega = str(tracker.latest_message["intent"].get("name"))

        if (afirma_nega == "afirmar"):
            return [SlotSet("dispensa_pensões_alim",True)]        


        elif (afirma_nega == "negar"):    
            return [SlotSet("dispensa_pensões_alim", False)]
        
        else:
            return [SlotSet("dispensa_pensões_alim", None)]

    def validate_dispensa_rend_pensões(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `dispensa_rend_pensões` value."""

        afirma_nega = str(tracker.latest_message["intent"].get("name"))

        if (afirma_nega == "afirmar"):
            return [SlotSet("dispensa_rend_pensões",True)]        


        elif (afirma_nega == "negar"):    
            return [SlotSet("dispensa_rend_pensões", False)]
        
        else:
            return [SlotSet("dispensa_rend_pensões", None)]


    def validate_dispensa_rend_pensões(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `dispensa_rend_pensões_sup` value."""

        afirma_nega = str(tracker.latest_message["intent"].get("name"))

        if (afirma_nega == "afirmar"):
            return [SlotSet("dispensa_rend_pensões_sup",True)]        


        elif (afirma_nega == "negar"):    
            return [SlotSet("dispensa_rend_pensões_sup", False)]
        
        else:
            return [SlotSet("dispensa_rend_pensões_sup", None)]


    def validate_dispensa_tx_liberatórias_englobar(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `dispensa_tx_liberatórias_englobar` value."""

        afirma_nega = str(tracker.latest_message["intent"].get("name"))

        if (afirma_nega == "afirmar"):
            return [SlotSet("dispensa_tx_liberatórias_englobar",True)]        


        elif (afirma_nega == "negar"):    
            return [SlotSet("dispensa_tx_liberatórias_englobar", False)]
        
        else:
            return [SlotSet("dispensa_tx_liberatórias_englobar", None)]

    def validate_dispensa_subsídios_PAC(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `dispensa_subsídios_PAC` value."""

        afirma_nega = str(tracker.latest_message["intent"].get("name"))

        if (afirma_nega == "afirmar"):
            return [SlotSet("dispensa_subsídios_PAC",True)]        


        elif (afirma_nega == "negar"):    
            return [SlotSet("dispensa_subsídios_PAC", False)]
        
        else:
            return [SlotSet("dispensa_subsídios_PAC", None)]

    def validate_dispensa_atos_isolados(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate `dispensa_atos_isolados` value."""

        afirma_nega = str(tracker.latest_message["intent"].get("name"))

        if (afirma_nega == "afirmar"):
            return [SlotSet("dispensa_atos_isolados",True)]        


        elif (afirma_nega == "negar"):    
            return [SlotSet("dispensa_atos_isolados", False)]
        
        else:
            return [SlotSet("dispensa_atos_isolados", None)]

These are my slots:

  dispensa_casado_trib_conjunta:
    type: rasa.shared.core.slots.BooleanSlot
    initial_value: null
    auto_fill: true
    influence_conversation: true

  dispensa_rendas_vitalícias:
    type: rasa.shared.core.slots.BooleanSlot
    initial_value: null
    auto_fill: true
    influence_conversation: true

  dispensa_rend_espécie:
    type: rasa.shared.core.slots.BooleanSlot
    initial_value: null
    auto_fill: true
    influence_conversation: true

  dispensa_pensões_alim:
    type: rasa.shared.core.slots.BooleanSlot
    initial_value: null
    auto_fill: true
    influence_conversation: true

  dispensa_rend_pensões:
    type: rasa.shared.core.slots.BooleanSlot
    initial_value: null
    auto_fill: true
    influence_conversation: true

  dispensa_rend_pensões_sup:
    type: rasa.shared.core.slots.BooleanSlot
    initial_value: null
    auto_fill: true
    influence_conversation: true

  dispensa_tx_liberatórias_englobar:
    type: rasa.shared.core.slots.BooleanSlot
    initial_value: null
    auto_fill: true
    influence_conversation: true

  dispensa_subsídios_PAC:
    type: rasa.shared.core.slots.BooleanSlot
    initial_value: null
    auto_fill: true
    influence_conversation: true

  dispensa_atos_isolados:
    type: rasa.shared.core.slots.BooleanSlot
    initial_value: null
    auto_fill: true
    influence_conversation: true

My form:

  IRS_dispensa_form:
    required_slots:
      dispensa_casado_trib_conjunta:
      - intent: afirmar
        type: from_intent
        value: true
      - intent: negar
        type: from_intent
        value: false

I’m using rasa version 2.xxx

Any idea why it doesn’t add the following slots to the form (if the previous slot requires it)?

Thanks a lot!

@nik202

do you have any idea how to solve this?

Is this a good approach to create a “dialog” based on yes/no answers?

Thanx!

@nonola give me some time to see the code Pedro, just endup with calls :exploding_head: I will get back to you soon.

1 Like

I didn’t test the code but… you have two function with same name def validate_dispensa_rend_pensões (line 170 and 192)

i think you miss the “_sup” hahaha :stuck_out_tongue:

another thing

       _dispensa_casado_trib_conjunta = tracker.slots.get("dispensa_casado_trib_conjunta")
        **if _dispensa_casado_trib_conjunta is not None:**
            if (_dispensa_casado_trib_conjunta == False):
                return ["dispensa_rendas_vitalícias"] + slots_mapped_in_domain
        **return slots_mapped_in_domain**

The code always return in this part or i’m confusing? :joy: :joy:

@itsjhonny ,

The problem happens after I answer to the second slot question.

After it well recognizes “negar” intent, then I’ve got the error in the red line…

Why?

humm, everything is right. I dont see any error

Maybe some - active_loop: erro or missing slot because the special character in “vitalícias”?

" While a form is active, if a user’s input does not fill the requested slot, the execution of the form action will be rejected i.e. the form will automatically raise an ActionExecutionRejection . These are the specific scenarios in which a form will raise an ActionExecutionRejection "

reference: Forms

Did you get this erro using rasa shell too?

@itsjhonny

Hi João!

I think I know whay is the problem:

2022-04-12 07:52:01 DEBUG    rasa.core.actions.forms  - Got mapping '{'type': 'from_entity', 'entity': 'dispensa_rendas_vitalícias', 'intent': [], 'not_intent': [], 'role': None, 'group': None}'

It should be validated with intent, not entity…

I wonder if this is the best approach to implement a form like this:

The green arrows stop the form; the red arrows are the slots to be filled (not all of them have the red arrow, but I think you understand)

@nonola Did you find the solution? If not, can you share your project folder? I can make some test later on my local :blush:

I did find a solution.

I defined all the slots in domain, forms, then those who weren’t needed I skip them.

Thanks.

@nonola easy peasy, and I saved :stuck_out_tongue: will talk on easter break Pedro :slight_smile:

1 Like