I’m facing this issue for quite some time already and I don’t know what I should do here. I’ve read doc and similar issues although I cannot find the source of my problem. Any help would be very much appreciated.
So, I implemented custom input and output channels cause I need to make a connection with a custom chat-widget we’re having.
My project structure looks like this:
├── app
│ ├── requirements.txt
│ ├── Dockerfile
│ ├── __init__.py
│ ├── models
│ ├── entrypoint-actions.sh
│ ├── endpoints.yml
│ ├── integrations
│ │ ├── __init__.py
│ │ └── suphelp
│ │ ├── suphelp-input.py
│ │ ├── suphelp-output.py
│ │ ├── __init__.py
│ ├── credentials.yml
│ ├── actions
│ ├── config.yml
│ ├── domain.yml
│ └── data
│ ├── nlu
│ ├── replies
│ ├── stories
│ └── rules
The problem I’ having is with suphelp channel implementation.
The content of suphelp-input.py
import inspect
import json
import logging
from typing import Any, Awaitable, Callable, Dict, List, Text
from flask import Blueprint
from rasa.core.channels.channel import (CollectingOutputChannel, InputChannel,
OutputChannel, UserMessage, QueueOutputChannel)
from sanic import Blueprint, response
from sanic.request import Request
from sanic.response import HTTPResponse
log = logging.getLogger("suphelp_input")
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s: %(message)s')
class SuphelperInput(InputChannel):
def name(self) -> Text:
"""Name of your custom channel."""
return "suphelper_input"
def __init__(self, url: Text = "http://localhost:8080/") -> None:
self.url = url
def format_bot_replies(self, output_messages: List[Dict[str, Any]]):
new_output_messages = []
for message in output_messages:
if message.get("buttons") == None:
new_output_messages.extend(
{
"type": "TextResponse",
"text": message.get("text")
}
)
else:
buttons = []
for button in message.get("buttons"):
buttons.extend(
{
"type": "ButtonVariant",
"id": "button-id",
"text": button.get("title")
}
)
new_output_messages.extend(
{
"type": "MarkupResponse",
"title": message.get("text"),
"replyMarkup": {
"buttons": buttons
}
}
)
return new_output_messages
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:
try:
user_id = request.json.get("user").get("id") # method to get sender_id
text = request.json.get("text") # 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=user_id,
input_channel=input_channel,
metadata=metadata,
)
)
return response.json(
body=json.dumps(self.format_bot_replies(collector.messages)),
status=200,
headers={"Content-type": "application/json"}
)
except Exception as error:
log.error(f"Suphelper error when trying to handle message.{error}")
return response.json(
{"status": "failed", "error": f"{error}"}, status=500
)
return custom_webhook
The content of suphelp-output.py:
import json
import logging
import requests
from rasa.core.channels import OutputChannel
from typing import List, Dict, Any, Text
from rasa.core.channels.channel import UserMessage
from sanic.response import HTTPResponse
log = logging.getLogger("suphelp_output")
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s: %(message)s')
class SuphelperBot(OutputChannel):
@classmethod
def name(cls) -> Text:
return "suphelper_output"
def __init__(self, url: str = "http://localhost:8080/") -> None: #SUPHELP_URL
super().__init__()
self.url = url
async def send_text_message(self, recipient_id: Text, text: Text, buttons: List[Dict[Text, Any]] = None, **kwargs: Any):
try:
if buttons == None:
response = {"type": "TextResponse", "text": text}
else:
response = {"type": "MarkupResponse", "title": text, "replyMarkup": {"buttons": buttons}}
post_request = requests.post(
url=self.url,
headers = {"Content-Type": "application/json"},
data=json.dumps(response),
)
post_request.close()
if not post_request.status_code == 200:
log.error(f"Failed to send message to SupHelp channel.\nStatus: {post_request.status_code}.\nBot Response: {response}")
return response
except Exception as error:
log.error(f"Failed to send message to SupHelp channel.")
return HTTPResponse(json.dumps({"error": f"Exception happened while sending messages between assistance and SupHelp widget.\nError: {error}"}), status=500)
async def send_response(self, recipient_id: Text, message: Dict[Text, Any]) -> None:
# Implement the logic to send the response to the recipient using the custom channel
pass
in credentials.yml I’m having it like this
integrations.suphelp.suphelp_input.SuphelperInput:
integrations.suphelp.suphelp_output.SuphelperBot:
url: "http://localhost:8080/"
But I tried different ways of specifying the channel in credentials.yml. None of which work. Every time I run I get this error
RasaException: Failed to find input channel class for 'integrations.suphelp.suphelp_input.SuphelperInput'. Unknown input channel. Check your credentials configuration to make sure the mentioned channel is not misspelled. If you are creating your own channel, make sure it is a proper name of a class in a module.
What am I doing wrong here?