# Custom Slot extraction and validation fails using custom actions

**URL:** <https://forum.rasa.com/t/custom-slot-extraction-and-validation-fails-using-custom-actions/52858>\
**Category:** Rasa Open Source\
**Created:** [April 26, 2022, 11:03pm UTC](https://forum.rasa.com/t/custom-slot-extraction-and-validation-fails-using-custom-actions/52858 "2022-04-26T23:03:10Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![kassem404](https://avatars.discourse-cdn.com/v4/letter/k/71c47a/32.png) [@kassem404](https://forum.rasa.com/u/kassem404)\
**Post date:** [April 26, 2022, 11:03pm UTC](https://forum.rasa.com/t/custom-slot-extraction-and-validation-fails-using-custom-actions/52858/1 "2022-04-26T23:03:10Z")

</div>

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.

 ![Web capture_26-4-2022_225422_localhost](https://europe1.discourse-cdn.com/flex013/uploads/rasa/original/3X/f/6/f6d224b1147619ef8d630eca7c5c2dd7f708ce92.jpeg) ![Web capture_26-4-2022_225458_localhost](https://europe1.discourse-cdn.com/flex013/uploads/rasa/original/3X/a/8/a8ea5a32f852b0cce702cc165f544b01c481c66a.jpeg)

![image](https://europe1.discourse-cdn.com/flex013/uploads/rasa/original/3X/b/7/b7624d59b8c32f131ce439d89f698bbf00133798.png)

rasa run actions --debug

 ![image](https://europe1.discourse-cdn.com/flex013/uploads/rasa/original/3X/7/7/77372f08c69b6ca13e4713cd75b637cbbab15c77.png)

- 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}
> 
> ```

---

<div class="post-metadata">

**Author:** ![anca](https://dub1.discourse-cdn.com/flex013/user_avatar/forum.rasa.com/anca/32/14540_2.png) [@anca](https://forum.rasa.com/u/anca)\
**Post date:** [April 29, 2022, 12:59pm UTC](https://forum.rasa.com/t/custom-slot-extraction-and-validation-fails-using-custom-actions/52858/2 "2022-04-29T12:59:15Z")

</div>

@kassem404 First of all the error you get when running the slot validation method `validate_model` is caused by a typo in your return value: it should be using a colon `{model: None}` instead of a comma.

Secondly I’d suggest investigating your if logic - perhaps have a merged list of apple and samsung models and rewrite this to `if slot_value not in merged_list:` At the moment you have a bug there because of the use of `or`, since `samsung_models` evaluates to `True`. Hope this helps!

---

<div class="post-metadata">

**Author:** ![kassem404](https://avatars.discourse-cdn.com/v4/letter/k/71c47a/32.png) [@kassem404](https://forum.rasa.com/u/kassem404)\
**Post date:** [April 29, 2022, 3:06pm UTC](https://forum.rasa.com/t/custom-slot-extraction-and-validation-fails-using-custom-actions/52858/3 "2022-04-29T15:06:47Z")

</div>

Hi, thank you so much for responding to my topic. Indeed I’ve noticed those two mistakes but somehow it didn’t fix my other issue, so I did more documentation reading and research and found out that the custom slot extract actions are always run on the background to keep the slots updated.

Here’s the action server log after fixing the two mistakes (I added a print in all methods for debugging):

> (venv) PS D:\chatbotTest\> rasa run actions --debug D:\FinanceBot\venv\lib\site-packages\sanic\_cors\extension.py:39: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead. SANIC\_VERSION = LooseVersion(sanic\_version) 2022-04-29 13:36:37 INFO rasa\_sdk.endpoint - Starting action endpoint server… 2022-04-29 13:36:37 INFO rasa\_sdk.executor - Registered function for ‘action\_ask\_brand’. 2022-04-29 13:36:37 INFO rasa\_sdk.executor - Registered function for ‘action\_ask\_model’. 2022-04-29 13:36:37 INFO rasa\_sdk.executor - Registered function for ‘validate\_purchase\_form’.  
> 2022-04-29 13:36:37 INFO rasa\_sdk.endpoint - Action endpoint is up and running on [http://0.0.0.0:5055](http://0.0.0.0:5055) 2022-04-29 13:36:37 DEBUG rasa\_sdk.utils - Using the default number of Sanic workers (1). 2022-04-29 13:37:14 DEBUG rasa\_sdk.executor - Received request to run ‘action\_ask\_brand’ Ask brand 2022-04-29 13:37:14 DEBUG rasa\_sdk.executor - Finished running ‘action\_ask\_brand’ 2022-04-29 13:37:19 DEBUG rasa\_sdk.executor - Received request to run ‘validate\_purchase\_form’  
> Extract brand Extract model Validate brand Validate model 2022-04-29 13:37:19 DEBUG rasa\_sdk.executor - Finished running ‘validate\_purchase\_form’ 2022-04-29 13:37:22 DEBUG rasa\_sdk.executor - Received request to run ‘action\_ask\_model’ Ask model 2022-04-29 13:37:22 DEBUG rasa\_sdk.executor - Finished running ‘action\_ask\_model’ 2022-04-29 13:37:42 DEBUG rasa\_sdk.executor - Received request to run ‘validate\_purchase\_form’  
> Extract brand Extract model Validate brand Validate model 2022-04-29 13:37:42 DEBUG rasa\_sdk.executor - Finished running ‘validate\_purchase\_form’ 2022-04-29 13:37:44 DEBUG rasa\_sdk.executor - Received request to run ‘action\_ask\_model’ Ask model 2022-04-29 13:37:44 DEBUG rasa\_sdk.executor - Finished running ‘action\_ask\_model’

I later found someone with the same issue, NoxDv on Github suggested to add a conditional statement in each extract method **if tracker.slots[“requested\_slot”] == “model”** and it worked 👍.

> <https://github.com/RasaHQ/rasa/issues/11067>
>
> \### Rasa Open Source version
> 
> 3.0.9
> 
> \### Rasa SDK version
> 
> 3.0.6
> 
> \### Rasa X ver…sion
> 
> \_No response\_
> 
> \### Python version
> 
> 3.8
> 
> \### What operating system are you using?
> 
> Windows
> 
> \### What happened?
> 
> Hi,
> 
> I tried to implement a form with multiple slots that use custom mappings so that I can extract all the entities by myself. 
> 
> When the form loop starts Rasa correctly detects that the first required slot has to be filled and I get promoted to enter the first sentence/word or whatever. After that, on each loop iteration all extract\_\<slot\> functions get called although only the extract\_\<first\_slot\_that\_has\_to\_be\_filled\> function should be called on the first iteration.
> 
> Domain
> \`\`\`yaml
> actions:
> - validate\_test\_form
> 
> intents:
> - request\_form
> 
> responses:
> utter\_ask\_one:
> - text: "give one"
> utter\_ask\_two:
> - text: "give two"
> utter\_ask\_three:
> - text: "give three"
> 
> slots:
> one:
> type: text
> influence\_conversation: false
> mappings:
> - type: custom
> conditions:
> - active\_loop: test\_form
> requested\_slot: one
> 
> two:
> type: text
> influence\_conversation: false
> mappings:
> - type: custom
> conditions:
> - active\_loop: test\_form
> requested\_slot: two 
> 
> three:
> type: text
> influence\_conversation: false
> mappings:
> - type: custom
> conditions:
> - active\_loop: test\_form
> requested\_slot: three 
> 
> forms:
> test\_form:
> required\_slots:
> - one
> - two
> - three
>       
> 
> session\_config:
> session\_expiration\_time: 60
> carry\_over\_slots\_to\_new\_session: true
> \`\`\`
> 
> Rules:
> \`\`\`yaml
> rules:
> - rule: Activate form
> steps:
> - intent: request\_form
> - action: test\_form
> - active\_loop: test\_form
> - slot\_was\_set:
> - requested\_slot: one
> - requested\_slot: two
> - requested\_slot: three
> \`\`\`
> 
> Actions
> \`\`\`python
> from typing import Any, Text, Dict, List
> from rasa\_sdk import Action, Tracker
> from rasa\_sdk.events import SlotSet, SessionStarted, ActionExecuted, EventType
> from rasa\_sdk import Tracker, FormValidationAction
> from rasa\_sdk.executor import CollectingDispatcher
> 
> 
> class ValidateCustomSlotMappings(FormValidationAction):
> def name(self) -\> Text:
> return "validate\_test\_form"
> 
> async def extract\_one(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict) -\> Dict\[Text, Any\]:
> print("extract one")
> text\_of\_last\_user\_message = tracker.latest\_message.get("text")
> return {"one": text\_of\_last\_user\_message}
> 
> async def extract\_two(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict) -\> Dict\[Text, Any\]:
> print("extract two")
> text\_of\_last\_user\_message = tracker.latest\_message.get("text")
> return {"two": text\_of\_last\_user\_message}
> 
> async def extract\_three(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict) -\> Dict\[Text, Any\]:
> print("extract three")
> text\_of\_last\_user\_message = tracker.latest\_message.get("text")
> return {"three": text\_of\_last\_user\_message}
> \`\`\`
> 
> 
> \### Command / Request
> 
> \_No response\_
> 
> \### Relevant log output
> 
> \_No response\_

Here is the extract method for model after adding that conditional statement:

> ```
> async def extract_model(
> self,
> dispatcher:CollectingDispatcher,
> tracker: Tracker,
> domain: DomainDict,
> ) -> Dict[Text, Any]:
> 
> print("Extract model")
> print(tracker.slots["requested_slot"] == "model")
> if tracker.slots["requested_slot"] == "model":
> text_of_last_user_message = tracker.latest_message.get("text")
> model= text_of_last_user_message
>     
> if model not in models:
> print(model)
>             
> print(tracker.get_slot("model"))
> return {"model": None}
>            
> print(tracker.get_slot("model"))
> return {"model": model}
>         
> print(tracker.get_slot("model")) 
> return {"model": tracker.get_slot("model")}
> 
> ```

Thank you all for the help!
