I'm calling the AWS Lambda repo using AWS CloudAPI for the fulfilment of Actions on Google(AoG). Everything works fine using actions-on-google NodeJS client except the error when which arises while calling from the AoG simulator producing "...isn't responding at the moment. Try again soon"
The node.js AWS Lambda code is as follows,
const { dialogflow } = require('actions-on-google'),
AWS = require('aws-sdk');
const app = dialogflow({
debug: true
});
const iotData = new AWS.IotData({
endpoint: '***thing_id***.iot.us-east-1.amazonaws.com'
}),
name = '***thing_name***',
/** The parameters required to retrieve the state of the endpoint */
paramsToGetThing = {
thingName: name
};
/**
* Notify the AWS IoT endpoint on command
* @param {DialogflowConversation} conv DialogflowConversation instance
* @return {void}
*/
app.intent('Notify IoT Intent', (conv) => {
console.log('Inside Notify IoT Intent function');
return new Promise((resolve, reject) => {
iotData.getThingShadow(paramsToGetThing, function (error, data) {
if (error) {
console.log(error, error.stack);
conv.close('Network error! Please try again after some time.');
reject(error);
} else {
const parsing = JSON.parse(data.payload);
console.log('Parsing state is ' + parsing.state.reported.connected);
if (parsing.state.reported.connected) {
console.log('Device is in online state and publish the command');
publishMessageForCommands(conv.action)
.then((response) => {
console.log('Inside publish message delay case');
setTimeout(function () {
console.log('Inside delaycheck after 2 seconds timeout function');
conv.ask(response);
resolve();
}, 2000); // Two seconds delay before speech response
})
.catch((rejectError) => {
conv.close(rejectError);
console.log('Inside CustomIntentHandler error block.');
reject();
});
} else {
console.log('Inside publish message before time out\nDevice is offline state and cannot publish the message to ' + name);
conv.close('The device is offline, please check the device and try again');
reject();
}
}
});
});
});
/**
* Error handler block
* @param {DialogflowConversation} conv DialogflowConversation instance
* @return {void}
*/
app.catch((conv, error) => {
console.error(error);
conv.ask('I encountered a glitch. Can you say that again?');
});
/**
* Default fallback intent
* @param {DialogflowConversation} conv DialogflowConversation instance
* @return {void}
*/
app.fallback((conv) => {
conv.ask(`I couldn't understand. Can you say that again?`);
});
exports.handler = app;
As I dig into the issue the AWS part found to be clean, as follows It also passed while running on dialogflow,
The problem arises when we try to test it from AoG simulator,
The error message is as follows,
{
insertId: "5a4yrqg2e6598n"
labels: {
channel: "preview"
querystream: "GOOGLE_USER"
source: "JSON_RESPONSE_VALIDATION"
}
logName: "projects/***project_name***/logs/actions.googleapis.com%2Factions"
receiveTimestamp: "2019-05-13T09:31:33.504048852Z"
resource: {
labels: {
action_id: "actions.intent.TEXT"
project_id: "***project_id***"
version_id: ""
}
type: "assistant_action"
}
severity: "ERROR"
textPayload: "MalformedResponse: Webhook error (206)"
timestamp: "2019-05-13T09:31:33.471947959Z"
trace: "projects/263334370390/traces/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ"
}
Sending request with post data:
{
insertId: "1inzh7lg2hdrojj"
labels: {
channel: "preview"
querystream: "GOOGLE_USER"
source: "AOG_REQUEST_RESPONSE"
}
logName: "projects/***projecct_name***/logs/actions.googleapis.com%2Factions"
receiveTimestamp: "2019-05-13T09:31:33.503838806Z"
resource: {
labels: {
action_id: "actions.intent.TEXT"
project_id: "***project_id***"
version_id: ""
}
type: "assistant_action"
}
severity: "DEBUG"
textPayload: "Sending request with post data: {"user":{"userId":"ABwppHE6s78QlB8ah1DEkuPAxvJvH23BWfHmJOjvn1L7KVUb1DfszUh_aIMyifDw1BfPZsH5Z2T1vmQ63Xu1aw","locale":"en-IN","lastSeen":"2019-05-13T09:23:50Z"},"conversation":{"conversationId":"ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ","type":"ACTIVE","conversationToken":"[]"},"inputs":[{"intent":"actions.intent.TEXT","rawInputs":[{"inputType":"KEYBOARD","query":"notify"}],"arguments":[{"name":"text","rawText":"notify","textValue":"notify"}]}],"surface":{"capabilities":[{"name":"actions.capability.ACCOUNT_LINKING"},{"name":"actions.capability.SCREEN_OUTPUT"},{"name":"actions.capability.AUDIO_OUTPUT"},{"name":"actions.capability.WEB_BROWSER"},{"name":"actions.capability.MEDIA_RESPONSE_AUDIO"}]},"isInSandbox":true,"availableSurfaces":[{"capabilities":[{"name":"actions.capability.WEB_BROWSER"},{"name":"actions.capability.AUDIO_OUTPUT"},{"name":"actions.capability.SCREEN_OUTPUT"}]}],"requestType":"SIMULATOR"}."
timestamp: "2019-05-13T09:31:33.018260116Z"
trace: "projects/263334370390/traces/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ"
}
Received response from agent with body:
{
insertId: "1inzh7lg2hdrojk"
labels: {
channel: "preview"
querystream: "GOOGLE_USER"
source: "AOG_REQUEST_RESPONSE"
}
logName: "projects/***project_name***/logs/actions.googleapis.com%2Factions"
receiveTimestamp: "2019-05-13T09:31:33.503838806Z"
resource: {
labels: {
action_id: "actions.intent.TEXT"
project_id: "***project_id***"
version_id: ""
}
type: "assistant_action"
}
severity: "DEBUG"
textPayload: "Received response from agent with body: HTTP/1.1 200 OK
Server: nginx/1.13.6
Date: Mon, 13 May 2019 09:31:33 GMT
Content-Type: application/json;charset=UTF-8
Content-Length: 4441
X-Cloud-Trace-Context: 3de98e1e9a922989893d5778d6cd1232/12117959397276118516;o=0
Google-Actions-API-Version: 2
X-SHARD: shard-2
Via: 1.1 google
Alt-Svc: clear
{"conversationToken":"[]","expectUserResponse":true,"expectedInputs":[{"inputPrompt":{"richInitialPrompt":{"items":[{"simpleResponse":{"textToSpeech":"Done"}}]}},"possibleIntents":[{"intent":"assistant.intent.action.TEXT"}]}],"responseMetadata":{"status":{"code":14,"message":"Webhook error (206)"},"queryMatchInfo":{"queryMatched":true,"intent":"8d15dee5-2250-41fd-a5c3-a5a1bf8b1014"},"delegatedRequest":{"delegatedRequest":"{\n \"responseId\": \"a8fe5f58-23a3-4d72-8f1a-7b1897257fc5\",\n \"queryResult\": {\n \"queryText\": \"notify\",\n \"action\": \"input.Notify\",\n \"parameters\": {\n },\n \"allRequiredParamsPresent\": true,\n \"fulfillmentText\": \"Done\",\n \"fulfillmentMessages\": [{\n \"text\": {\n \"text\": [\"Done\"]\n }\n }],\n \"outputContexts\": [{\n \"name\": \"projects/aog-sample-6c818/agent/sessions/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ/contexts/actions_capability_screen_output\"\n }, {\n \"name\": \"projects/aog-sample-6c818/agent/sessions/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ/contexts/actions_capability_account_linking\"\n }, {\n \"name\": \"projects/aog-sample-6c818/agent/sessions/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ/contexts/actions_capability_audio_output\"\n }, {\n \"name\": \"projects/aog-sample-6c818/agent/sessions/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ/contexts/google_assistant_input_type_keyboard\"\n }, {\n \"name\": \"projects/aog-sample-6c818/agent/sessions/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ/contexts/actions_capability_web_browser\"\n }, {\n \"name\": \"projects/aog-sample-6c818/agent/sessions/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ/contexts/actions_capability_media_response_audio\"\n }],\n \"intent\": {\n \"name\": \"projects/aog-sample-6c818/agent/intents/8d15dee5-2250-41fd-a5c3-a5a1bf8b1014\",\n \"displayName\": \"Notify IoT Intent\"\n },\n \"intentDetectionConfidence\": 1.0,\n \"languageCode\": \"en-in\"\n },\n \"originalDetectIntentRequest\": {\n \"source\": \"google\",\n \"version\": \"2\",\n \"payload\": {\n \"isInSandbox\": true,\n \"surface\": {\n \"capabilities\": [{\n \"name\": \"actions.capability.ACCOUNT_LINKING\"\n }, {\n \"name\": \"actions.capability.SCREEN_OUTPUT\"\n }, {\n \"name\": \"actions.capability.AUDIO_OUTPUT\"\n }, {\n \"name\": \"actions.capability.WEB_BROWSER\"\n }, {\n \"name\": \"actions.capability.MEDIA_RESPONSE_AUDIO\"\n }]\n },\n \"requestType\": \"SIMULATOR\",\n \"inputs\": [{\n \"rawInputs\": [{\n \"query\": \"notify\",\n \"inputType\": \"KEYBOARD\"\n }],\n \"arguments\": [{\n \"rawText\": \"notify\",\n \"textValue\": \"notify\",\n \"name\": \"text\"\n }],\n \"intent\": \"actions.intent.TEXT\"\n }],\n \"user\": {\n \"lastSeen\": \"2019-05-13T09:23:50Z\",\n \"locale\": \"en-IN\",\n \"userId\": \"ABwppHE6s78QlB8ah1DEkuPAxvJvH23BWfHmJOjvn1L7KVUb1DfszUh_aIMyifDw1BfPZsH5Z2T1vmQ63Xu1aw\"\n },\n \"conversation\": {\n \"conversationId\": \"ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ\",\n \"type\": \"ACTIVE\",\n \"conversationToken\": \"[]\"\n },\n \"availableSurfaces\": [{\n \"capabilities\": [{\n \"name\": \"actions.capability.WEB_BROWSER\"\n }, {\n \"name\": \"actions.capability.AUDIO_OUTPUT\"\n }, {\n \"name\": \"actions.capability.SCREEN_OUTPUT\"\n }]\n }]\n }\n },\n \"session\": \"projects/aog-sample-6c818/agent/sessions/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ\"\n}"},"delegatedResponse":{"delegatedResponse":"{\"statusCode\":200,\"body\":\"{\\\"payload\\\":{\\\"google\\\":{\\\"expectUserResponse\\\":true,\\\"richResponse\\\":{\\\"items\\\":[{\\\"simpleResponse\\\":{\\\"textToSpeech\\\":\\\"Notified\\\"}}]}}}}\",\"headers\":{\"content-type\":\"application/json;charset=utf-8\"}}"}}}."
timestamp: "2019-05-13T09:31:33.471512803Z"
trace: "projects/263334370390/traces/ABwppHEjcsRXQzqvNSSYSnGKh-9pWv5_c03_IihzYMPvo7dvPGT_wfuIsvJKt3-BQKXgofT1_FILM_Z8inBiAQ"
}
The expected behaviour is to get the response in the AoG simulator but I'm getting error instead. Any help would be much appreciated.
@Daniel, I also had this problem running dialogFlow on AWS Lambda. I solved it by intercepting the actions-on-google response and sending just the body as a JSON response. Below is my example code. It appears that the actions-on-google sdk wants to return a 200 response when dialogFlow just wants the JSON.
'use strict';
/**
- Lambda functions use a callback function that returns "error and response"
- If there is no error you can usually call the callback function with callback(null,response)
- Many NPM packages handle this callback for you so you dont actually see where the response is sent. By wrapping the lambda handler in another callback function I can see the response that google is sending from my lambda function
*/
exports.handler = (event,context,callback) => {
function googleCallbackInterceptFunction (err, response){
console.log('Google Callback');
console.log(response);
/** ^^ THis logging response shows me that google is wrapping the response in a {body:response, status:200} object. Dialogflow just wants the response so we're going to use the next line vv to pull out the response and just send that*/
response = JSON.parse(response.body);
console.log('Google Callback');
console.log(response);
/** This callback vv is the lambda callback that wraps this entire program. when this callback is fired the lambda sends a response to dialogflow and then terminates itself. Normally the actions-on-google sdk would fire this function but if it does send the wrapped response "{body:response, status:200}" dialogflow is not smart enough to find the response within the body. */
callback(null, response);
}
const {
dialogflow,
Image,
} = require('actions-on-google');
// Create an app instance
const app = dialogflow({
debug: true //THIS IS A CONFIGURATION SETTING BUILT INTO the actions-on-google SDK that WILL LOG THE REQUEST AND RESPONSES (On their own but will not show you that google is also wrapping them in a 200 response object which we dont want)
});
/** 3 basic intents that use the app.intent and conv.ask/conv.close functionality of the actions-on-google sdk. Based on these responses google will send a response that dialogflow can interpret. */
app.intent('Default Welcome Intent', conv => {
console.log('Default welcome intent HIT');
conv.ask('Hi, how is it going?');
conv.ask(`Here's a picture of a cat`);
// conv.ask(new Image({
// url: 'https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/imgs/160204193356-01-cat-500.jpg',
// alt: 'A cat',
// }))
});
// Intent in Dialogflow called `Goodbye`
app.intent('Goodbye', conv => {
conv.close('See you later!')
});
app.intent('Default Fallback Intent', conv => {
conv.ask(`I didn't understand. Can you tell me something else?`)
});
app(event,context,googleCallbackInterceptFunction);//I am initializing the actions-on-google sdk with MY callback function, not the Lambda's. This way I can log the result before the lambda responds and is termindated. MY callback function returns the result to the lambda callback function so the lambda still works as intended.
};