Rasa Node Action Server

Table of Contents

NodeJS alternative to Rasa SDK Action Server

Why

  • Allow people to write their own action server in JavaScript with NodeJS
  • Does not require the use of Rasa-SDK
  • Simple and not verbose action definition

Install

$ npm install rasa-node-action-server

Quickstart

To start a simple Action Server you can use the following quick start example.

// index.js
const { RasaNodeActionServer, RasaActionEvent } = require("./rasa-node-action-server");

const rnas = new RasaNodeActionServer();

rnas.define("action_hello_world", (action, res) => {
  res
    .addEvent(RasaActionEvent.bot("Hello world, from your action server"))
    .send();
});

rnas.start();

and run

node index.js

The RasaNodeActionServer by default will start an express server on localhost:5055. For more examples, check out the examples folder on Github.

Rasa Custom Connectors

If you want to pass additional parameters to your rasa action inside your request, you should use the metadata field as suggested in the rasa documentation.

Custom Connector Example

For a quick start, you can use the following custom connector which allows the metadata parameter to be forwarded to the server.

# custom_connector.py
import asyncio
import inspect
from sanic import Sanic, Blueprint, response
from sanic.request import Request
from sanic.response import HTTPResponse
from typing import Text, Dict, Any, Optional, Callable, Awaitable, NoReturn

import rasa.utils.endpoints
from rasa.core.channels.channel import (
    InputChannel,
    CollectingOutputChannel,
    UserMessage,
)
import logging

logger = logging.getLogger(__name__)

class RasaNodeChannel(InputChannel):
    """A simple web bot that listens on a url and responds."""

    @classmethod
    def name(self) -> Text:
        return "rnc"

    def blueprint(
        self, on_new_message: Callable[[UserMessage], Awaitable[None]]
    ) -> Blueprint:

        custom_webhook = Blueprint(
            "custom_webhook_{}".format(type(self).__name__),
            inspect.getmodule(self).__name__,
        )

        @custom_webhook.route("/", methods=["GET"])
        async def health(request: Request) -> HTTPResponse:
            return response.json({"status": "ok"})

        @custom_webhook.route("/webhook", methods=["POST"])
        async def receive(request: Request) -> HTTPResponse:
            sender_id = request.json.get("sender") # method to get sender_id
            text = request.json.get("message") # method to fetch text
            input_channel = self.name() # method to fetch input channel
            metadata = self.get_metadata(request) # method to get metadata

            collector = CollectingOutputChannel()

            # include exception handling

            await on_new_message(
                UserMessage(
                    text,
                    collector,
                    sender_id,
                    input_channel=input_channel,
                    metadata=metadata,
                )
            )

            return response.json(collector.messages)

        return custom_webhook

    def get_metadata(self, req) -> Text:
        return req.json.get("metadata") or self.name()

and define it inside the credentials.yml file like this:

rest:

custom_channel.RasaNodeChannel:

rasa:
  url: "<http://localhost:5002/api>"

You can then test your server by running:

curl -XPOST <http://localhost:5005/webhooks/rnc/webhook> \\
  -H "Content-type: application/json" \\
  -d '{"sender": "lykos94",  "message": "Hello bot", "metadata":{}}'

response:

[{"recipient_id":"lykos94","text":"Hello world, from your action server"}]

Maintainers


comments powered by Disqus