Get my custom action going

Hello everyone

I am a Masterstudent at the University of St.Gallen and currently developing a Chatbot for argumentative learning. Throug your amazing masterclass I’ve managed to create the basic chatbot functions, intents and also got my bot up and running on my own server with rasax. The final step, is that I need to implement a pretrained model and already defined functions(orginally from a slackbot) as a custom action. The original code for the bot is the following:

import slack
import ssl as ssl_lib
import certifi
from ArgumentBot.argument_bot import ArgumentBot
from farm.infer import Inferencer

argument_bot_sent = {}

SLACK_BOT_TOKEN = "<TOKEN>"


def start_argument_bot(web_client: slack.WebClient, user_id: str, channel: str):
argument_bot = ArgumentBot(channel)

welcome = argument_bot.get_message_payload()

response = web_client.chat_postMessage(**welcome)

argument_bot.timestamp = response["ts"]

if channel not in argument_bot_sent:
    argument_bot_sent[channel] = {}
argument_bot_sent[channel][user_id] = argument_bot

@slack.RTMClient.run_on(event="message")
def message(**payload):
data = payload["data"]
web_client = payload["web_client"]
channel_id = data.get("channel")
user_id = data.get("user")
text = data.get("text")

if text and text.lower() == "start":
    return start_argument_bot(web_client, user_id, channel_id)

if "subtype" not in data:
    argument_bot = ArgumentBot(channel_id)
    elements = predict_components(data.get("text"))
    feedback = prepare_feedback(data.get("text"), elements)
    response = argument_bot.get_feedback_payload(feedback)
    web_client.chat_postMessage(**response)


def load_bert_model():
"""Load in the pre-trained model"""
global model
save_dir = 'Model'
model = Inferencer.load(save_dir)

def predict_components(text: str):
text_to_analyze = [{'text': '{}'.format(text)}]
result = model.inference_from_dicts(dicts=text_to_analyze)

annotated_text = [[i['label'], i['start'], i['end']] for i in result[0]['predictions'] if
                  i['probability'] > 0.75]
count = 0
count_claim = 0
count_premise = 0
elements = []
for ann in annotated_text:
    if ann[0] != 'O':
        elements.append({
            'id': count,
            'label': ann[0].lower(),
            'start': ann[1],
            'end': ann[2]
        })
        if ann[0].lower() == 'claim':
            count_claim += 1
        else:
            count_premise += 1
    else:
        continue
    count += 1

return elements, count_claim, count_premise

def prepare_feedback(text: str, elements: tuple):
feedback_text = "Hier kommt das Feedback zu Deiner Argumentation, " \
                "Claims werden *fett* und Premises _kursiv_ dargestellt:\n\n\n"
before = 0
for e in elements[0]:
    start = e['start']
    end = e['end']
    marker = '*' if e['label'] == 'claim' else '_'
    feedback_text += text[before:start]
    feedback_text += marker
    feedback_text += text[start:end]
    feedback_text += marker
    before = end
if before == 0:
    feedback_text += text

if elements[1] > elements[2] or elements[1] < 2:
    if elements[1] < 2:
        feedback_text += "\n\nIch würde dir empfehlen, deinen Text noch argumentativer zu gestalten. " \
                         "Versuche mindestens zwei Claims mit relevanten Premises zu stützen\n"
    else:
        feedback_text += "\n\nIch würde dir empfehlen, deinen Text noch argumentativer zu gestalten. " \
                         "Versuche Deine Claims besser mit relevanten Premises zu stützen\n"
else:
    feedback_text += "\n\nIch empfinde Deine Argumentation als gelungen! " \
                     "Du hast mehrere Aussagen gemacht und diese mit relevanten Premises gestützt. Weiter so!\n"
return feedback_text

if __name__ == "__main__":
  load_bert_model()
  ssl_context = ssl_lib.create_default_context(cafile=certifi.where())
  slack_token = SLACK_BOT_TOKEN
  rtm_client = slack.RTMClient(token=slack_token, ssl=ssl_context)
  rtm_client.start()

So tried to implement the relevant code in a custom action like this:

from rasa_sdk import Action
from farm.infer import Inferencer    

class LoadBot(Action):
  def name(self):
    return "action_loadbot"

  def run():
    """Load in the pre-trained model"""
    global model
    save_dir = 'Model'
    model = Inferencer.load(save_dir)

