Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a local speechbot service #88

Open
wants to merge 30 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6468855
Set up Rasa service
emilyxzhou Jun 21, 2021
c2172c3
Set up Rasa service with temporary data
emilyxzhou Jun 25, 2021
f82e66f
Add bot src files to path
emilyxzhou Jun 28, 2021
606f3e4
Test training and running Rasa from Python script
emilyxzhou Jun 28, 2021
c2e74d7
Remove Rasa server script from launch file
emilyxzhou Jun 28, 2021
c5c222b
Create separate script to launch Rasa server
emilyxzhou Jul 1, 2021
0964d7b
Add rasa_greeter workspace
emilyxzhou Jul 1, 2021
ef5bfbf
Add dependencies
emilyxzhou Jul 1, 2021
29cb991
Add tests
emilyxzhou Jul 1, 2021
b58608b
Launch rasa server with bash script
emilyxzhou Jul 1, 2021
802c646
Move script to start rasa server to /scripts
emilyxzhou Jul 1, 2021
4da9a9d
Merge branch 'develop' of https://github.com/interaction-lab/HARMONI …
emilyxzhou Jul 2, 2021
a015924
Add test
emilyxzhou Jul 3, 2021
1da6989
Update rostests
emilyxzhou Jul 3, 2021
ee82337
Merge branch 'develop' of https://github.com/interaction-lab/HARMONI …
emilyxzhou Jul 12, 2021
1984885
Update README
emilyxzhou Jul 12, 2021
02aac16
Update requirements.txt
emilyxzhou Jul 12, 2021
2175b48
Set harmoniResponse message value
emilyxzhou Jul 12, 2021
38cf49c
Update rostests
emilyxzhou Jul 12, 2021
07f526b
Update rasa version
emilyxzhou Jul 15, 2021
ae2c9ef
Set up chatbot pattern example
emilyxzhou Jul 15, 2021
8cebccf
Use loop in chatbot example
emilyxzhou Jul 19, 2021
30b2a2e
Merge branch 'develop' of https://github.com/interaction-lab/HARMONI …
emilyxzhou Jul 20, 2021
a1b229d
Merge branch 'develop' of https://github.com/interaction-lab/HARMONI …
emilyxzhou Jul 21, 2021
e4ae7f0
Set up voice-based speechbot example
emilyxzhou Jul 21, 2021
e378af2
Remove extra lines
emilyxzhou Jul 22, 2021
6bddee3
Move rasa_example to harmoni_models
emilyxzhou Jul 27, 2021
11b3213
Update script to start rasa server
emilyxzhou Jul 27, 2021
e63969b
Update README
emilyxzhou Jul 27, 2021
4c8baf2
Ignore TTS
chrismbirmingham Aug 5, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dockerfiles/harmoni/kinetic/base/dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions dockerfiles/harmoni/noetic/base/dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions harmoni_core/harmoni_pattern/config/configuration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
55 changes: 55 additions & 0 deletions harmoni_core/harmoni_pattern/pattern_scripting/chatbot.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
]
]
}
]
19 changes: 18 additions & 1 deletion harmoni_dialogues/harmoni_bot/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# 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
There are two default Rasa assistant available: `rasa_example` and `rasa_greeter`. The Rasa workspaces for these assistants are located in
`harmoni_bot/src`, and can be set in `configuration.yaml` by changing the rasa_assistant parameter value to the name of the corresponding workspace:
"rasa_greeter" for `rasa_greeter`, "rasa_example" for `rasa_example`, etc. 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.

### Adding Rasa assistants
Rasa workspaces must be placed in the `harmoni_bot` directory for the start_server script to find the workspace path.

## Usage
## Parameters
Expand All @@ -14,6 +23,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)
6 changes: 6 additions & 0 deletions harmoni_dialogues/harmoni_bot/config/configuration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 9 additions & 1 deletion harmoni_dialogues/harmoni_bot/launch/bot_service.launch
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<!-- This is an example launch file for how a ROS application could use a node -->
<launch>

<arg name="use_aws_lex" default="true"/>
<arg name="use_aws_lex" default="false"/>
<arg name="use_google" default="false"/>
<arg name="use_rasa" default="true"/>
<arg name="instance_id" default="default"/>
<group if="$(arg use_aws_lex)">
<rosparam file="$(find harmoni_bot)/config/configuration.yaml" subst_value="True"/>
Expand All @@ -14,4 +15,11 @@
<param name="instance_id" value="$(arg instance_id)"/>
<node pkg="harmoni_bot" type="google_service.py" name="harmoni_bot_google_$(arg instance_id)" output="screen" args="$(arg instance_id)"/>
</group>
<group if="$(arg use_rasa)">
<rosparam file="$(find harmoni_bot)/config/configuration.yaml" subst_value="True"/>
<param name="instance_id" value="$(arg instance_id)"/>
<param name="harmoni_bot_dir" value="$(find harmoni_bot)"/>
<node pkg="harmoni_bot" type="start_rasa_server.sh" name="start_rasa_server" />
<node pkg="harmoni_bot" type="rasa_service.py" name="harmoni_bot_rasa_$(arg instance_id)" output="screen" args="$(arg instance_id)"/>
</group>
</launch>
93 changes: 93 additions & 0 deletions harmoni_dialogues/harmoni_bot/nodes/rasa_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/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):
"""This is a class representation of a harmoni_dialogue service
(HarmoniServiceManager). It is essentially an extended combination of the
:class:`harmoni_common_lib.service_server.HarmoniServiceServer` and
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how this line is true; HarmoniServiceServer is really just an extension of the HarmoniActionServer and you have not overridden any of its functions

:class:`harmoni_common_lib.service_manager.HarmoniServiceManager` classes

:param name: Name of the current service
:type name: str
:param param: input parameters of the configuration.yaml file
:type param: from yaml
"""

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()
1 change: 1 addition & 0 deletions harmoni_dialogues/harmoni_bot/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rasa==2.7.1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the Rasa version differs between the requirements (2.7.1) file and the docker images (2.4.2)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, the dockerfiles have been updated to 2.7.1 now!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

12 changes: 12 additions & 0 deletions harmoni_dialogues/harmoni_bot/scripts/start_rasa_server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

RASA_ASSISTANT=$(rosparam get /rasa/default_param/rasa_assistant)
RASA_DIR=$(rosparam get /harmoni_bot_dir)

if [ "$RASA_ASSISTANT" == "rasa_example" ] || [ "$RASA_ASSISTANT" == "rasa_greeter" ]
then
cd "$RASA_DIR"/src/"$RASA_ASSISTANT"
rasa train && rasa run
else
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check works for now, but it doesn't work with future bots. Can we dynamically support based on what directories exist in the RASA_DIR?

echo "Not a valid Rasa bot"
fi
RMichaelSwan marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 5 additions & 1 deletion harmoni_dialogues/harmoni_bot/setup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# ! 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

# 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')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know this line isn't needed. harmoni_common_lib doesn't have it and is imported the same way.

},
)

setup(**setup_args)
38 changes: 38 additions & 0 deletions harmoni_dialogues/harmoni_bot/src/harmoni_bot/rasa_client.py
Original file line number Diff line number Diff line change
@@ -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->"
Empty file.
27 changes: 27 additions & 0 deletions harmoni_dialogues/harmoni_bot/src/rasa_example/actions/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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]]:
#
# dispatcher.utter_message(text="Hello World!")
#
# return []
Copy link
Member

@chrismbirmingham chrismbirmingham Jul 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably delete this

Empty file.
Loading