diff --git a/dialogflow_task_executive/CMakeLists.txt b/dialogflow_task_executive/CMakeLists.txt index eec641946..acbc5e0ff 100644 --- a/dialogflow_task_executive/CMakeLists.txt +++ b/dialogflow_task_executive/CMakeLists.txt @@ -21,15 +21,19 @@ find_package(catkin REQUIRED COMPONENTS add_message_files( FILES DialogResponse.msg + IntentInfo.msg ) add_action_files( FILES DialogText.action + RegisterIntent.action + ListIntent.action ) generate_messages( DEPENDENCIES + std_msgs actionlib_msgs ) diff --git a/dialogflow_task_executive/README.md b/dialogflow_task_executive/README.md index 7b27db552..5e5a89924 100644 --- a/dialogflow_task_executive/README.md +++ b/dialogflow_task_executive/README.md @@ -65,6 +65,8 @@ Please read [app_manager](https://github.com/PR2/app_manager/) for more detailed ## Create new Intent in Dialogflow +### Web console + For your new task, create new Intent as below. `Action` section, you can set full name (`/`), app name or camel-cased name of your `app_manager` app. @@ -78,6 +80,39 @@ If your app is registered as `your_package/your_demo`, you need to set `your_pac In order to fulfill other forms, please read [dialogflow doc](https://dialogflow.com/docs/intents) for more detailed information abount Intent. +### ROS node using API + +You can add new intent with actionlib. First, you launch + +```shell +roslaunch dialogflow_task_executive dialogflow_intent_client.launch credential:= +``` +or make `launch_intent_client` arg true in `dialogflow_ros.launch` . + +To check registered intent, call the action `~list_intent_action` . + +To register the intent, call the action `~register_intent_action` . For example, if you want to add the intent, named `hello`, invoked with the word `hi`, you call the action on shell like + +```shell +rostopic pub /dialogflow_intent_client/register_intent_action/goal dialogflow_task_executive/RegisterIntentActionGoal "header: + seq: 0 + stamp: + secs: 0 + nsecs: 0 + frame_id: '' +goal_id: + stamp: + secs: 0 + nsecs: 0 + id: '' +goal: + intent: + intent: 'hello' + concat_training_phrases: + - '' + message_texts: + - 'hi'" +``` ## Usage diff --git a/dialogflow_task_executive/action/ListIntent.action b/dialogflow_task_executive/action/ListIntent.action new file mode 100644 index 000000000..7d2d078d4 --- /dev/null +++ b/dialogflow_task_executive/action/ListIntent.action @@ -0,0 +1,6 @@ +std_msgs/Empty request +--- +dialogflow_task_executive/IntentInfo[] intents +bool done +--- +string status diff --git a/dialogflow_task_executive/action/RegisterIntent.action b/dialogflow_task_executive/action/RegisterIntent.action new file mode 100644 index 000000000..dd6d81d65 --- /dev/null +++ b/dialogflow_task_executive/action/RegisterIntent.action @@ -0,0 +1,5 @@ +dialogflow_task_executive/IntentInfo intent +--- +bool done +--- +string status diff --git a/dialogflow_task_executive/launch/dialogflow_intent_client.launch b/dialogflow_task_executive/launch/dialogflow_intent_client.launch new file mode 100644 index 000000000..f25db1127 --- /dev/null +++ b/dialogflow_task_executive/launch/dialogflow_intent_client.launch @@ -0,0 +1,11 @@ + + + + + + google_cloud_credentials_json: $(arg credential) + + + diff --git a/dialogflow_task_executive/launch/dialogflow_ros.launch b/dialogflow_task_executive/launch/dialogflow_ros.launch index fa0045df8..679cab919 100644 --- a/dialogflow_task_executive/launch/dialogflow_ros.launch +++ b/dialogflow_task_executive/launch/dialogflow_ros.launch @@ -10,6 +10,7 @@ + + + + + + + diff --git a/dialogflow_task_executive/msg/IntentInfo.msg b/dialogflow_task_executive/msg/IntentInfo.msg new file mode 100644 index 000000000..332e81164 --- /dev/null +++ b/dialogflow_task_executive/msg/IntentInfo.msg @@ -0,0 +1,3 @@ +string intent +string[] concat_training_phrases +string[] message_texts diff --git a/dialogflow_task_executive/node_scripts/dialogflow_client.py b/dialogflow_task_executive/node_scripts/dialogflow_client.py index cb4253243..df562c9b7 100644 --- a/dialogflow_task_executive/node_scripts/dialogflow_client.py +++ b/dialogflow_task_executive/node_scripts/dialogflow_client.py @@ -117,7 +117,7 @@ def make_dialog_msg(self, result): # check if ROS_PYTHON_VERSION exists in indigo if (self.language == 'ja-JP' and ("ROS_PYTHON_VERSION" not in os.environ - or os.environ["ROS_PYTHON_VERSION"] == "2")): + or sys.version_info.major <= 2)): msg.query = result.query_text.encode("utf-8") msg.response = result.fulfillment_text.encode("utf-8") else: diff --git a/dialogflow_task_executive/node_scripts/dialogflow_intent_node.py b/dialogflow_task_executive/node_scripts/dialogflow_intent_node.py new file mode 100644 index 000000000..b07e6e0f9 --- /dev/null +++ b/dialogflow_task_executive/node_scripts/dialogflow_intent_node.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +import actionlib +from dialogflow_task_executive.msg import RegisterIntentAction, RegisterIntentGoal, RegisterIntentResult, RegisterIntentFeedback +from dialogflow_task_executive.msg import ListIntentAction, ListIntentGoal, ListIntentResult, ListIntentFeedback +from dialogflow_task_executive.msg import IntentInfo +import google.cloud.dialogflow as df +from google.oauth2.service_account import Credentials +import rospy + +class DialogflowIntentClient(object): + def __init__(self): + credentials_json = rospy.get_param( + '~google_cloud_credentials_json', None) + credentials = Credentials.from_service_account_file(credentials_json) + self.intents_client = df.IntentsClient(credentials=credentials) + self.project_id = credentials.project_id + self._intent_create_as = actionlib.SimpleActionServer("~register_intent_action", + RegisterIntentAction, + execute_cb=self.register_cb, + auto_start=False) + self._intent_list_as = actionlib.SimpleActionServer("~list_intent_action", + ListIntentAction, + execute_cb=self.list_cb, + auto_start=False) + self._intent_create_as.start() + self._intent_list_as.start() + + def register_df_intent(self, intent_name, training_phrases, message_texts): + parent = df.AgentsClient.agent_path(self.project_id) + phrases = [] + for phrase in training_phrases: + part = df.Intent.TrainingPhrase.Part(text=phrase) + training_phrase = df.Intent.TrainingPhrase(parts=[part]) + phrases.append(training_phrase) + text = df.Intent.Message.Text(text=message_texts) + message = df.Intent.Message(text=text) + intent = df.Intent( + display_name=intent_name, training_phrases=phrases, messages=[message]) + response = self.intents_client.create_intent( + request={"parent": parent, "intent": intent} + ) + return response + + def list_df_intent(self): + parent = df.AgentsClient.agent_path(self.project_id) + req = df.ListIntentsRequest(parent=parent, + intent_view=df.IntentView.INTENT_VIEW_FULL) + intents = self.intents_client.list_intents(request=req) + msgs = [] + for intent in intents: + msg = IntentInfo() + msg.intent = intent.display_name + for training_phrase in intent.training_phrases: # training phrases + phrase = "" + for part in training_phrase.parts: + phrase += str(part.text) + msg.concat_training_phrases.append(str(phrase)) + for message in intent.messages: + msg.message_texts.append(str(message.text)) + msgs.append(msg) + return msgs + + def register_cb(self, goal): + feedback = RegisterIntentFeedback() + result = RegisterIntentResult() + success = False + try: + res = self.register_df_intent(goal.intent.intent, + goal.intent.concat_training_phrases, + goal.intent.message_texts) + feedback.status = str(res) + success = True + rospy.loginfo("Succeeded in registering the intent: {}".format(goal.intent.intent)) + except Exception as e: + rospy.logerr(str(e)) + feedback.status = str(e) + success = False + finally: + self._intent_create_as.publish_feedback(feedback) + result.done = success + self._intent_create_as.set_succeeded(result) + + def list_cb(self, goal): + feedback = ListIntentFeedback() + result = ListIntentResult() + success = False + try: + msgs = self.list_df_intent() + intents_info = "" + for msg in msgs: + result.intents.append(msg) + intents_info += "{}, ".format(msg.intent) + success = True + rospy.loginfo("Listed intents: {}".format(intents_info)) + # from IPython.terminal import embed; ipshell=embed.InteractiveShellEmbed(config=embed.load_default_config())(local_ns=locals()) + except Exception as e: + rospy.logerr(str(e)) + feedback.status = str(e) + success = False + finally: + self._intent_list_as.publish_feedback(feedback) + result.done = success + self._intent_list_as.set_succeeded(result) + + +if __name__ == "__main__": + rospy.init_node("dialogflow_intent_manager") + node = DialogflowIntentClient() + rospy.spin() diff --git a/dialogflow_task_executive/requirements.in b/dialogflow_task_executive/requirements.in index e0fae0c7e..541f8331c 100644 --- a/dialogflow_task_executive/requirements.in +++ b/dialogflow_task_executive/requirements.in @@ -1 +1,3 @@ dialogflow==1.1.1 +google-auth==2.9.0 +google-cloud-dialogflow==2.14.1 diff --git a/dialogflow_task_executive/requirements.in.noetic b/dialogflow_task_executive/requirements.in.noetic index 63615e454..2591bca47 100644 --- a/dialogflow_task_executive/requirements.in.noetic +++ b/dialogflow_task_executive/requirements.in.noetic @@ -4,3 +4,5 @@ grpcio-status==1.48.1 googleapis-common-protos[grpc]==1.56.2 protobuf==3.20.1 # fix Could not find a version that matches protobuf<4.0.0dev,<5.0.0dev,>=3.15.0,>=3.20.1,>=4.21.3 (from google-api-core[grpc]==1.33.1->dialogflow==1.1.1->-r requirements.in (line 1)) grpcio==1.54.0 # via google-api-core, googleapis-common-protos, grpcio-status +google-auth==2.9.0 +google-cloud-dialogflow==2.14.1 diff --git a/google_chat_ros/scripts/google_chat_ros_node.py b/google_chat_ros/scripts/google_chat_ros_node.py index a66d1ba7b..43031e417 100644 --- a/google_chat_ros/scripts/google_chat_ros_node.py +++ b/google_chat_ros/scripts/google_chat_ros_node.py @@ -222,13 +222,11 @@ def event_cb(self, event, publish_topic=True): action = event.get('action') msg.action.action_method_name = action.get('actionMethodName') if action.get('parameters'): - parameters = [] for param in action.get('parameters'): action_parameter = ActionParameter() - action_parameter.key = param.get('key') - action_parameter.value = param.get('value') - parameters.append(action_parameter) - msg.action.parameters = parameters + action_parameter.key = param.get("key") if param.get("key") else "" + action_parameter.value = param.get("value") if param.get("value") else "" + msg.action.parameters.append(action_parameter) if publish_topic: self._card_activity_pub.publish(msg) return msg diff --git a/google_chat_ros/src/google_chat_ros/google_chat.py b/google_chat_ros/src/google_chat_ros/google_chat.py index f704bf67d..c95b78a8e 100644 --- a/google_chat_ros/src/google_chat_ros/google_chat.py +++ b/google_chat_ros/src/google_chat_ros/google_chat.py @@ -125,7 +125,7 @@ def _pubsub_cb(self, message): except Exception as e: rospy.logerr("Failed to handle the request from Cloud PubSub.") rospy.logerr("It might be caused because of invalid type message from GCP") - rospy.logerr("{}".str(e)) + rospy.logerr(e) finally: message.ack()