Trigger a story (or intent) from a custom action?

I have a custom action that calls an api and get a binary response as to whether a user is registered or not.

I need to trigger different stories depending on the case.

So, I have defined two intents in domain.yml - known_user and unknown_user.

How do i trigger one of these intents in the action ?

class ActionGetUser(Action):

    def name(self) -> Text:
        return "action_get_user"

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

        email = tracker.get_slot("email")
        response = api.get_user({"email" :email})
        if response['status_code'] == 200:
            dispatcher.utter_message(response['data']['name'])
            result = [SlotSet("user_status", "known_user")]
        else :
            dispatcher.utter_message(response['data']['message'])
            result = [SlotSet("user_status", "unknown_user")]

        return result

Update : I am using Slots like how @tyd suggested but wonder if there is an approach without Slots?

Hi @agrawal-mohit! I think what you are looking for are slots.

You can set slots in your custom action and use slots to influence how the dialogue progresses.

Yeah, i am using slots currently and it works in simple cases where i can create stories accordingly. But setting a slot just for this purpose seem unnecessary. I’m trying to see if this can be done without setting a slot.

Are you sure you are using slots like the docs? Your original post did not make that clear.

Why do you think that your slot is unnecessary? Storing whether this user is registered or not seems like exactly the right use case. From the docs:

Slots are your bot’s memory. They act as a key-value store which can be used to store information the user provided (e.g their home city) as well as information gathered about the outside world (e.g. the result of a database query)

Well actually I did after posting the question. For this use case yes, slots are fine.

But i was just wondering if there can be a way to do this without involving slots because the main idea is to trigger a specific intent. If your application warrants this mechanism at many places you will end up creating a lot of flags. It’s not a major issue though. Thanks again for helping me out.

1 Like

Slots are important if you require this information later in the context. Suppose the user asks another question and it requires you to know if he is known or not, then register this information in the slot.

However if you require to trigger a particlar flow given a condition in your custom action, you can do so by triggerring a follow up action using the tracker events - Events from where you can continue your story .

1 Like

Thanks @souvikg10

Hello @souvikg10 thank you for information, but i have one use case when follow up intent makes sense.

I have for example 10 stories about one stuff (eg. ask_weather). Each story starts with intent ask_weather and solves almost every specific case, which can most likely to occur (story for form when user is unhelpful or he wants to stop in the middle of the process, he needs to explain slot, he wants to stop and then changes his mind, he wants to stop and then he really wants to stop, he needs to explain and then he changes his mind, (and more and more combinations and scenarios, which handle different user inputs for one stuff…)).

Then in middle of some other story (different topic, eg chitchat), the chatbot asks eg: “are you going outside tonight?”, user responds with “yes” and at this moment I would like to follow intent ask_weather from my custom action. Otherwise, if I want to handle all 10 cases which can happen after ask_weather intent (and I have them already handled somewhere), I would have to manually concat current story with each of these 10 stories. So I have to write these scenarios again behind current story. I hope it’s clear :slight_smile:

Follow up action is not enough and it doesnt solve this. It is like to call one action and thats all. I need to jump to different stories as his “yes” meant exactly /ask_weather.

Thank you very much if you can help me

I found a tricky way to start another story.

The main idea is…
Rasa core will continue story if I can make the list of Tracker’s events that are same as when the user started.

This is easily possible by returning action_listen and the first user intent of the story sequentially.

action.py example:

class CheckMemberAction(Action):
    def name(self) -> Text:
        return "action_check_member"

    @staticmethod
    def start_story_events(story_intent):
        # type: (Text) -> List[Dict]
        return [ActionExecuted("action_listen")] + [UserUttered("/" + story_intent, {
            "intent": {"name": story_intent, "confidence": 1.0},
            "entities": {}
        })]

    def run(self, dispatcher, tracker, domain):
        if is_unknown_user:
            return self.start_story_events("request.signup")
        else:
            return self.start_story_events("request.form_purchase")
    ...

story.md example:

## some story that ends up with action 
 * ...
 - ...
 - action_check_member

## story1 
 * request.signup
 - utter...
 - ..

## story2
 * request.form_purchase
 - utter_...
 - form_purchase
 - ...

If you want to do this in FormAction class, call self.deactivate() first.

   def start_story_events(self, story_intent):
    # type: (Text) -> List[Dict]
    return self.deactivate() + [ActionExecuted("action_listen")] + [UserUttered("/" + story_intent, {
        "intent": {"name": story_intent, "confidence": 1.0},
        "entities": {}
    })]

It works only when the action is placed at the end of the story.
But, I think it could possible in the middle of story… by adding Restarted() and SetSlot() for preserving slot… (I didn’t tried. :wink: )

This method looks a bit tricky and may not be compatible in the next version, but I actually use this very often when the scenario is complicated and has many user selection steps.

12 Likes

Hi @b1uec0in,

thats awesome nasty trick. It really works!!! You save me a lot of writing. So far I only need it for the classic actions.

Thank you very much.