RasaHQ / rasa

💬 Open source machine learning framework to automate text- and voice-based conversations: NLU, dialogue management, connect to Slack, Facebook, and more - Create chatbots and voice assistants
https://rasa.com/docs/rasa/
Apache License 2.0
18.95k stars 4.64k forks source link

Exception while running chatbot 'Pizza_form' #3150

Closed tchax3742 closed 5 years ago

tchax3742 commented 5 years ago
**Rasa version**: 0.14.5 **Python version**: 3.6.4 **Operating system** (windows, osx, ...): Linux Ubuntu 18.04 **Issue**: I am running my chatbot in cmd I get the following: ![Screenshot from 2019-03-26 05-34-27](https://user-images.githubusercontent.com/46517278/54970015-39c77e80-4f89-11e9-897f-cfb754882c08.png) **Content of actions file (actions.py)**: ```py # -*- coding: utf-8 -*- from typing import Dict, Text, Any, List, Union from rasa_core_sdk import ActionExecutionRejection from rasa_core_sdk import Tracker from rasa_core_sdk.events import SlotSet from rasa_core_sdk.executor import CollectingDispatcher from rasa_core_sdk.forms import FormAction, REQUESTED_SLOT class PizzaForm(FormAction): #Example of a custom form action def name(self): # type: () -> Text #Unique identifier of the form return "pizza_form" @staticmethod def required_slots(tracker: Tracker) -> List[Text]: #A list of required slots that the form has to fill return ["size", "pizzatype", "ask_toppings", "toppings","num_pizza"] def slot_mappings(self): # type: () -> Dict[Text: Union[Dict, List[Dict]]] # A dictionary to map required slots to # - an extracted entity # - intent: value pairs # - a whole message # or a list of them, where a first match will be picked return {"size": self.from_entity(entity="size", not_intent="chitchat"), "pizzatype": self.from_entity(entity="pizzatype", not_intent="chitchat"), "ask_toppings": [self.from_entity(entity="toppings"), self.from_intent(intent='affirm', value=True), self.from_intent(intent='deny', value=False)], "toppings": self.from_intent(entity="toppings", not_intent="chitchat"), "num_pizza": [self.from_entity(entity="num_pizza", intent=["inform", "order_pizza"]), self.from_entity(entity="number")], "feedback": [self.from_entity(entity="feedback"), self.from_text()]} @staticmethod def size_db(): # type: () -> List[Text] #Database of supported sizes return ["large", "extra large", "medium", "small"] def toppings_db(): # type: () -> List[Text] #Database of supported toppings return ["onions", "mushrooms", "extra cheese", "sausage", "green peppers"] def pizzatype_db(): # type: () -> List[Text] # Database of supported pizzatypes return ["BBQ", "Buffalo chicken", "Hawaiian", "cheese"] @staticmethod def is_int(string: Text) -> bool: # Check if a string is an integer try: int(string) return True except ValueError: return False def validate(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any]) -> List[Dict]: # Validate extracted requested slot # else reject the execution of the form action # extract other slots that were not requested # but set by corresponding entity slot_values = self.extract_other_slots(dispatcher, tracker, domain) # extract requested slot slot_to_fill = tracker.get_slot(REQUESTED_SLOT) if slot_to_fill: slot_values.update(self.extract_requested_slot(dispatcher, tracker, domain)) if not slot_values: # reject form action execution # if some slot was requested but nothing was extracted # it will allow other policies to predict another action raise ActionExecutionRejection(self.name(), "Failed to validate slot {0} " "with action {1}" "".format(slot_to_fill, self.name())) # we'll check when validation failed in order # to add appropriate utterances for slot, value in slot_values.items(): if slot == 'size': if value.lower() not in self.size_db(): dispatcher.utter_template('utter_wrong_size', tracker) # validation failed, set slot to None slot_values[slot] = None elif slot == 'pizzatype': if value.lower() not in self.pizzatype_db(): dispatcher.utter_template('utter_wrong_pizzatype', tracker) # validation failed, set slot to None slot_values[slot] = None elif slot == 'ask_toppings': if isinstance(value, str): if 'yes' in value: # convert "out..." to True slot_values[slot] = True elif 'no' in value: # convert "in..." to False slot_values[slot] = False else: dispatcher.utter_template('utter_wrong_toppings1', tracker) # validation failed, set slot to None slot_values[slot] = None elif slot == 'toppings': if value.lower() not in self.toppings_db(): dispatcher.utter_template('utter_wrong_toppings2', tracker) # validation failed, set slot to None slot_values[slot] = None elif slot == 'num_pizza': if not self.is_int(value) or int(value) <= 0: dispatcher.utter_template('utter_wrong_num_pizza', tracker) # validation failed, set slot to None slot_values[slot] = None # validation succeed, set the slots values to the extracted values return [SlotSet(slot, value) for slot, value in slot_values.items()] def submit(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any]) -> List[Dict]: # Define what the form has to do # after all required slots are filled # utter submit template dispatcher.utter_template('utter_affirm', tracker) return [] ``` **Content of domain file (domain.yml)** (if used & relevant): ```yaml intents: # add your intents - greet - thanks - toppings_deny - stop - toppings_affirm - inform - what_is_pizza_cost - order_pizza - chitchat: use_entities: false entities: - size - pizzatype - toppings - num_pizza - number - feedback slots: size: type: text pizzatype: type: text ask_toppings: type: text toppings: type: text num_pizza: type: text feedback: type: text requested_slot: type: unfeaturized templates: utter_greet: - "Hey there, welcome to Tatenda's pizzabot, how can I help you" utter_thank: - "You're welcome!" utter_ask_size: - "We have small, medium, large and extra large pizzas. Which one do you want?" utter_ask_pizzatype: - "We have BBQ, Hawaiian, Bacon Cheeseburger, and Buffalo chicken pizzas, which one do you want" utter_ask_toppings: - "Do you want any toppings?" utter_ask_num_pizza: - "How many pizzas do you want" utter_toppings: - "We have mushrooms, onions, bacon, green peppers and sausage toppings, which would you like?" utter_fallback: - "sorry, I did not get you" utter_affirm: - "thanks for the details.your order is being processed" utter_default: - "sorry, i couldnt understand you.please retry" utter_ask_feedback: - text: "please give your feedback on your experience so far" utter_slots_values: - text: "You have ordered the following pizza: - size: {size} - pizzatype: {pizzatype} - num_pizza: {num_pizza} - toppings: {toppings} - feedback: {feedback}" utter_submit: - text: "submit" utter_chitchat: - text: "chitchat" utter_ask_continue: - text: "do you want to continue?" utter_wrong_size: - text: "We do not have that size, please try again" utter_wrong_pizzatype: - text: "We do not have that type of pizza, please choose from Hawaiian, buffalo chicken, Magherita and BBQ" utter_wrong_num_pizza: - text: "number of people should be a positive integer e.g 3, 4, please try again" utter_wrong_toppings1: - text: "Please answer with yes/no" utter_wrong_toppings2: - text: "Those toppings are currently unavailable, please choose from mushrooms, onions, bacon, green peppers and sausage toppings " actions: # all the utter actions from the templates, plus any custom actions - utter_slots_values - utter_ask_continue - utter_chitchat - utter_greet - utter_thank forms: - pizza_form ```
akelad commented 5 years ago

could you post the actual error it's throwing?

tchax3742 commented 5 years ago

it just displays "ERROR:rasa_core.processor:Encountered an exception while running action 'pizza_form'. Bot will continue, but the actions events are lost. Make sure to fix the exception in your custom code.", even when i run the bot using python nothing more. print("Your bot is ready to talk! Type your messages here or send 'stop'") while True: a = input() if a == 'stop': break responses = agent.handle_message(a) for response in responses: print(response["text"]) My custom action code(for FormAction) does not show any errors, when i greet the bot, it responds properly, but when i enter other intents that's when it posts the error. I got the code from your FormAction examples code on github.

WARNING:rasa_core.agent:Passing a text to agent.handle_message(...) is deprecated. Rather use agent.handle_text(...). ERROR:rasa_core.processor:Encountered an exception while running action 'pizza_form'. Bot will continue, but the actions events are lost. Make sure to fix the exception in your custom code. You have ordered the following pizza:

akelad commented 5 years ago

can you post the error from the action server? because this is just the error from rasa_core, which doesn't tell us much apart from the fact that the action execution failed

tchax3742 commented 5 years ago

Screenshot from 2019-03-29 09-01-49 i only see that when i run python -m rasa_core_sdk.endpoint --actions actions, no errors

akelad commented 5 years ago

that looks like it's not actually being executed then, what's the contents of your endpoints yaml?

tchax3742 commented 5 years ago

action_endpoint: url: http://localhost:5055/webhook

tchax3742 commented 5 years ago

it's properly indented though in my yaml file

tchax3742 commented 5 years ago

I have now included --endpoints endpoints.ym while running the model, like this, python -m rasa_core.run -d models/dialogue -u models/nlu/default/current --endpoints endpoints.yml, and it shows displays the error below, i think that's where the problem is Screenshot from 2019-03-29 15-40-42

tchax3742 commented 5 years ago

@akelad the action server is showing that the error is in actions.py slot mappings File "/home/tatenda/finalbot/finalbot0_env/actions.py", line 46, in slot_mappings not_intent="chitchat"), TypeError: from_intent() got an unexpected keyword argument 'entity' Screenshot from 2019-03-30 14-21-58 and this is the code


        # type: () -> Dict[Text: Union[Dict, List[Dict]]]
       # A dictionary to map required slots to
        #    - an extracted entity
        #    - intent: value pairs
          #  - a whole message
         #   or a list of them, where a first match will be picked

        return {"size": self.from_entity(entity="size",
                                          not_intent="chitchat"),
                "pizzatype": self.from_entity(entity="pizzatype",
                                               not_intent="chitchat"),
                "ask_toppings": [self.from_entity(entity="toppings"),
                                    self.from_intent(intent='affirm',
                                                     value=True),
                                    self.from_intent(intent='deny',
                                                     value=False)],
                "toppings": self.from_intent(entity="toppings",
                                              not_intent="chitchat"),
                "num_pizza": [self.from_entity(entity="num_pizza",
                                               intent=["inform",
                                                        "order_pizza"]),
                              self.from_entity(entity="number")],
                "feedback": [self.from_entity(entity="feedback"),
                             self.from_text()]}
