Form action does not trigger after switching to new RASA 1.2.3

Version:

  • rasa 1.2.3
  • rasa-sdk 1.2.0

Problem: When running my bot the form authenticate_form_short is not triggered. it seems like the bot doesn’t know that it is time to start the form. I used to use the old RASA version 0.0.X. Recently I upgrated RASA to 1.2.3. To do this, I started a complete new RASA project with a different folder structure (see figure below). Since that day, my bot doesnt work anymore. The code is mostly the same as in the previous project. All I did was changing some details according to migration guide 1 and migration guide 2.

–> Both are obviously NOT complete :frowning:

I have a very short story called story_testing. Every utter in this story (see below) works as excpected. Once the form is supposed to start, nothing happends! The utter is not triggered. That means, the form doesn’t ask the user to write his name. Instead there is simply a blank line in my frontend. If I write my name MyName, I get the following error message in my terminal 2:

Failed to validate slot name with action authenticate_form_short

Possible Solution A: --> most likely !!!

I think the way I start the bot is not correct. Or is it?

Terminal 1:

rasa run --endpoints endpoints.yml --credentials credentials.yml

Terminal 2:

rasa run actions --actions actions

Terminal 3 (in folder frontend):

livereload

Here is my directory tree:

image

Possible Solution B:

There is a problem with my frontend. However,I think this is not the case! I tried to start the bot only in my terminal. After doing rasa train, I write rasa shell. Afterwards the bot starts. Once the form should be triggered (according my story story_testing) it says:

rasa.core.actions.action - Failed to run custom action ‘authenticate_form_short’. Couldn’t connect to the server at ‘http://localhost:5055/webhook’. Is the server running? Error: Cannot connect to host loc alhost:5055 ssl:None [Connect call failed (’::1’, 5055, 0, 0)]

rasa.core.processor - Encountered an exception while running action ‘authenticate_form_short’. Bot will continue, but the actions events are lost. Please check the logs of your action server for more infor mation.

Possible Solution C:

the action.py file is not in correct folder. Therefore I copied it in 2 folders. You can find it in the action-folder as well in rasa_project folder. I could find both ways on the internet

Possible Solution D:

Maybe my policies are not correct.

config.yml

language: "de_core_news_sm"             
pipeline: pretrained_embeddings_spacy   


policies:
  - name: "KerasPolicy"
    epochs: 400
    max_history: 3
  - name: "MemoizationPolicy"
    max_history: 3
  - name: "FormPolicy"   # To use forms, you also need to include the FormPolicy in your policy configuration file.
  - name: "FallbackPolicy"
    # min confidence needed to accept an NLU prediction
    nlu_threshold: 0.3
    # min confidence needed to accept an action prediction from Rasa Core
    core_threshold: 0.3
    # name of the action to be called if the confidence of intent / action
    # is below the threshold
    fallback_action_name: 'action_default_fallback'

Last but not least, here are some relevant files:

stories.md

## story_testing
* greet
- utter_introduction
* pleaseWait
- utter_alright
* bye
- utter_bye_servicetime
- authenticate_form_short
- form{"name": "authenticate_form_short"}  
- slot{"name": "Sam"} 
- slot{"name_last": "Johns"} 
- slot{"contract_no": "123457890"}      
- form{"name": null}  

actions.py

from datetime import datetime
import datetime as dt
import re
import os
from typing import Dict, Text, Any, List, Union

from dateutil.relativedelta import relativedelta

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

from rasa.nlu.utils import write_json_to_file
from rasa.core.policies.fallback import FallbackPolicy



from rasa.core.agent import Agent
from rasa.core.policies.keras_policy import KerasPolicy
from rasa.core.policies.form_policy import FormPolicy, MemoizationPolicy


class AuthenticateFormShort(FormAction):
    """Short authentication for other questions."""

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

        return "authenticate_form_short"

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

        return ["name", "name_last",
                "contract_no"]  # Every time the form action gets called, it will ask the user for the next slot in required_slots which is not already set. Note the order!!!

    def submit(self, dispatcher, tracker, domain):
        # type: (CollectingDispatcher, Tracker, 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_form_bye', tracker)
        return []

    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 {
            "name": [self.from_entity(entity="name", not_intent=["chitchat"])],
            "name_last": [self.from_entity(entity="name_last", not_intent=["chitchat"])],
            "contract_no": [self.from_entity(entity="contract_no", not_intent=["chitchat"])],
        }

    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():
           *.....The following is only the validation process....*


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

Can you please help me to trigger my form, please!

Hi @Chatbot_Ra,

I think your problem is:

rasa.core.actions.action - Failed to run custom action ‘authenticate_form_short’. Couldn’t connect to the server at ‘http://localhost:5055/webhook’. Is the server running? Error: Cannot connect to host loc alhost:5055 ssl:None [Connect call failed (’::1’, 5055, 0, 0)]

Can you please post the content of endpoints.yml ? Also I think “rasa run actions -vv” would give us more info about what happens.

Alternatively, if there is no confidential content, you could zip the bot, upload it and then I’ll investigate whats wrong with that.

Regards Julian

Hi

Thank you very much for your help!

The content of endpoint.yml:

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

After running rasa run actions -vv I get:

