Classifying entities with similar values

Hi im working in a project where i need to fill in a slot with height and weigh information (multi intent) thus i have an intent defined height_information+weight_information with a few training examples.

The issue is that since height and weight values are so similar eg, 58 kgs and 170 cm that even though the inetent is being classifed correctly both the values are classfied as a singles entity weight, any way to solve this issue as i want 3 digit answers to be classified as height ( in cm) and 2 digit answers to be classified as weight (in kg)

anyone with any ideas how to fix this would be really helpful

thank you

@Jivitesh, you can use Regex to validate kilos (2 digits) and weight (3 digits). Or, if you’re using form, you can even validate each slot on your actions.py file just by verifying if kilos has 2 digits and weight, 3 for example.

Hi, the issue is if i use regex then id have to stop the DIETclassifier from classfying intents right?

currently DIET is really working well for me.

I am using forms to fill the slots, but i dont have much experience with action.py if you could just show me how the code for the check for 2 and 3 digits will go i will be able to understand how to implement it much better

thank you for your response

@Jivitesh, you do not have to remove DIET, you can just use regex like I said or validate slot using your actions.py. That’d look like this:

On your actions.py:

class ValidateKiloWeightForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_kilo_weight_form"

    async def validate_kilos(self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any]
    ) -> Dict[Text, Any]:

        # validate if kilo has 2 digits
        if value.isdigit() and len(value) == 2:
            return {"kilos": value}
        else: 
            dispatcher.utter_message("Kilos must contain only 2 digits")
            return {"kilos": None}

    async def validate_weight(self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any]
    ) -> Dict[Text, Any]:

        # validate if weight has 3 digits
        if value.isdigit() and len(value) == 3:
            return {"weight": value}
        else: 
            dispatcher.utter_message("weight must contain only 3 digits")
            return {"weight": None}

Then, declare form’s name on your domain, section actions.

actions:
- validate_kilo_weight_form

Retrain and test your assistant. This way, when user says something different than 2 and 3 digits, your assistant will ask again until user says correctly.

ive learnt so much from you, I used regex as i already had a form running and adding regex was just easier, also because its still a little hard for me to grasp how the the syntax of a from allows you to fill a slot and tie it in with the bots replies, but i will learn and probably could create many interesting forms of my own with custom actions soon

thank you - I had another question with regards to connecting a food API Edamam to make / query api call and get recipes from their database to display for the user, I just cant find any documentation for linking API with your chat bot ,do you suggest I open another topic about this?

@Jivitesh, I like to help :smiley: ! Good to know that it helped you. Make an API is simple too, that’s an example:

I created a function to make the call for me:

def api_call(kilos, weight):
    request_url = "https://link.com/api/"

    info = {
        "kilos": kilos,
        "weight": weight
    }

    try:
        # make the API call
        response = requests.post(
            request_url, data=info
        )
        response.raise_for_status()
        

    except requests.exceptions.HTTPError as err:
        raise SystemExit(err)

    return response
    print(response.status_code)

Then, after I get all slot info from form, the assistant call the action submit to that function:

class ActionSubmitResume(Action):
    def name(self) -> Text:
        return "action_submit_form"

    def run (self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any], 
    ) -> List[Dict]:

        # get slot data
        kilos = tracker.get_slot("kilos")
        weight = tracker.get_slot("weight")

        # here you call that function with arguments...
        api_call(kilos, weight)

Just that @Jivitesh !

I recommend you to watch Rasa MasterClass Series on Youtube to learn more about how Rasa works etc. You will learn a lot there. I do not know which version you’re building your assistant, but they used Rasa 1.x version, but you can learn anyway. Hope it help you :smiley:

Ohh I will definitely try to give this approach a try and if i have any issues, ill comeback and ask some more questions :grin: , thank you

Hi marcos , got a question with the requests.post function while making my api call im trying to call EDAMAMs recipe search which takes in the query of the format

|“https://api.edamam.com/search?q=chicken&app_id=${YOUR_APP_ID}&app_key=${YOUR_APP_KEY}&from=0&to=3&calories=591-722&health=alcohol-free”|

im getting my values for health and various other parameters i have to pass in the url through A form but i dont know how i can pass these values to requests.post()

i have attached a screenshot below

where i want the my request to look like the variable query and slot values to fill in the blanks of the query (like how the allergies slot highlighted- replaces the text in the actual url)

thank you, this would be one of the final stages of getting my bot to work, ill definitely be mentioning all the help you provided in the project report- you have certainly helped me alot

@Jivitesh, but are trying to get some value from API to fill a certain slot, right? If so, you should not make a post request, but a get request. This way, you will make a call just to get some value from there. Pay attention to this :smiley:. Check out the W3 Schools HTTP Methods.

To pass arguments on your function, you should declare. Something like this:

let api_call(custom_type, meal_type, allergies, verornot):

Then, after all your forms slots are filled, you call that action_submit() function like we talked before, and call your api_call() passing those values…

action_submit():
  # you can do something before call api_call() like creating variables to get data from slots
  custom_type = tracker.get_slot("custom_type")
  meal_type = tracker.get_slot("meal_type")
  allergies = tracker.get_slot("allergies")
  verornot = tracker.get_slot("verornot")

  # call the function passing those values
  api_call(custom_type, meal_type, allergies, verornot)

Just an example to make clear what I meant!

Hi Marcos, sorry for being unclear but my slot values are already filled by the user by a form I provide
so for eg cuisinetype= Indian , allergies= soy-free now i need to pass these values in the url to post a request to the api so it can give me a recipe output with regard to the slot values which satisfy particular conditions

I cant figure out how to pass those slot values in the URL using the .post() method

I will go through the W3 resource you have provided and if i find something in the mean time I will inform you

Hopefully you understand that the slot values are already with me i just need to pass it into the URL to make the request

Thank you

@Jivitesh, so its the same idea. That should look something like this:

def api_call(custom_type, meal_type, allergies, verornot):
  request_url = "your_url.com"

  info = {
    "custom_type": custom_type,
    "meal_type": meal_type,
    "allergies": allergies,
    "verornot": verornot
  }

  try:
        # make the API call
        response = requests.post(
            request_url, data=info
        )
        response.raise_for_status()
        

    except requests.exceptions.HTTPError as err:
        raise SystemExit(err)

    return response
    print(response.status_code)

When you call your api_call() function:

action_submit():
  # you can do something before call api_call() like creating variables to get data from slots
  custom_type = tracker.get_slot("custom_type")
  meal_type = tracker.get_slot("meal_type")
  allergies = tracker.get_slot("allergies")
  verornot = tracker.get_slot("verornot")

  # call the function passing those values
  api_call(custom_type, meal_type, allergies, verornot)

I Will try by wednesday and get back to you as I have a test, as soon as thats over i can work in my Rasa bot again :))

thank you once again marcos really appreciate it

@Jivitesh, no worry! God bless you :smiley:. Good look on your test.

:)) im back !!

Hi marcos I had another question, I wanted to set a slot value just from user text and not from and entity or intent.

I am using a from to collect other slots which are required to be classified from an entity, but this particular slot needs to me collected as just free-text is that possible?

@Jivitesh, on your slot mapping:

forms:
  your_form_name:
    your_slot_name:
    - type: from_text

Check docs about Forms.

Thank you marcos , it works!

Hi marcos im still working on my bot and was trying to validate my slot using a validate form but I keep getting this error validate_cuisine_type() missing 1 required positional argument: ‘domain’

any ideas?

@Jivitesh, check if all slots are in your domain. And check if you only need to retrain in case you have done a change before.