tchax3742 commented 5 years ago

and i would like to know if this is even the right way to implement actions.py


# -*- coding: utf-8 -*-
from typing import Dict, Text, Any, List, Union

from rasa_core_sdk import ActionExecutionRejection
from rasa_core_sdk import Tracker
from rasa_core_sdk.events import SlotSet
from rasa_core_sdk.executor import CollectingDispatcher
from rasa_core_sdk.forms import FormAction, REQUESTED_SLOT

class PizzaForm(FormAction):
    #Example of a custom form action

    def name(self):
        # type: () -> Text
        #Unique identifier of the form

        return "pizza_form"

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

        return ["size", "pizzatype", "ask_toppings", "toppings","num_pizza"]

    def slot_mappings(self):
        # type: () -> Dict[Text: Union[Dict, List[Dict]]]
       # A dictionary to map required slots to
        #    - an extracted entity
        #    - intent: value pairs
          #  - a whole message
         #   or a list of them, where a first match will be picked

        return {"size": self.from_entity(entity="size",
                                          not_intent="chitchat"),
                "pizzatype": self.from_entity(entity="pizzatype",
                                               not_intent="chitchat"),
                "ask_toppings": [self.from_entity(entity="toppings"),
                                    self.from_intent(intent='affirm',
                                                     value=True),
                                    self.from_intent(intent='deny',
                                                     value=False)],
                "toppings": self.from_intent(entity="toppings",
                                              not_intent="chitchat"),
                "num_pizza": [self.from_entity(entity="num_pizza",
                                               intent=["inform",
                                                        "order_pizza"]),
                              self.from_entity(entity="number")],
                "feedback": [self.from_entity(entity="feedback"),
                             self.from_text()]}

    @staticmethod
    def size_db():
        # type: () -> List[Text]
        #Database of supported sizes
        return ["large",
                "extra large",
                "medium",
                "small"]

    def toppings_db():
        # type: () -> List[Text]
        #Database of supported toppings
        return ["onions",
                "mushrooms",
                "extra cheese",
                "sausage",
                "green peppers"]

    def pizzatype_db():
        # type: () -> List[Text]
        # Database of supported pizzatypes
        return ["BBQ",
                "Buffalo chicken",
                "Hawaiian",
                "cheese"]

    @staticmethod
    def is_int(string: Text) -> bool:
        # Check if a string is an integer
        try:
            int(string)
            return True
        except ValueError:
            return False

    def validate(self,
                 dispatcher: CollectingDispatcher,
                 tracker: Tracker,
                 domain: Dict[Text, Any]) -> List[Dict]:
        # Validate extracted requested slot
         #   else reject the execution of the form action

        # extract other slots that were not requested
        # but set by corresponding entity
        slot_values = self.extract_other_slots(dispatcher, tracker, domain)

        # extract requested slot
        slot_to_fill = tracker.get_slot(REQUESTED_SLOT)
        if slot_to_fill:
            slot_values.update(self.extract_requested_slot(dispatcher,
                                                           tracker, domain))
            if not slot_values:
                # reject form action execution
                # if some slot was requested but nothing was extracted
                # it will allow other policies to predict another action
                raise ActionExecutionRejection(self.name(),
                                               "Failed to validate slot {0} "
                                               "with action {1}"
                                               "".format(slot_to_fill,
                                                         self.name()))

        # we'll check when validation failed in order
        # to add appropriate utterances
        for slot, value in slot_values.items():
            if slot == 'size':
                if value.lower() not in self.size_db():
                    dispatcher.utter_template('utter_wrong_size', tracker)
                    # validation failed, set slot to None
                    slot_values[slot] = None

            elif slot == 'pizzatype':
                if value.lower() not in self.pizzatype_db():
                    dispatcher.utter_template('utter_wrong_pizzatype', tracker)
                    # validation failed, set slot to None
                    slot_values[slot] = None

            elif slot == 'ask_toppings':
                if isinstance(value, str):
                    if 'yes' in value:
                        # convert "out..." to True
                        slot_values[slot] = True
                    elif 'no' in value:
                        # convert "in..." to False
                        slot_values[slot] = False
                    else:
                        dispatcher.utter_template('utter_wrong_toppings1',
                                                  tracker)
                        # validation failed, set slot to None
                        slot_values[slot] = None

            elif slot == 'toppings':
                if value.lower() not in self.toppings_db():
                    dispatcher.utter_template('utter_wrong_toppings2', tracker)
                    # validation failed, set slot to None
                    slot_values[slot] = None

            elif slot == 'num_pizza':
                if not self.is_int(value) or int(value) <= 0:
                    dispatcher.utter_template('utter_wrong_num_pizza',
                                              tracker)
                    # validation failed, set slot to None
                    slot_values[slot] = None

        # validation succeed, set the slots values to the extracted values
        return [SlotSet(slot, value) for slot, value in slot_values.items()]

    def submit(self,
               dispatcher: CollectingDispatcher,
               tracker: Tracker,
               domain: Dict[Text, Any]) -> List[Dict]:
        # Define what the form has to do
           # after all required slots are filled

        # utter submit template
        dispatcher.utter_template('utter_affirm', tracker)
        return []
akelad commented 5 years ago

@tchax3742 you already asked this question on a different issue so i'm going to close this. Please avoid creating duplicate issues in future