If a slot is not requested, slot mapping doesn't work

Hi !

During some tests with Role Entities, I figure out my slot mapping doesn’t work like I tought. Currently, it work because my slots names and my entities names are the same.

But I tried to change one and it is not fill now.

My test is with the same intent, it trigger the FormAction action_get_top_users which the beginning is :

class ActionGetTopUsers(FormAction):

    def name(self) -> Text:
        return "action_get_top_users"

    @staticmethod
    def required_slots(tracker: Tracker) -> List[Text]:
        """A list of required slots that the form has to fill"""
        return ["moment", "application"]

    def slot_mappings(self) -> Dict[Text, Any]:
        return {"moment": self.from_entity(entity="moment",
                                                  intent=["moment_added",
                                                          "top_users"]),
                "application": self.from_entity(entity="application",
                                             intent=["application_added", 
                                                     "top_users"]),
                "site": self.from_entity(entity="city-site",
                                             intent=["city_added", "top_users"])}

If i had site to my requested slot (just by adding it in the list of required_slot), the debug is

2020-05-25 15:07:45 **DEBUG** rasa_sdk.forms - Activated the form 'action_get_top_users'
2020-05-25 15:07:45 **DEBUG** rasa_sdk.forms - Validating pre-filled required slots: {'moment': 'hier', 'application': 'skype'}
2020-05-25 15:07:45 **DEBUG** rasa_sdk.forms - Validating user input '{'intent': {'name': 'top_users', 'confidence': 0.9999955296516411}, 
'entities': [{'entity': 'application', 'start': 14, 'end': 19, 'value': 'skype', 'extractor': 'DIETClassifier'}, 
{'entity': 'moment', 'start': 20, 'end': 24, 'value': 'hier', 'extractor': 'DIETClassifier'}, 
{'entity': 'city-site', 'start': 27, 'end': 32, 'value': 'Porto', 'extractor': 'DIETClassifier'}], 
'intent_ranking': [{'name': 'top_users', 'confidence': 0.9999955296516411}, 
{'name': 'mood_unhappy_return_question', 'confidence': 2.146076894860016e-06}, 
{'name': 'goodbye', 'confidence': 5.99835573211749e-07}, 
{'name': 'temperature', 'confidence': 5.354330596674117e-07}, 
{'name': 'mood_great_return_question', 'confidence': 3.627311855325388e-07}, 
{'name': 'bot_challenge', 'confidence': 2.8670544338638143e-07}, 
{'name': 'greet', 'confidence': 1.801675466595043e-07}, 
{'name': 'deny', 'confidence': 1.6207697228765017e-07}, 
{'name': 'reseau_action', 'confidence': 1.442294745856998e-07}, 
{'name': 'weather', 'confidence': 8.303375409468572e-08}], 'text': 'qui a utilisé skype hier à Porto ?'}'
2020-05-25 15:07:45 **DEBUG** rasa_sdk.forms - Extracted 'hier' for extra slot 'moment'.
2020-05-25 15:07:45 **DEBUG** rasa_sdk.forms - Extracted 'skype' for extra slot 'application'.
2020-05-25 15:07:45 **DEBUG** rasa_sdk.forms - Extracted 'Porto' for extra slot 'site'.
2020-05-25 15:07:45 **DEBUG** rasa_sdk.forms - Validating extracted slots: {'moment': 'hier', 'application': 'skype', 'site': 'Porto'}
2020-05-25 15:07:45 **DEBUG** rasa_sdk.forms - No slots left to request, all required slots are filled:
moment: hier
application: skype
site: Porto
2020-05-25 15:07:45 **DEBUG** rasa_sdk.forms - Submitting the form 'action_get_top_users'

So we can see I have all my slot filled.

But if site is not required :

2020-05-25 15:12:41 **DEBUG** rasa_sdk.forms - Activated the form 'action_get_top_users'
2020-05-25 15:12:41 **DEBUG** rasa_sdk.forms - Validating pre-filled required slots: {'moment': 'hier', 'application': 'skype'}
2020-05-25 15:12:41 **DEBUG** rasa_sdk.forms - Validating user input '{'intent': {'name': 'top_users', 'confidence': 0.9999955296516411}, 
'entities': [{'entity': 'application', 'start': 14, 'end': 19, 'value': 'skype', 'extractor': 'DIETClassifier'}, 
{'entity': 'moment', 'start': 20, 'end': 24, 'value': 'hier', 'extractor': 'DIETClassifier'}, 
{'entity': 'city-site', 'start': 27, 'end': 32, 'value': 'Porto', 'extractor': 'DIETClassifier'}], 
'intent_ranking': [{'name': 'top_users', 'confidence': 0.9999955296516411}, 
{'name': 'mood_unhappy_return_question', 'confidence': 2.146076894860016e-06}, 
{'name': 'goodbye', 'confidence': 5.99835573211749e-07}, 
{'name': 'temperature', 'confidence': 5.354330596674117e-07}, 
{'name': 'mood_great_return_question', 'confidence': 3.627311855325388e-07}, 
{'name': 'bot_challenge', 'confidence': 2.8670544338638143e-07}, 
{'name': 'greet', 'confidence': 1.801675466595043e-07}, 
{'name': 'deny', 'confidence': 1.6207697228765017e-07}, 
{'name': 'reseau_action', 'confidence': 1.442294745856998e-07}, 
{'name': 'weather', 'confidence': 8.303375409468572e-08}], 'text': 'qui a utilisé skype hier à Porto ?'}'
2020-05-25 15:12:41 **DEBUG** rasa_sdk.forms - Extracted 'hier' for extra slot 'moment'.
2020-05-25 15:12:41 **DEBUG** rasa_sdk.forms - Extracted 'skype' for extra slot 'application'.
2020-05-25 15:12:41 **DEBUG** rasa_sdk.forms - Validating extracted slots: {'moment': 'hier', 'application': 'skype'}
2020-05-25 15:12:41 **DEBUG** rasa_sdk.forms - No slots left to request, all required slots are filled:
moment: hier
application: skype
2020-05-25 15:12:41 **DEBUG** rasa_sdk.forms - Submitting the form 'action_get_top_users'

The Validating Input User part is exactly the same in both cases, but it doesn’t extract the same information and I don’t understand why ?

I thought that slot_mapping in a FormAction work even if the slot is not requested.

Can you help me please ?

Thank you

The custom slot mappings only work for requested slots, see the example in de docs. If you want to see how this is implemented you can also check the get_mappings_for_slot function.

Okay thank you, but in fact I don’t see in the documentation that Slot Mapping only works for requested Slot, I think it should be interesting to precise it. So now, I only need to find how to set not requested slot !

I agree that it is somewhat unclear but the first sentence of the comment in the code example states: “A dictionary to map required slots to…”

ow I’m sorry, this is true ^^" Read better would avoid me some trouble

To set a slot that has not been requested you could try to hook into the request_next_slot:

def request_next_slot(self, dispatcher, tracker, domain):
        next_slot = super().request_next_slot(dispatcher, tracker, domain)

        # All required slots have been filled
        if not next_slot:
            # Check whether the 'site' slot has been filled
            if not tracker.get_slot('site'):
                # Set the slot
                return [SlotSet('site', 'foobar')]

        return next_slot

My use of the function is a little different so I’m not 100% sure if the code above will work directly for your usecase.

Okay thank you, I’ll watch to this, thank you for your time !

@flythe globally, your code is good, i can have optional slot, but when I have one, I need to do an other input to launch my action.

Thank you, I keep looking for a solution.