Problem with form with all slots as custom mappings

Let’s suppose that I have a form and every slot is filled by a custom action in my file actions.py. Also, from the docs we see that step nº2 states the following:

  1. Make sure that in the domain file you list for your form only those slots that use predefined mappings.

After seeing this my domain.yml has defined a form like this:

forms:                                                                                                                                            
  contract_form:                                                                                                                                  

And when I run rasa shell I get the following error:

2021-04-30 19:13:55 ERROR    rasa.core.processor  - Encountered an exception while running action 'contract_form'.Bot will continue, but the actions events are lost. Please check the logs of your action server for more information.
Traceback (most recent call last):
  File "/home/jsv/Documents/CARRERA/tfg4/resources/rasa-chatbot/.env/lib/python3.6/site-packages/rasa/core/processor.py", line 773, in _run_action
    output_channel, nlg, temporary_tracker, self.domain
  File "/home/jsv/Documents/CARRERA/tfg4/resources/rasa-chatbot/.env/lib/python3.6/site-packages/rasa/core/actions/loops.py", line 26, in run
    events += await self.activate(output_channel, nlg, tracker, domain)
  File "/home/jsv/Documents/CARRERA/tfg4/resources/rasa-chatbot/.env/lib/python3.6/site-packages/rasa/core/actions/forms.py", line 706, in activate
    for slot_name in self.required_slots(domain):
  File "/home/jsv/Documents/CARRERA/tfg4/resources/rasa-chatbot/.env/lib/python3.6/site-packages/rasa/core/actions/forms.py", line 58, in required_slots
    return list(domain.slot_mapping_for_form(self.name()).keys())
AttributeError: 'NoneType' object has no attribute 'key

I’ve tried to add a simple if statement in forms.py checking for a None in domain.slot_mapping_for_form(self.name()) and return an empty list if that is true, but It gives me an error in rasa_sdk as it isn’t prepared to handle empty lists, so I think that’s not the way to solve it.

Then I’ve tried to add to my domain.yml a dummy slot like this:

forms:                                                                                                                                            
  contract_form:
    dont_crash:
      - type: from_text

This kind solves the problem, it only complains saying that it hasn’t found any slot like that one, but continues with the execution.

Is there a better way to solve this problem? Do you have any thoughts on this? @fkoerner

Hi there, it sounds like you’ve defined an empty form, and this is a not very graceful crash resulting from the empty form. Is that true? If so, what are you trying to achieve with an empty form?

If not, can you share the full form?

The reasoning behind why it’s empty is because I don’t have any slot that uses predefined mappings. And from the docs we can read the following:

  1. Make sure that in the domain file you list for your form only those slots that use predefined mappings.

Oh I see, I’m not sure I’ve seen a form with only custom slot mappings before. We may need to add more graceful error handling.

Have you overridden the required_slots method (as in: part 3. of the docs section you linked)?

Yes, I have. Actually, it looks like this:

async def required_slots(
  self,
  slots_mapped_in_domain: List[Text],
  dispatcher: "CollectingDispatcher",
  tracker: "Tracker",
  domain: "DomainDict",
 ) -> Optional[List[Text]]:

    extra = [
      "vendor_name", 
      "vendor_address",
      ...]

    return extra + slots_mapped_in_domain

Okay, it seems this is a bug. I’ve created an issue here, please, feel free to add any information you think might be useful.

Until this is fixed: are you sure you need custom slot mappings for each of the slots? Maybe using a predefined slot mapping with a validation method would work. Otherwise, if you don’t want to use a predefined mapping + validation for any of your slots, your dummy slot is a decent workaround. To get rid of the error message, you just need to add the below to your domain.

slots:
  dont_crash:
    type: text
    influence_conversation: false
    auto_fill: false

Hi Jay… I’ve faced a similar issue… I wanted to see how you progressed after returning ‘required_slots’ from your required_slots method. Can we get in touch please? My email ID is: hadimaredia@gmail.com. Thanks :slight_smile:

I’ve checked the issue and it is perfectly described.

The reasons why I’m using custom slot mappings are the following:

  1. I have vendor_attr and buyer_attr (I want to extract the same entity from both). So I use a custom slot mapping to check that the required_slot is referring to the vendor or buyer and only set that particular slot.

  2. Also, I use it to select only those values that came from a specific extractor. To do it I iterate over each entity tracker.latest_message['entities'] and then I place restrictive conditionals inside the for each loop.

If you think there is a better way to do it please let me know. Also, if you need more information don’t hesitate to ask me and I’ll upload it.

Hm… so do you have an entity for each type like so:

entities:
 - vendor_attr
 - buyer_attr
slots:
 - vendor_attr
 - buyer_attr

or just one entity and a slot for each type, like so:

entities:
 - attr
slots:
 - vendor_attr
 - buyer_attr

I’m following the second approach. I’ve thought of it as less repetitive.

Also, I’ve forgotten to mention that I’m also using the custom slots to clean some of the inputs that are captured by the RegexEntityExtractor. Let’s suppose that I have a regex like this [A-Za-z]{1,3}[- ]*[0-9]{1,6}. As you can see I’m letting the user input - and spaces, but I don’t really care about them. So I just reformat the input in the extract_ function.

Hi @ergaster, sorry for the bit of a delay in my response – I was offline. I think the second approach makes sense. If you don’t want to use a dummy slot, here’s one way you could do it:

forms:
  my_form:
    required_slots:
        buyer_attr:
          - type: from_entity
            entity: attr
        vendor_attr:
          - type: from_entity
            entity: attr
def validate_vendor_attr(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
# clean input
# check correct extractor
# check required slot, if vendor_attr set vendor_attr slot else set no slot
def validate_buyer_attr(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
# clean input
# check correct extractor
# check required slot, if buyer_attr set buyer_attr slot else set no slot

Of course, it’d be best to put the cleaning code and the extractor code into a method that the two validation methods can use, to avoid code duplication.

Alternatively, you could also do something like:

forms:
  my_form:
    required_slots:
        attr:
           - type: from_entity
              entity: attr

And then you could write a form validation like so:

def validate_attr(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
# clean input
# check correct extractor
# check required slot, if vendor_attr set vendor_attr slot else set buyer_attr

Let me know if you have questions about these two approaches.

Hey there!

Was this bug fixed? I am trying to do something similar but still getting an error. \

Thanks!

Hi @abhi. The bug was fixed. Could you let me know what error you’re getting and what you’re trying to do?

Sorry for the late reply. I had been getting a similar error because i had all my slots set with custom mappings. I ended up moving away from this approach so I dont have the exact error messages and I can’t seem to find them. Sorry about that. If i come across it again i will reply back!

1 Like

No worries, thanks for letting me know!