Dynamic Action implementation

I have got a dictionary of some questions and its respected feedback according to user input. I’ve some parts name like Battery, Accelerator, Looking Glass etc. If the user input Battery then the action will generate battery-related questions dictionary through API. My action is given below.

> class ActionPartsQuestion(Action):
>     """ Fetch the Year using api """
>     def name(self):
>         return "action_parts_question"
>     def run(self, dispatcher: CollectingDispatcher,
>             tracker: Tracker,
>             domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:
>         global last_actions, model, parts
>         last_actions = "action_parts_question"
>         link = "http://mind.zantrik.com/api/A1/GetPartsQA?PartsName="+"battery"
>         r = requests.get(link)
>         response = r.text
>         json_data = json.loads(response)
>         l = {i["text"]:i["value"] for i in json_data["data"]}
>         return [UserUtteranceReverted()]

So far it can generate the dictionary according to user input. Now I want to ask this list of questions generated by API one after another. If one of the replies is negative I’ll throw the respected feedback from the dictionary and it will terminate. In which approach I should go for?

Any kind of suggestion will be highly appreciated.

You could try using a form with dynamically generated required slots in accordance with the list of questions, and have the validation function check that the reply is positive. You could then use the validation functions to check that the responses are positive - if you’re just expecting yes/nos this could be just one function. You’d need to overwrite a few of the built in forms methods.

1 Like

Hey @mloubser Thanks for your response. Could you please share some resources where I can get the concept of form with dynamically generated required slots?

If you look at the docstring for required_slots it says:

   Use `tracker` to request different list of slots depending on the state of the dialogue 

A simple example in sort of pseudo code:

class MyForm(FormAction):
    def required_slots(tracker: "Tracker") -> List[Text]:
        """A list of required slots that the form has to fill.

        Use `tracker` to request different list of slots
        depending on the state of the dialogue
        if tracker.latest_message.get("intent") == "myintent":
           dynamic_slots = [x,y,z]
           dynamic_slots = [a,b,c]

        return [dynamic_slots]

You could check any condition in the tracker - a slot value, intent, etc. And you could have as much logic as you want for generating the list of slots required.

You’ll need a utter_ask_<slot> method for each required slot though, so keep in mind for this you may also want to overwrite other methods of the form class.

1 Like

If you know ahead of time all the possible required slots, you can set all the utter_ask_<slot> in your domain as usual, but if this also will be dynamic, you’ll need to overwrite e.g.

class MyForm(FormAction):
    def request_next_slot(
        dispatcher: "CollectingDispatcher",
        tracker: "Tracker",
        domain: Dict[Text, Any],
    ) -> Optional[List[EventType]]:
        """Request the next slot and utter template if needed,
            else return None"""

        for slot in self.required_slots(tracker):
            if self._should_request_slot(tracker, slot):
                logger.debug(f"Request next slot '{slot}'")
## Replace the following line with your dynamically generated text to request the slot at hand
                dispatcher.utter_message(template=f"utter_ask_{slot}", **tracker.slots) 
                return [SlotSet(REQUESTED_SLOT, slot)]

        # no more required slots to fill
        return None