class PredictComponents(Action):
  def name(self):
    return "action_predictcomponents"

  def run(text: str):
    text_to_analyze = [{'text': '{}'.format(text)}]
    result = model.inference_from_dicts(dicts=text_to_analyze)

    annotated_text = [[i['label'], i['start'], i['end']] for i in result[0]['predictions'] if
                      i['probability'] > 0.75]
    count = 0
    count_claim = 0
    count_premise = 0
    elements = []
    for ann in annotated_text:
        if ann[0] != 'O':
            elements.append({
                'id': count,
                'label': ann[0].lower(),
                'start': ann[1],
                'end': ann[2]
            })
            if ann[0].lower() == 'claim':
                count_claim += 1
            else:
                count_premise += 1
        else:
            continue
        count += 1

    return elements, count_claim, count_premise

class PrepareFeedback(Action):
  def name(self):
    return "action_preparefeedback"

  def run(text: str, elements: tuple):
    feedback_text = "Hier kommt das Feedback zu Deiner Argumentation, " \
                    "Claims werden *fett* und Premises _kursiv_ dargestellt:\n\n\n"
    before = 0
    for e in elements[0]:
        start = e['start']
        end = e['end']
        marker = '*' if e['label'] == 'claim' else '_'
        feedback_text += text[before:start]
        feedback_text += marker
        feedback_text += text[start:end]
        feedback_text += marker
        before = end
    if before == 0:
        feedback_text += text

    if elements[1] > elements[2] or elements[1] < 2:
        if elements[1] < 2:
            feedback_text += "\n\nIch würde dir empfehlen, deinen Text noch argumentativer zu gestalten. " \
                             "Versuche mindestens zwei Claims mit relevanten Premises zu stützen\n"
        else:
            feedback_text += "\n\nIch würde dir empfehlen, deinen Text noch argumentativer zu gestalten. " \
                             "Versuche Deine Claims besser mit relevanten Premises zu stützen\n"
    else:
        feedback_text += "\n\nIch empfinde Deine Argumentation als gelungen! " \
                         "Du hast mehrere Aussagen gemacht und diese mit relevanten Premises gestützt. Weiter so!\n"
    return feedback_text

But I did not work out. Can anybody help with this transformation of the code? Any steps in the right direction would be a help for me. :raised_hands::smiley:

Thank you!

Hi! @TKueng!

What version of rasa are you using? Could you please post you stories as well? I think custom action’s run method is not properly overridden that’s why it is not running. This is the standard structure of any custom action:

from rasa_sdk import Action
from rasa_sdk.events import SlotSet

class ActionCheckRestaurants(Action):
   def name(self) -> Text:
      return "action_check_restaurants"

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

      cuisine = tracker.get_slot('cuisine')
      q = "select * from restaurants where cuisine='{0}' limit 1".format(cuisine)
      result = db.query(q)

      return [SlotSet("matches", result if result is not None else [])]

See the signature of run method in it.

Hello @saurabh-m523

Thank you so much for your first input. Updated the signature (e.g. LoadBot to ActionLoadBot). Rasa versions that I use are the following:

rasa-core==0.11.1
rasa-nlu==0.13.2

The following are my stories-data:

greet+affirmation+retrieve+farewell

  • greet
    • utter_greet
  • affirmation
    • utter_task
  • submit
    • action_load_bot
    • action_predict_components
    • action_prepare_feedback
  • retrieve
    • utter_textback
  • goodbye
    • utter_goodbye

greet+explanation+affirmation+retrieve+farewell

  • greet
    • utter_greet
  • explanation
    • utter_explanation
  • affirmation
    • utter_task
  • submit
    • action_load_bot
    • action_predict_components
    • action_prepare_feedback
  • retrieve
    • utter_textback
  • goodbye
    • utter_goodbye

affirmation+retrieve+farewell

  • affirmation
    • utter_task
  • submit
    • action_load_bot
    • action_predict_components
    • action_prepare_feedback
  • retrieve
    • utter_textback
  • goodbye
    • utter_goodbye

I have a question concerning the run function. Is it obligatory to use/specify the arguments dispatcher, tracker, and domain? And is it possible to deploy own arguments like the one I used (e.g. text: str)?

Thank your for your help! Best greetings from Switzerland!:switzerland::switzerland:

Hey, you are using very old version, I can’t help you with that :sweat_smile: as I have worked only on versions when rasa nlu and core were combined into rasa. Is it possible for you to upgrade? If so I highly recommend you to upgrade to the latest version.

Now, for the run function (for rasa version > 1.X.X), yes the run function should have exactly these three arguments. This is because at runtime rasa will call this function and pass the required arguments automatically.

1 Like