Hi, I am new to Rasa development and I haven’t quite understood the concept of custom Slot extraction and validation through custom actions since I’m having some issues, for example, I am working right now on a project where I will display a carousel of Smartphone brands and models for the user to select. I have created two slots brand and model with custom mapping, a form called purchase_form that gets activated when the intent of the user is to purchase a phone. For the display of the carousel I have implemented a custom action “action_ask_brand” and “action_ask_model” that dispatches to the webchat widget an attachment to display the brand and model carousel. Once the user clicks on a button to select a brand , the brand slot should be set using a custom action method “extract_brand” that takes the last user message and fills the slot and then should be validated in the “validate_brand”. I’m having an issue with this as the slots get filled, but then reset to None and I’m stuck in a loop.
rasa run actions --debug
- domain.yml :
forms:
purchase_form:
required_slots: - brand - model
slots:
brand:
type: text mappings: - type: custom
model:
type: text mappings: - type: custom
- actions.py
brands=[“SAMSUNG”,“APPLE”]
apple_models=[“iPhone 13”,“iPhone 13 Pro”,“iPhone SE”]
samsung_models=[“Galaxy Z”,“Galaxy S”,“Galaxy Note”]
class AskForSlotAction(Action):
def name(self) -> Text: return "action_ask_brand" def run(self,dispatcher: CollectingDispatcher, tracker: Tracker, domain: DomainDict ) -> List[EventType]: message = { "type": "template", "payload": { "template_type": "generic", "elements": [] } } for brand in brands: element={ "title": "Brand", "subtitle": brand, "image_url": f"./brands/{brand}.png", "buttons": [ { "title": brand, "payload": brand, "type": "postback" }, ] } message["payload"]["elements"].append(element) dispatcher.utter_message(text= "Choose a smartphone brand :" ) dispatcher.utter_message(attachment=message) return []
class AskForSlotAction(Action):
def name(self) -> Text: return "action_ask_model" def run(self,dispatcher: CollectingDispatcher, tracker: Tracker, domain: DomainDict ) -> List[EventType]: brand=tracker.get_slot("brand") if brand == "SAMSUNG": models=samsung_models elif brand== "APPLE": models=apple_models message = { "type": "template", "payload": { "template_type": "generic", "elements": [] } } for model in models: element={ "title": "Brand", "subtitle": brand, "image_url": f"./models/{model}.png", "buttons": [ { "title": model, "payload": brand, "type": "postback" }, ] } message["payload"]["elements"].append(element) dispatcher.utter_message(text= "Choose a model :" ) dispatcher.utter_message(attachment=message) return []
class ValidateSimulationForm(FormValidationAction):
def name(self) -> Text: return "validate_purchase_form" async def required_slots( self, domain_slots: List[Text], dispatcher: "CollectingDispatcher", tracker: "Tracker", domain: "DomainDict", ) -> List[Text]: slots = ["brand","model"] return slots async def extract_brand( self, dispatcher:CollectingDispatcher, tracker: Tracker, domain: DomainDict, ) -> Dict[Text, Any]: text_of_last_user_message = tracker.latest_message.get("text") brand=text_of_last_user_message if brand not in brands : return {"brand": None} return {"brand": brand} async def extract_model( self, dispatcher:CollectingDispatcher, tracker: Tracker, domain: DomainDict, ) -> Dict[Text, Any]: text_of_last_user_message = tracker.latest_message.get("text") model=text_of_last_user_message if model not in apple_models or samsung_models: return {"model": None} return {"model": model} def validate_brand( self, slot_value: Any, dispatcher: CollectingDispatcher, tracker: Tracker, domain: DomainDict, ) -> Dict[Text, Any]: if slot_value not in brands: dispatcher.utter_message(text=f"You have to choose a brand!") return {"brand" , None} dispatcher.utter_message(text=f"OK! You have chosen the brand: {slot_value}.") return {"brand": slot_value} def validate_model( self, slot_value: Any, dispatcher: CollectingDispatcher, tracker: Tracker, domain: DomainDict, ) -> Dict[Text, Any]: if slot_value not in apple_models or samsung_models : dispatcher.utter_message(text=f"You have to choose a model") return {"model" , None} dispatcher.utter_message(text=f"OK! You have chosen: {slot_value}.") return {"model": slot_value}