2019-08-28 09:08:59 INFO     rasa_sdk.endpoint  - Starting action endpoint server...
2019-08-28 09:09:06 DEBUG    pykwalify.compat  - Using yaml library: c:\users\123456\appdata\local\continuum\anaconda3\envs\rasa_project\lib\site-packages\ruamel\yaml\__init__.py
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_slot_reset_contract_no'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_slot_reset_birthday'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_slot_reset_name'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_slot_reset_namelast'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_slot_reset_streetName'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_slot_reset_houseNumber'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_slot_reset_zipName'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_slot_reset_cityName'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_default_fallback'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'action_check_working_hours'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form_short'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form_name'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form_namelast'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form_birthday'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form_streetname'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form_housenumber'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form_zip'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form_cityname'.
2019-08-28 09:09:06 INFO     rasa_sdk.executor  - Registered function for 'authenticate_form_contractnumber'.
2019-08-28 09:09:11 INFO     rasa_sdk.endpoint  - Action endpoint is up and running. on ('0.0.0.0', 5055)

Here you can see a list of all my actions and forms that I have defined in my chatbot. I did not mention all the actions and forms (except of authenticate_form_short) in order to keep the example simple.

For me, this looks like all actions and forms are recognized by the code. I doubt that the code itself is wrong, since I get no error message indicating this. Also everthing was working with my old RASA version (with a different folder structure).

Therefore my number 1 solution approach --> find a way to start the bot correctly. Please tell me if you think I am wrong

Hi @Chatbot_Ra,

the syntax and imports seem to be correct, imho. Can you please start the action server with -vv again and then try to trigger the action and post the full verbose output of both rasa and actions here?

Thanks!

Regards Julian

EDIT: While writing the text below, I found the solution to my problem:

It is simple:

Every time the form action gets called, it will ask the user for the next slot in required_slots which is not already set. It does this by looking for a template called utter_ask_{slot_name} , so you need to define these in your domain file for each required slot.

I forgot to define these in my new project. Therefore the form was not triggered!

However, maybe other people have similar problems, therefore I keep the original answer below:


Hey, this is what I did:

I cloned the following rasa bot (formbot): Rasa formbot example

I installed the following packages into my environment:

pip install python-socketio==4.2.1
pip install python-engineio==3.8.2
pip install tf==1.13.2
pip install numpy==1.16.0
pip install -U spacy
python -m spacy download en_core_web_sm   
pip install livereload

Then I changed the config file according to what I have written in my first post. Only exception now is, that I keep using english language this time:

language: "en_core_web_sm"
pipeline: pretrained_embeddings_spacy   #  We started with this one! Used to be called as spacy_sklearn.

I added my frontend files to the project in order to start the bot in my browser. Now it get’s exciting:

I start the bot with the following commands:

Terminal 1:

rasa run --endpoints endpoints.yml --credentials credentials.yml --cors="*" --enable-api

Terminal 2:

rasa run actions --actions actions

Terminal 3 (change directory to frontend):

livereload

Surpriese! --> The bot is running in the browser and the form action works! However, bot does not work if I start it via rasa shell:

 Failed to run custom action 'restaurant_form'. Couldn't connect to the server at 'http://localhost:5055/webhook'. Is the server running? Error: Cannot connect to host localhost:5
055 ssl:None [Connect call failed ('::1', 5055, 0, 0)]

Afterwards my plan was to add my own story to the stories.md file which contains my self-written form action (which used to work in the old RASA versions). Since I know the form is supposed to work (at least the restaurant form in the example), I now expected it to work in this case, too.

–> Unfortunately, the form action doesn’t get triggered when I start it via terminal 1, 2 and 3. Again! However, there is no error message in any terminal what so ever.

–>Therefore I think, it must have sth. to do with my form action itself!!!

Maybe you can have a look at my form please! I tried to compare it with the form used in the example (see Restaurant form in example)`.

Is it possible that my form doesn’t return anything?

Is the syntax correct? Maybe the new version is more strict regarding to this.

actions.py

class AuthenticateFormShort(FormAction):
    """Short authentication for other questions."""

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

        return "authenticate_form_short"

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

        return ["contract_no"]

    def submit(self, dispatcher, tracker, domain):
        # type: (CollectingDispatcher, Tracker, 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_form_bye', tracker)
        return []

    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 {
            "contract_no": [self.from_entity(entity="contract_no", not_intent=["chitchat"])],
        }

    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 == 'contract_no':
                ErlaubteZeichen = '123456789ABCDEFGHIJKLMNPQRSTVWYXZ'

                value = value.replace('O', '0').replace('o', '0')  # replace "o" and "O" with number "0"

                if value.strip(ErlaubteZeichen):
                    dispatcher.utter_template('utter_contract_no_help_A', tracker)
                    # validation failed, set slot to None
                    slot_values[slot] = None
                else:
                    if (len(str(value)) >= 7) and (len(str(value)) <= 10):
                        slot_values[slot] = value
                    else:
                        dispatcher.utter_template('utter_contract_no_help_B', tracker)
                        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()]

I simplified the story as much as posible:

stories.md

##story_testing
* greet
    - utter_introduction
* pleaseWait
    - utter_alright
* bye
    - utter_bye_servicetime
    - authenticate_form_short
    - form{"name": "authenticate_form_short"}  
    - slot{"contract_no": "123457890"}      
    - form{"name": null} 
* thankyou
    - utter_noworries