Slot values being overwritten in Rasa

I have two slots that are age and salary. The bot is getting confused and is overwriting these values

age:
      - entity: age_english
        type: from_entity
        not_intent: salary_english


 salary:
      - entity: salary
        type: from_entity
        not_intent: age_english

I have even tried not intents. Any help regarding this would be greatly appreciated

1 Like

@kearnsw any idea on how this might work?

age:
    type: text
    influence_conversation: True
    auto_fill: False

I have set the auto_fill to False and even then the slots are being overwritten

Hello @afnaan

What do you mean by “overwritten”? If entity extraction yields wrong results, you might need to provide more (or more realistic) training data.

hey @j.mosig what I mean when I am saying that the slots are being overwritten is that I have two numeric values in my form that is age and the salary. For both of these I have to just write their numerical values instead of giving values to bot like Age 30 salary 50000 So the bot confuses these values and overwrites the value of age in salary even though it has been filled before

You can see in these images that age is already stored in a slot but when I enter the salary it gets overwritten in the age slot @j.mosig

Ok, so because both age and salary are integers, both slots get mapped from the same entity. You can use a form validation action to fix this. In your validation action, you can check that age is some number below 200 and salary something above 10000. You can also check what the requested slot is and make sure age only gets filled when age is requested. Does this help?

2 Likes

@j.mosig I tried what you told me but the thing is I do not know we will write the story. I have edited the function for form validation action

class ValidateAgeForm(FormValidationAction):
    
    def name(self) -> Text:
        return "user_age_form"

    @staticmethod
    def age() -> List[Text]:
       
        age = [age for age in range(19, 101)]
        print(age)
        return age

    def validate_cuisine(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate age value."""
        print('Age validated')
        if slot_value in self.age():
            
            return {"age": slot_value}
        else:
            # validation failed, set this slot to None so that the
            # user will be asked for the slot again
            return {"age": None}        

I think this function should verify that the age is between 18-100 but I don’t know how to write its story.

Also before the slot is validated it needs to has some input to be validated I guess? but as soon as I enter the salary the age slot gets overwritten.

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

should be

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

if your form is called user_age_form.

@j.mosig so I have tried it and this works but the issue that I am now having is that when I enter salary even after validation it tries to overwrite the age but is unable to do so because of the validation check. so to validate other slots do we have to make a separate class or if we do it in the same class? what will be the process.

I have even tried slot mappings and still it is not working. Thank you so much for the help

class ValidateAgeForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_user_details_form"
    
    def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
        return {
            "age": self.from_entity(entity="age", intent="age_english"),
            "salary": self.from_entity(entity="salary", intent="salary_english")
        }


    def validate_age(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict
    ) -> Dict[Text, Any]:
        if int(slot_value) >= 18 and int(slot_value) <= 100:
            print(slot_value)
            return {"age": slot_value}
        else:
            dispatcher.utter_message('Age must be between 18 and 100')
            return {"age": None}    

    def validate_salary(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict
    ) -> Dict[Text, Any]:
        print('Salary:', slot_value)
        if len(slot_value) >= 4 and len(slot_value) <= 7:
            print(slot_value)
            return {"salary": slot_value}
        else:
            dispatcher.utter_message('Salary must be between 1000 and 1000000')
            return {"salary": None}

@ChrisRahme do you have any idea how we might be able to do this?

Your validation seems fine.

What do you mean by that? What is happening?

No, you write it in a same class same it is for a same action.

2 Likes

Maybe try mapping from text:

class ValidateAgeForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_user_details_form"
    
    def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
        return {
            "age": self.from_text(),
            "salary": self.from_text()
        }

    def validate_age(self, slot_value, dispatcher, tracker, domain) -> Dict[Text, Any]:
        try:
            slot_value = float(slot_value)
        except:
            dispatcher.utter_message('Age must be a number')
            return {"age": None} 

        if 18 <= slot_value <= 100:
            print(slot_value)
            return {"age": slot_value}
        else:
            dispatcher.utter_message('Age must be between 18 and 100')
            return {"age": None}    

    def validate_salary(self, slot_value, dispatcher, tracker, domain) -> Dict[Text, Any]:
        try:
            slot_value = float(slot_value)
        except:
            dispatcher.utter_message('Salary must be a number')
            return {"salary": None} 

        if 1000 <= slot_value <= 1000000:
            print(slot_value)
            return {"salary": slot_value}
        else:
            dispatcher.utter_message('Salary must be between 1000 and 1000000')
            return {"salary": None}

No this does not resolves the issue. I mean by that is slot validation is working fine but when I write the salary it still overwrites the age slot. Now in the age slot there is a validation check and due to that it throws the exception that age must be between 18 & 100. The issue is still there that the bot is getting confused between age and salary as both the values are integers i.e age:18 salary:18000

To validate slot mappings of this kind, it’s probably best to just overwrite the run method:

class ValidateUserDetailsForm(Action):
    def name(self) -> Text:
        return "validate_user_details_form"

    def run(
        self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict
    ) -> List[EventType]:
        print(f"slots_to_validate: {tracker.slots_to_validate()}")
        # ...
1 Like

@j.mosig Can you please elaborate on how to overwrite the run method, I have written the following code but it is still overwriting the slots.

    def name(self) -> Text:
        return "validate_balance_inquiry_form"
    def run(
          self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: DomainDict
      ) -> List[EventType]:

          slot_picked = tracker.slots_to_validate()
          validation_events = []
          try:
            slot_name = (list(slot_picked.keys())[0])
            slot_value = (list(slot_picked.values())[0])
            print(slot_name, slot_value, )
            if 6 < len(slot_value) < 15:
                validation_events.append(SlotSet("acc_num", slot_value))
            elif len(slot_value) <= 6:
                validation_events.append(SlotSet("otp", slot_value))
            else:
                validation_events.append(SlotSet(slot_name, slot_value))
          except Exception as e:
            print(e)
          

PLEASE IGNORE THE LIST OUT OF INDEX ERROR. Thanks for your help. Regards

@ChrisRahme Can you help me figuring out how to do it?

@j.mosig can you please help me?

@nik202 can you help me on this?

@Juste I am having trouble with this. I am thinking of a new approach where I make an intent inform and get my otp and account no in the same intent. Will that somehow work?

@afnaan the run method has to return a list of events. Your function doesn’t return anything. You should have something like return [SlotSet("acc_num", your_value)] somewhere.