diff --git a/.gitignore b/.gitignore
index e5c2e6d5..b21ce3d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -122,3 +122,5 @@ harmoni_actuators/harmoni_tts/content/*
doc/_build
harmoni_actuators/harmoni_tts/scale_stats.npy
harmoni_actuators/harmoni_tts/temp_data/tts.wav
+
+harmoni_actuators/harmoni_tts/TTS/
diff --git a/dockerfiles/harmoni/kinetic/base/dockerfile b/dockerfiles/harmoni/kinetic/base/dockerfile
index 3d4bf170..df8ebaa4 100644
--- a/dockerfiles/harmoni/kinetic/base/dockerfile
+++ b/dockerfiles/harmoni/kinetic/base/dockerfile
@@ -89,6 +89,8 @@ RUN \
google-cloud-speech==1.3.2 \
dialogflow \
google-api-python-client \
+ # Rasa
+ rasa==2.7.1 \
# Local STT
deepspeech \
# Testing
diff --git a/dockerfiles/harmoni/noetic/base/dockerfile b/dockerfiles/harmoni/noetic/base/dockerfile
index b315e02c..e3f1f773 100644
--- a/dockerfiles/harmoni/noetic/base/dockerfile
+++ b/dockerfiles/harmoni/noetic/base/dockerfile
@@ -66,6 +66,8 @@ RUN \
google-cloud-speech==1.3.2 \
dialogflow \
google-api-python-client \
+ # Rasa
+ rasa==2.7.1 \
# Local STT
deepspeech \
# Testing
diff --git a/harmoni_core/harmoni_pattern/config/configuration.yaml b/harmoni_core/harmoni_pattern/config/configuration.yaml
index 52b6069a..24b79f02 100644
--- a/harmoni_core/harmoni_pattern/config/configuration.yaml
+++ b/harmoni_core/harmoni_pattern/config/configuration.yaml
@@ -23,6 +23,11 @@ mic_test:
trigger_intent: "Hey"
pattern_scripting: $(find harmoni_pattern)/pattern_scripting/mic_test.json
+chatbot:
+ default_param:
+ trigger_intent: "Hey"
+ pattern_scripting: $(find harmoni_pattern)/pattern_scripting/chatbot.json
+
speak_test:
default_param:
diff --git a/harmoni_core/harmoni_pattern/pattern_scripting/chatbot.json b/harmoni_core/harmoni_pattern/pattern_scripting/chatbot.json
new file mode 100644
index 00000000..f9d4e0c5
--- /dev/null
+++ b/harmoni_core/harmoni_pattern/pattern_scripting/chatbot.json
@@ -0,0 +1,55 @@
+[
+ {
+ "set": "setup",
+ "steps": [
+ {
+ "microphone_default": {
+ "action_goal": "ON",
+ "resource_type": "sensor",
+ "wait_for": ""
+ }
+ },
+ {
+ "stt_default": {
+ "action_goal": "ON",
+ "resource_type": "detector",
+ "wait_for": ""
+ }
+ }
+ ]
+ },
+ {
+ "set": "loop",
+ "steps": [
+ {
+ "stt_default": {
+ "resource_type": "detector",
+ "wait_for": "new"
+ }
+ },
+ {
+ "bot_default": {
+ "action_goal": "REQUEST",
+ "resource_type": "service",
+ "wait_for": "new"
+ }
+ },
+ {
+ "tts_default": {
+ "action_goal": "REQUEST",
+ "resource_type": "service",
+ "wait_for": "new"
+ }
+ },
+ [
+ {
+ "speaker_default": {
+ "action_goal": "DO",
+ "resource_type": "actuator",
+ "wait_for": "new"
+ }
+ }
+ ]
+ ]
+ }
+]
diff --git a/harmoni_dialogues/harmoni_bot/README.md b/harmoni_dialogues/harmoni_bot/README.md
index 00d242fe..cac46d65 100644
--- a/harmoni_dialogues/harmoni_bot/README.md
+++ b/harmoni_dialogues/harmoni_bot/README.md
@@ -1,6 +1,16 @@
# HARMONI Bot
-This package wraps different chatbot services that can be used with HARMONI. Currently we support AWS Lex and Goodle Dialogflow. Rasa is a high priority on our roadmap for local chatbot functionality.
+This package wraps different chatbot services that can be used with HARMONI. Currently we support AWS Lex, Google Dialogflow, and Rasa.
+
+### Using the Rasa service
+The default Rasa assistant is `rasa_example`. The Rasa workspace for this assistant is located in `harmoni_models/bot`,
+and the assistant name can be set in `configuration.yaml` by changing the rasa_assistant parameter value to the name of the corresponding workspace.
+
+The start_rasa_server.sh script is called before the service is launched and gets the correct path to train and run the Rasa server through rosparam.
+The model is only trained and run if it is a valid bot within `harmoni_models/bot`.
+
+### Adding Rasa assistants
+Custom Rasa bots must also be placed in `harmoni_models/bot` for the start_server script to find the workspace path.
## Usage
## Parameters
@@ -14,6 +24,14 @@ Parameters input for the aws lex service:
|bot_alias | | |
|region_name | | |
+Parameters input for the Rasa service:
+
+| Parameters | Definition | Values |
+|----------------------|------------|--------|
+|rasa_assistant | | |
+|host | | |
+|post | | |
+
## Testing
## References
[Documentation](https://harmoni.readthedocs.io/en/latest/packages/harmoni_bot.html)
\ No newline at end of file
diff --git a/harmoni_dialogues/harmoni_bot/config/configuration.yaml b/harmoni_dialogues/harmoni_bot/config/configuration.yaml
index 2c31bd18..cca383ab 100644
--- a/harmoni_dialogues/harmoni_bot/config/configuration.yaml
+++ b/harmoni_dialogues/harmoni_bot/config/configuration.yaml
@@ -22,3 +22,9 @@ google:
language: "en"
session_id: "testing"
credential_path: "$(find harmoni_bot)/config/private-keys_en.json"
+
+rasa:
+ default_param:
+ rasa_assistant: "rasa_example"
+ host: "localhost"
+ port: 5005
diff --git a/harmoni_dialogues/harmoni_bot/launch/bot_service.launch b/harmoni_dialogues/harmoni_bot/launch/bot_service.launch
index 5ea5c4de..17bec0b4 100644
--- a/harmoni_dialogues/harmoni_bot/launch/bot_service.launch
+++ b/harmoni_dialogues/harmoni_bot/launch/bot_service.launch
@@ -1,8 +1,9 @@
-
+
+
@@ -14,4 +15,11 @@
+
+
+
+
+
+
+
diff --git a/harmoni_dialogues/harmoni_bot/nodes/rasa_service.py b/harmoni_dialogues/harmoni_bot/nodes/rasa_service.py
new file mode 100755
index 00000000..0576069d
--- /dev/null
+++ b/harmoni_dialogues/harmoni_bot/nodes/rasa_service.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+
+# Common Imports
+import rospy
+import roslib
+
+from harmoni_common_lib.constants import State
+from harmoni_common_lib.service_server import HarmoniServiceServer
+from harmoni_common_lib.service_manager import HarmoniServiceManager
+
+# Specific Imports
+import argparse
+import os
+import rasa
+import threading
+from harmoni_common_lib.constants import DialogueNameSpace
+from harmoni_bot.rasa_client import RasaClient
+
+
+class RasaService(HarmoniServiceManager):
+ """Rasa chatbot service"""
+
+ def __init__(self, name, param):
+ """Constructor method: Initialization of variables and lex parameters + setting up"""
+ super().__init__(name)
+ """ Initialization of variables and parameters """
+ self.host = param["host"]
+ self.port = param["port"]
+
+ self.rasa_client = RasaClient(
+ self.host,
+ self.port
+ )
+
+ self.state = State.INIT
+ return
+
+ def request(self, input_text):
+ """[summary]
+
+ Args:
+ input_text (str): User request (or input text) for triggering Rasa Intent
+
+ Returns:
+ object: It contains information about the response received (bool) and response message (str)
+ response: bool
+ message: str
+ """
+ rospy.loginfo("Start the %s request" % self.name)
+ self.state = State.REQUEST
+
+ try:
+ rasa_response = self.rasa_client.get_rasa_response(input_text)
+ rospy.loginfo(f"The Rasa response is {rasa_response}")
+ self.state = State.SUCCESS
+ except Exception as e:
+ rospy.loginfo(f"Exception occurred: {e}")
+ self.state = State.FAILED
+ rasa_response = ""
+ self.response_received = True
+ self.result_msg = rasa_response
+ return {"response": self.state, "message": rasa_response}
+
+
+def main():
+ """[summary]
+ Main function for starting HarmoniRasa service
+ """
+ service_name = DialogueNameSpace.bot.name
+ instance_id = rospy.get_param("instance_id") # "default"
+ service_id = f"{service_name}_{instance_id}"
+ try:
+ rospy.init_node(service_name, log_level=rospy.INFO)
+ params = rospy.get_param("rasa" + "/" + instance_id + "_param/")
+ s = RasaService(service_id, params)
+ service_server = HarmoniServiceServer(service_id, s)
+ service_server.start_sending_feedback()
+ rospy.spin()
+ except rospy.ROSInterruptException:
+ pass
+
+
+if __name__ == "__main__":
+ main()
diff --git a/harmoni_dialogues/harmoni_bot/requirements.txt b/harmoni_dialogues/harmoni_bot/requirements.txt
new file mode 100644
index 00000000..72d22dd5
--- /dev/null
+++ b/harmoni_dialogues/harmoni_bot/requirements.txt
@@ -0,0 +1 @@
+rasa==2.7.1
diff --git a/harmoni_dialogues/harmoni_bot/scripts/start_rasa_server.sh b/harmoni_dialogues/harmoni_bot/scripts/start_rasa_server.sh
new file mode 100755
index 00000000..83a067a3
--- /dev/null
+++ b/harmoni_dialogues/harmoni_bot/scripts/start_rasa_server.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+shopt -s dotglob
+shopt -s nullglob
+
+RASA_ASSISTANT=$(rosparam get /rasa/default_param/rasa_assistant)
+RASA_MODEL_DIR=$(rosparam get /bot_model_dir)
+RASA_BOTS=("$RASA_MODEL_DIR/bot/"*)
+
+found=0
+for dir in "${RASA_BOTS[@]}" ; do
+ dir=$(basename "$dir")
+ [[ $dir = "$RASA_ASSISTANT" ]] && found=1
+done
+
+if [ $found == 1 ]; then
+ cd "$RASA_MODEL_DIR"/bot/"$RASA_ASSISTANT" || exit
+ rasa train && rasa run
+else
+ echo "Not a valid Rasa bot"
+fi
\ No newline at end of file
diff --git a/harmoni_dialogues/harmoni_bot/setup.py b/harmoni_dialogues/harmoni_bot/setup.py
index 25781ac2..92c6dbea 100644
--- a/harmoni_dialogues/harmoni_bot/setup.py
+++ b/harmoni_dialogues/harmoni_bot/setup.py
@@ -1,4 +1,5 @@
# ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD
+import os
from distutils.core import setup
from catkin_pkg.python_setup import generate_distutils_setup
@@ -6,7 +7,10 @@
# fetch values from package.xml
setup_args = generate_distutils_setup(
packages=['harmoni_bot'],
- package_dir={'': 'src'},
+ package_dir={
+ '': 'src',
+ 'harmoni_bot': os.path.join('src', 'harmoni_bot')
+ },
)
setup(**setup_args)
diff --git a/harmoni_dialogues/harmoni_bot/src/harmoni_bot/rasa_client.py b/harmoni_dialogues/harmoni_bot/src/harmoni_bot/rasa_client.py
new file mode 100644
index 00000000..818ac4ea
--- /dev/null
+++ b/harmoni_dialogues/harmoni_bot/src/harmoni_bot/rasa_client.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+import logging
+import requests
+
+logging.basicConfig(level=logging.INFO)
+
+
+class RasaClient:
+
+ def __init__(
+ self,
+ host="localhost",
+ port=5005
+ ):
+ self._host = host
+ self._port = port
+
+ def get_rasa_response(self, input_text):
+ headers = {
+ 'Content-Type': 'application/json',
+ }
+ data = '{ "sender": "test_user", "message": "' + input_text + '", "metadata": {} }'
+ response = requests.post(f"http://{self._host}:{self._port}/webhooks/myio/webhook", headers=headers, data=data)
+ logging.info(f"Bot response: \n{response.text}\n")
+ try:
+ text = response.json()[0]["text"]
+ except IndexError:
+ logging.error("Warning: Bad index")
+ text = "I didn't catch that"
+ return text
+
+
+if __name__ == "__main__":
+ rasa_client = RasaClient()
+ r = "Let's start a conversation with the rasa bot. What would you like to say?\n->"
+ while True:
+ i = input(r)
+ r = rasa_client.get_rasa_response(i) + "\n->"
diff --git a/harmoni_dialogues/harmoni_bot/test/rasa.test b/harmoni_dialogues/harmoni_bot/test/rasa.test
new file mode 100644
index 00000000..b9a62ad0
--- /dev/null
+++ b/harmoni_dialogues/harmoni_bot/test/rasa.test
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/harmoni_dialogues/harmoni_bot/test/rostest_rasa.py b/harmoni_dialogues/harmoni_bot/test/rostest_rasa.py
new file mode 100755
index 00000000..83766cb2
--- /dev/null
+++ b/harmoni_dialogues/harmoni_bot/test/rostest_rasa.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python3
+
+# Common Imports
+import rospy
+import sys
+import unittest
+from collections import deque
+
+# Specific Imports
+import requests
+from actionlib_msgs.msg import GoalStatus
+from harmoni_common_lib.action_client import HarmoniActionClient
+from harmoni_common_lib.constants import DialogueNameSpace, ActionType
+from harmoni_common_msgs.msg import harmoniFeedback, harmoniResult
+
+PKG = "test_harmoni_bot"
+
+
+class TestRasa(unittest.TestCase):
+
+ def setUp(self):
+ """
+ Set up the client for requesting to harmoni_bot
+ """
+ rospy.init_node("test_rasa", log_level=rospy.INFO)
+ self.text = rospy.get_param("test_rasa_input")
+ self.instance_id = rospy.get_param("instance_id")
+ self.host = rospy.get_param("rasa/default_param/host")
+ self.port = rospy.get_param("rasa/default_param/port")
+ self.result = False
+ self.name = DialogueNameSpace.bot.name + "_" + self.instance_id
+ self.service_client = HarmoniActionClient(self.name)
+ self.client_result = deque()
+ self.service_client.setup_client(self.name, self.result_cb, self.feedback_cb)
+ # NOTE currently no feedback, status, or result is received.
+ rospy.Subscriber(
+ "/harmoni_bot_default/feedback", harmoniFeedback, self.feedback_cb
+ )
+ rospy.Subscriber("/harmoni_bot_default/status", GoalStatus, self.status_cb)
+ rospy.Subscriber("/harmoni_bot_default/result", harmoniResult, self.result_cb)
+ rospy.loginfo("TestRasa: Started up; waiting for Rasa startup")
+
+ # Wait until Rasa server is running before running any test methods
+ connected = False
+ while not connected:
+ try:
+ headers = {
+ 'Content-Type': 'application/json',
+ }
+ data = '{ "sender": "test_user", "message": "", "metadata": {} }'
+ response = requests.post(
+ f"http://{self.host}:{self.port}/webhooks/myio/webhook", headers=headers, data=data
+ )
+ connected = response.ok
+ except Exception:
+ rospy.loginfo("Retrying server connection...")
+ rospy.sleep(1)
+
+ rospy.loginfo("TestRasa: Started")
+
+ def feedback_cb(self, data):
+ rospy.loginfo(f"Feedback: {data}")
+ self.result = False
+
+ def status_cb(self, data):
+ rospy.loginfo(f"Status: {data}")
+ self.result = False
+
+ def result_cb(self, data):
+ rospy.loginfo(f"Result: {data}")
+ self.result = True
+
+ def test_request_response(self):
+ rospy.loginfo(f"The input text is {self.text}")
+ # expected response from the rasa_example bot when the input text is "Hello"
+ text = "Hey! How are you?"
+ self.service_client.send_goal(
+ action_goal=ActionType.REQUEST.value,
+ optional_data=self.text,
+ wait=True,
+ )
+ rasa_response = self.service_client.get_result()
+ rospy.loginfo("HarmoniResult:" + rasa_response.message)
+ assert rasa_response.message == text
+
+
+def main():
+ # TODO convert to a test suite so that setup doesn't have to run over and over.
+ import rostest
+
+ rospy.loginfo("test_rasa started")
+ rospy.loginfo("TestRasa: sys.argv: %s" % str(sys.argv))
+ rostest.rosrun(PKG, "test_rasa", TestRasa, sys.argv)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/harmoni_dialogues/harmoni_bot/test/test_rasa_client.py b/harmoni_dialogues/harmoni_bot/test/test_rasa_client.py
new file mode 100755
index 00000000..26126cd4
--- /dev/null
+++ b/harmoni_dialogues/harmoni_bot/test/test_rasa_client.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+import mock
+import requests
+import unittest
+
+from harmoni_bot.rasa_client import RasaClient
+
+
+class TestRasaClient(unittest.TestCase):
+
+ def setUp(self):
+ self.client = RasaClient("localhost", 5005)
+
+ @mock.patch("harmoni_bot.rasa_client.requests.post")
+ def test_get_rasa_response(self, mock_post):
+ recipient_id = "test_user"
+ text = "Welcome to the Interaction Lab!"
+ mock_post.return_value.json.return_value = [
+ {"recipient_id": recipient_id, "text": text}
+ ]
+ rasa_response = self.client.get_rasa_response("Hello world!")
+ assert rasa_response == text
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/harmoni_models/bot/rasa_example/actions/__init__.py b/harmoni_models/bot/rasa_example/actions/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/harmoni_models/bot/rasa_example/actions/actions.py b/harmoni_models/bot/rasa_example/actions/actions.py
new file mode 100644
index 00000000..040c2bc0
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/actions/actions.py
@@ -0,0 +1,23 @@
+# This files contains your custom actions which can be used to run
+# custom Python code.
+#
+# See this guide on how to implement these action:
+# https://rasa.com/docs/rasa/custom-actions
+
+
+# This is a simple example for a custom action which utters "Hello World!"
+
+# from typing import Any, Text, Dict, List
+#
+# from rasa_sdk import Action, Tracker
+# from rasa_sdk.executor import CollectingDispatcher
+#
+#
+# class ActionHelloWorld(Action):
+#
+# def name(self) -> Text:
+# return "action_hello_world"
+#
+# def run(self, dispatcher: CollectingDispatcher,
+# tracker: Tracker,
+# domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:
diff --git a/harmoni_models/bot/rasa_example/addons/__init__.py b/harmoni_models/bot/rasa_example/addons/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/harmoni_models/bot/rasa_example/addons/custom_channel.py b/harmoni_models/bot/rasa_example/addons/custom_channel.py
new file mode 100644
index 00000000..85416140
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/addons/custom_channel.py
@@ -0,0 +1,57 @@
+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,
+)
+
+
+class MyIO(InputChannel):
+ def name(self) -> Text:
+ """Name of your custom channel."""
+ return "myio"
+
+ 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
diff --git a/harmoni_models/bot/rasa_example/config.yml b/harmoni_models/bot/rasa_example/config.yml
new file mode 100644
index 00000000..c6c2f39b
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/config.yml
@@ -0,0 +1,39 @@
+# Configuration for Rasa NLU.
+# https://rasa.com/docs/rasa/nlu/components/
+language: en
+
+pipeline:
+# # No configuration for the NLU pipeline was provided. The following default pipeline was used to train your model.
+# # If you'd like to customize it, uncomment and adjust the pipeline.
+# # See https://rasa.com/docs/rasa/tuning-your-model for more information.
+# - name: WhitespaceTokenizer
+# - name: RegexFeaturizer
+# - name: LexicalSyntacticFeaturizer
+# - name: CountVectorsFeaturizer
+# - name: CountVectorsFeaturizer
+# analyzer: char_wb
+# min_ngram: 1
+# max_ngram: 4
+# - name: DIETClassifier
+# epochs: 100
+# constrain_similarities: true
+# - name: EntitySynonymMapper
+# - name: ResponseSelector
+# epochs: 100
+# constrain_similarities: true
+# - name: FallbackClassifier
+# threshold: 0.3
+# ambiguity_threshold: 0.1
+
+# Configuration for Rasa Core.
+# https://rasa.com/docs/rasa/core/policies/
+policies:
+# # No configuration for policies was provided. The following default policies were used to train your model.
+# # If you'd like to customize them, uncomment and adjust the policies.
+# # See https://rasa.com/docs/rasa/policies for more information.
+# - name: MemoizationPolicy
+# - name: RulePolicy
+# - name: TEDPolicy
+# max_history: 5
+# epochs: 100
+# constrain_similarities: true
diff --git a/harmoni_models/bot/rasa_example/credentials.yml b/harmoni_models/bot/rasa_example/credentials.yml
new file mode 100644
index 00000000..b9318e86
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/credentials.yml
@@ -0,0 +1,36 @@
+# This file contains the credentials for the voice & chat platforms
+# which your bot is using.
+# https://rasa.com/docs/rasa/messaging-and-voice-channels
+
+rest:
+# # you don't need to provide anything here - this channel doesn't
+# # require any credentials
+
+addons.custom_channel.MyIO:
+ username: "user_name"
+ another_parameter: "some value"
+
+#facebook:
+# verify: ""
+# secret: ""
+# page-access-token: ""
+
+#slack:
+# slack_token: ""
+# slack_channel: ""
+# slack_signing_secret: ""
+
+#socketio:
+# user_message_evt:
+# bot_message_evt:
+# session_persistence:
+
+#mattermost:
+# url: "https:///api/v4"
+# token: ""
+# webhook_url: ""
+
+# This entry is needed if you are using Rasa X. The entry represents credentials
+# for the Rasa X "channel", i.e. Talk to your bot and Share with guest testers.
+rasa:
+ url: "http://localhost:5002/api"
diff --git a/harmoni_models/bot/rasa_example/data/nlu.yml b/harmoni_models/bot/rasa_example/data/nlu.yml
new file mode 100644
index 00000000..dedbedbf
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/data/nlu.yml
@@ -0,0 +1,92 @@
+version: "2.0"
+
+nlu:
+- intent: greet
+ examples: |
+ - hey
+ - hello
+ - hi
+ - hello there
+ - good morning
+ - good evening
+ - moin
+ - hey there
+ - let's go
+ - hey dude
+ - goodmorning
+ - goodevening
+ - good afternoon
+
+- intent: goodbye
+ examples: |
+ - good afternoon
+ - cu
+ - good by
+ - cee you later
+ - good night
+ - bye
+ - goodbye
+ - have a nice day
+ - see you around
+ - bye bye
+ - see you later
+
+- intent: affirm
+ examples: |
+ - yes
+ - y
+ - indeed
+ - of course
+ - that sounds good
+ - correct
+
+- intent: deny
+ examples: |
+ - no
+ - n
+ - never
+ - I don't think so
+ - don't like that
+ - no way
+ - not really
+
+- intent: mood_great
+ examples: |
+ - perfect
+ - great
+ - amazing
+ - feeling like a king
+ - wonderful
+ - I am feeling very good
+ - I am great
+ - I am amazing
+ - I am going to save the world
+ - super stoked
+ - extremely good
+ - so so perfect
+ - so good
+ - so perfect
+
+- intent: mood_unhappy
+ examples: |
+ - my day was horrible
+ - I am sad
+ - I don't feel very well
+ - I am disappointed
+ - super sad
+ - I'm so sad
+ - sad
+ - very sad
+ - unhappy
+ - not good
+ - not very good
+ - extremly sad
+ - so saad
+ - so sad
+
+- intent: bot_challenge
+ examples: |
+ - are you a bot?
+ - are you a human?
+ - am I talking to a bot?
+ - am I talking to a human?
diff --git a/harmoni_models/bot/rasa_example/data/rules.yml b/harmoni_models/bot/rasa_example/data/rules.yml
new file mode 100644
index 00000000..51f030d0
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/data/rules.yml
@@ -0,0 +1,13 @@
+version: "2.0"
+
+rules:
+
+- rule: Say goodbye anytime the user says goodbye
+ steps:
+ - intent: goodbye
+ - action: utter_goodbye
+
+- rule: Say 'I am a bot' anytime the user challenges
+ steps:
+ - intent: bot_challenge
+ - action: utter_iamabot
diff --git a/harmoni_models/bot/rasa_example/data/stories.yml b/harmoni_models/bot/rasa_example/data/stories.yml
new file mode 100644
index 00000000..360348ea
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/data/stories.yml
@@ -0,0 +1,29 @@
+version: "2.0"
+
+stories:
+- story: happy path
+ steps:
+ - intent: greet
+ - action: utter_greet
+ - intent: mood_great
+ - action: utter_happy
+
+- story: sad path 1
+ steps:
+ - intent: greet
+ - action: utter_greet
+ - intent: mood_unhappy
+ - action: utter_cheer_up
+ - action: utter_did_that_help
+ - intent: affirm
+ - action: utter_happy
+
+- story: sad path 2
+ steps:
+ - intent: greet
+ - action: utter_greet
+ - intent: mood_unhappy
+ - action: utter_cheer_up
+ - action: utter_did_that_help
+ - intent: deny
+ - action: utter_goodbye
diff --git a/harmoni_models/bot/rasa_example/domain.yml b/harmoni_models/bot/rasa_example/domain.yml
new file mode 100644
index 00000000..b5225577
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/domain.yml
@@ -0,0 +1,34 @@
+version: "2.0"
+
+intents:
+ - greet
+ - goodbye
+ - affirm
+ - deny
+ - mood_great
+ - mood_unhappy
+ - bot_challenge
+
+responses:
+ utter_greet:
+ - text: "Hey! How are you?"
+
+ utter_cheer_up:
+ - text: "Here is something to cheer you up:"
+ image: "https://i.imgur.com/nGF1K8f.jpg"
+
+ utter_did_that_help:
+ - text: "Did that help you?"
+
+ utter_happy:
+ - text: "Great, carry on!"
+
+ utter_goodbye:
+ - text: "Bye"
+
+ utter_iamabot:
+ - text: "I am a bot, powered by Rasa."
+
+session_config:
+ session_expiration_time: 60
+ carry_over_slots_to_new_session: true
diff --git a/harmoni_models/bot/rasa_example/endpoints.yml b/harmoni_models/bot/rasa_example/endpoints.yml
new file mode 100644
index 00000000..1128e1d5
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/endpoints.yml
@@ -0,0 +1,42 @@
+# This file contains the different endpoints your bot can use.
+
+# Server where the models are pulled from.
+# https://rasa.com/docs/rasa/model-storage#fetching-models-from-a-server
+
+#models:
+# url: http://my-server.com/models/default_core@latest
+# wait_time_between_pulls: 10 # [optional](default: 100)
+
+# Server which runs your custom actions.
+# https://rasa.com/docs/rasa/custom-actions
+
+#action_endpoint:
+# url: "http://localhost:5055/webhook"
+
+# Tracker store which is used to store the conversations.
+# By default the conversations are stored in memory.
+# https://rasa.com/docs/rasa/tracker-stores
+
+#tracker_store:
+# type: redis
+# url:
+# port:
+# db:
+# password:
+# use_ssl:
+
+#tracker_store:
+# type: mongod
+# url:
+# db:
+# username:
+# password:
+
+# Event broker which all conversation events should be streamed to.
+# https://rasa.com/docs/rasa/event-brokers
+
+#event_broker:
+# url: localhost
+# username: username
+# password: password
+# queue: queue
diff --git a/harmoni_models/bot/rasa_example/tests/test_stories.yml b/harmoni_models/bot/rasa_example/tests/test_stories.yml
new file mode 100644
index 00000000..d46e39b3
--- /dev/null
+++ b/harmoni_models/bot/rasa_example/tests/test_stories.yml
@@ -0,0 +1,91 @@
+#### This file contains tests to evaluate that your bot behaves as expected.
+#### If you want to learn more, please see the docs: https://rasa.com/docs/rasa/testing-your-assistant
+
+stories:
+- story: happy path 1
+ steps:
+ - user: |
+ hello there!
+ intent: greet
+ - action: utter_greet
+ - user: |
+ amazing
+ intent: mood_great
+ - action: utter_happy
+
+- story: happy path 2
+ steps:
+ - user: |
+ hello there!
+ intent: greet
+ - action: utter_greet
+ - user: |
+ amazing
+ intent: mood_great
+ - action: utter_happy
+ - user: |
+ bye-bye!
+ intent: goodbye
+ - action: utter_goodbye
+
+- story: sad path 1
+ steps:
+ - user: |
+ hello
+ intent: greet
+ - action: utter_greet
+ - user: |
+ not good
+ intent: mood_unhappy
+ - action: utter_cheer_up
+ - action: utter_did_that_help
+ - user: |
+ yes
+ intent: affirm
+ - action: utter_happy
+
+- story: sad path 2
+ steps:
+ - user: |
+ hello
+ intent: greet
+ - action: utter_greet
+ - user: |
+ not good
+ intent: mood_unhappy
+ - action: utter_cheer_up
+ - action: utter_did_that_help
+ - user: |
+ not really
+ intent: deny
+ - action: utter_goodbye
+
+- story: sad path 3
+ steps:
+ - user: |
+ hi
+ intent: greet
+ - action: utter_greet
+ - user: |
+ very terrible
+ intent: mood_unhappy
+ - action: utter_cheer_up
+ - action: utter_did_that_help
+ - user: |
+ no
+ intent: deny
+ - action: utter_goodbye
+
+- story: say goodbye
+ steps:
+ - user: |
+ bye-bye!
+ intent: goodbye
+ - action: utter_goodbye
+
+- story: bot challenge
+ steps:
+ - user: |
+ are you a bot?
+ intent: bot_challenge
+ - action: utter_iamabot