I need help with Displaying the response I get from my API to Dialogflow UI. Here is my code. I am currently using WebHook to connect Dialogflow to backend in Heroku.
My code
const functions = require('firebase-functions');
var admin = require("firebase-admin");
var serviceAccount = require("../../reactpageagent-dxug-firebase-adminsdk-26f6q-e1563ff30f.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://reactpageagent-dxug.firebaseio.com"
});
const { WebhookClient } = require('dialogflow-fulfillment');
const { Card, Suggestion } = require('dialogflow-fulfillment');
const axios = require('axios');
module.exports = (request, response) => {
const agent = new WebhookClient({ request, response });
function welcome(agent) {
agent.add('Welcome to my agent');
}
function rhymingWordHandler(agent) {
const word = agent.parameters.word;
agent.add(`Here are the rhyming words for ${word}`)
axios.get(`https://api.datamuse.com/words?rel_rhy=${word}`)
.then((result) => {
console.log(result.data);
result.data.map(wordObj => {
console.log(wordObj.word);
agent.add(JSON.stringify(wordObj.word));
return;
// agent.end(`${wordObj.word}`);
});
});
};
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('rhymingWord', rhymingWordHandler);
agent.handleRequest(intentMap);
}
When I console.log my the result. I get the data from the API in my console.log output, but the Data is not displayed in Dialogflow UI I also do not get any error.
Heroku log
I had real trouble with the result.data.map line of code.
In the end, I avoided it and instead processed result.data after the .then((result) => { line by checking the array length that my API returned, and if it was > 0, loop through it to output each line individually, using agent.add. If the array length was 0, I used agent.add to display a message saying 'No records found'. I used a catch to log any errors (again, using agent.add to send an error message to the user).
I have a problem testing my Skill on actions on google and in my mobile. It works fine with dialogflow but when I arrive to the Intent defined by the function questions_ready on the assistent of google or in the actions google web page to test it, I have the error "'final_response' must be set.". But it works on Dialogflow, so I dont know where it is the error. I leave here my code hoping some of you can help me! Thank you!
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {WebhookClient} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
// Variables
var procedure;
var procedure;
var finish = "Ok, thank you. We have finished the questions";
var status_questions = [
{question: 'Ok, here is your first question. Please, could you tell me how do you feel on a scale from 1 to 10 ? Where 1 is very bad and 10 is very good'},
{question: 'Your workload is very high, high, normal, low or very low?'},
{question: 'Tell me what is your level of monotony on a scale from 1 to 10. Where 1 is very bad and 10 is very good'}];
var currentIndex = 0;
var currentQuestion = "";
var flag = 0;
var list_answers = [];
var list_index = [];
var list_timestamps = [];
var list_questions = [];
const dialogflowAgentRef = db.collection('dialogflow').doc();
//
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function welcome(agent) {
agent.add(`Welcome to my agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function manual(agent) {
const problem = agent.parameters.manual_problems;
if (problem == "DDoS problem"){
procedure = `Ok! Let's see how to solve your `+ problem + `. Specify here the procedure to resolve DDoS problem`;
}
else {
procedure = `Sorry, we don't have a manual to resolve that problem`;
}
agent.add((procedure));
}
function user_name(agent){
const user = agent.parameters.names;
return db.runTransaction(t => {
t.set(dialogflowAgentRef, {name: user});
return Promise.resolve('Write complete');
}).then(doc => {
agent.add(`Ok ${user}, are you ready?`);
}).catch(err => {
console.log(`Error writing to Firestore: ${err}`);
agent.add(`Failed to write "${user}" to the Firestore database.`);
});
}
//WHEN IT IS CALLING THIS FUNCTION I GOT THE ERROR
function questions_ready(agent) {
if(currentIndex === 0){
list_index=[];
list_answers=[];
list_questions=[];
list_timestamps=[];
list_index.push(currentIndex);
currentQuestion = status_questions[currentIndex++].question;
}
else if(currentIndex >=1 && currentIndex <= 10){
list_index.push(currentIndex);
currentQuestion = status_questions[currentIndex++].question;
flag = 1;
const answParam = agent.parameters.answers;
const ans = answParam;
list_answers.push(ans);
}
else {
currentIndex = 0;
currentQuestion = finish;
flag=0;
const answParam = agent.parameters.answers;
const ans = answParam;
list_answers.push(ans);
agent.add(currentQuestion);
return db.runTransaction(t => {
t.update(dialogflowAgentRef, {time: list_timestamps});
t.update(dialogflowAgentRef, {index: list_index});
t.update(dialogflowAgentRef, {question: list_questions});
t.update(dialogflowAgentRef, {answer: list_answers});
return Promise.resolve('Write complete');
});
}
const ques = currentQuestion;
list_questions.push(ques);
var timestamp = Date.now();
list_timestamps.push(timestamp);
let ctx = {'name': 'projects/prueba-firebase-v1/agent/sessions/545ec712-8f69-6999-a50b-4127d38bce82/contexts/questions_ready', 'lifespan': 14,
'parameters': {'timestamp': list_timestamps, 'list_index': list_index, 'list_questions': list_questions, 'list_answers': list_answers}}; //, 'list_index': list_index
agent.setContext(ctx);
agent.add(currentQuestion);
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set(enter code here'Manual', manual);
intentMap.set('User_Name', user_name);
intentMap.set('Questions_Ready', questions_ready);
agent.handleRequest(intentMap);
});
The messages I obtain on Actions on Google are:
-On Simulator Display:
Prueba application isn't responding right now. Try again soon.
-Errors:
MalformedResponse
'final_response' must be set.
-Debug:
{
"response": "Prueba application isn't responding right now. Try again soon.",
"expectUserResponse": false,
"conversationToken": "GidzaW11bG...",
"audioResponse": "//NExAASWK...",
"debugInfo": {
"assistantToAgentDebug": {
"curlCommand": "curl -v 'https://api.api.ai/api/integrations/google?token=6094e3dbd9e242679d0dcc603568b120&versionId=3' -H 'Content-Type: application/json;charset=UTF-8' -H 'Google-Actions-API-Version: 2' -H 'Authorization: eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxZTg2OWU3YmY0MGRkYzNkM2RlMDgwNDI1OThiYTgzNTA5NzBmMGEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhdWQiOiJjeWJlcm9wcy1maXJlYmFzZS12MSIsIm5iZiI6MTU0MjcxMTY5MCwiaWF0IjoxNTQyNzExOTkwLCJleHAiOjE1NDI3MTIxMTAsImp0aSI6Ijc5NWYyZTViZGRjNzk5ZDAxMTY2MDZhZmEyZjJiMDRlYjU3MDk4ZGQifQ.XNBl3DcL2Zhw9bXHvPG52U21ATIb52snsQ5YF9T57cf9HrEeau6XTPfbtALdkiTEqhRfcihQTwLu7wAMdvmqTeeDaRW3F8C2xDCitT2bjPryeDJ3eyoJvI2cTy5Vhf1oN3WwsHdlM0D59JYyNtTH1NE-B60bnLCPQNe7Mv23aUnipdo-LsAytF_d9Bpz93SR_WZITqP6-FpqHSSuUHL3qi8idqGNQrtFF6RQ5-AGKkLkqE-V_Sa2iLmpqDsi4fP3RYW0bajuSFrn74JvrziQKQR4ZaFc4ITjPtJlhboCTgJusOqpFvOYV_-LF5FqgswaiMqUtaX8YBW_EKLLMLoS2A' -A 'Mozilla/5.0 (compatible; Google-Cloud-Functions/2.1; +http://www.google.com/bot.html)' -X POST -d '{\"user\":{\"userId\":\"ABwppHFD2VxLtzrmnMXp4XsxyE13Xc7mxOhaf7cbxMUQg7OEe_I1qRVlcDck8Rl-bESCZBPi3cHvESEbHYfvecHr59o\",\"locale\":\"en-US\",\"lastSeen\":\"2018-11-20T10:51:47Z\"},\"conversation\":{\"conversationId\":\"ABwppHH7q6m-4okbTf3aKCU-dgpEAoOmeCLle2AZjocfLI6i8BS1Lhqcx4InD3QBKboVr4yyTPcaOhAOMgQsaHIgHRU\",\"type\":\"ACTIVE\",\"conversationToken\":\"[]\"},\"inputs\":[{\"intent\":\"actions.intent.TEXT\",\"rawInputs\":[{\"inputType\":\"KEYBOARD\",\"query\":\"yes\"}],\"arguments\":[{\"name\":\"text\",\"rawText\":\"yes\",\"textValue\":\"yes\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"},{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"}]},\"isInSandbox\":true,\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"}]}],\"requestType\":\"SIMULATOR\"}'",
"assistantToAgentJson": "{\"user\":{\"userId\":\"ABwppHFD2VxLtzrmnMXp4XsxyE13Xc7mxOhaf7cbxMUQg7OEe_I1qRVlcDck8Rl-bESCZBPi3cHvESEbHYfvecHr59o\",\"locale\":\"en-US\",\"lastSeen\":\"2018-11-20T10:51:47Z\"},\"conversation\":{\"conversationId\":\"ABwppHH7q6m-4okbTf3aKCU-dgpEAoOmeCLle2AZjocfLI6i8BS1Lhqcx4InD3QBKboVr4yyTPcaOhAOMgQsaHIgHRU\",\"type\":\"ACTIVE\",\"conversationToken\":\"[]\"},\"inputs\":[{\"intent\":\"actions.intent.TEXT\",\"rawInputs\":[{\"inputType\":\"KEYBOARD\",\"query\":\"yes\"}],\"arguments\":[{\"name\":\"text\",\"rawText\":\"yes\",\"textValue\":\"yes\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"},{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"}]},\"isInSandbox\":true,\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"}]}],\"requestType\":\"SIMULATOR\"}"
},
"agentToAssistantDebug": {
"agentToAssistantJson": "{\n \"responseMetadata\": {\n \"status\": {\n \"code\": 10,\n \"message\": \"Failed to parse Dialogflow response into AppResponse because of empty speech response\",\n \"details\": [{\n \"#type\": \"type.googleapis.com/google.protobuf.Value\",\n \"value\": \"{\\\"id\\\":\\\"95a80a4d-4a98-461f-b183-88a358d87ebc\\\",\\\"timestamp\\\":\\\"2018-11-20T11:06:30.765Z\\\",\\\"lang\\\":\\\"en-us\\\",\\\"result\\\":{},\\\"alternateResult\\\":{},\\\"status\\\":{\\\"code\\\":206,\\\"errorType\\\":\\\"partial_content\\\",\\\"errorDetails\\\":\\\"Webhook call failed. Error: Webhook response was empty.\\\"},\\\"sessionId\\\":\\\"ABwppHH7q6m-4okbTf3aKCU-dgpEAoOmeCLle2AZjocfLI6i8BS1Lhqcx4InD3QBKboVr4yyTPcaOhAOMgQsaHIgHRU\\\"}\"\n }]\n }\n }\n}"
},
"sharedDebugInfoList": [
{
"name": "ResponseValidation",
"debugInfo": "",
"subDebugEntryList": [
{
"name": "MalformedResponse",
"debugInfo": "'final_response' must be set.",
"subDebugEntryList": []
}
]
}
]
},
"visualResponse": {
"visualElementsList": [
{
"displayText": {
"content": "Prueba application isn't responding right now. Try again soon."
}
}
],
"suggestionsList": [],
"agentLogoUrl": ""
},
"clientError": 0,
"is3pResponse": 1,
"clientOperationList": []
}
Here there are the images I have on my Questions_Ready Intent and the Dialog established by Dialogflow where all work perfectly. The problem is when I try to test the skill on Actions on Google
Questions_Ready Intent (I)
Questions_Ready Intent (II)
Answers Entities
Dialogflow dialog (I)
Dialogflow dialog (II)
Result obtained on Actions on Google
On the Debug json:
debugInfo.agentToAssistantDebug.agentToAssistantJson: "Failed to parse Dialogflow response into AppResponse because of empty speech response"
We can see that Dialogflow is not returning any response that Actions on Google understands. Example response for AoG
So, my thoughts are that the webhook is not returning the correct format for Actions on Google, which is different from Dialogflow.
Here you have a repository for examples of the responses for Actions on Google: https://github.com/dialogflow/fulfillment-webhook-json/tree/master/responses/v2/ActionsOnGoogle/RichResponses
I'm sorry I can't help further with the node.js code, I implemented the webhooks in another language, but the json response is the same for all of them.
From the code sample you provided, it seems like you've been using Dialogflow's Fulfillment library and not the Actions on Google client library, you can test out various samples and take a look at the dependencies up on Github, take this sample for example. The index.js file will have the following import declared when using the Actions on Google Node.js library:
const {dialogflow} = require('actions-on-google');
I am trying to invoke Lambda through cloudfront viewer request . Here is my Lambda code
'use strict';
const AWS = require("aws-sdk");
const docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = (event, context, callback) => {
/* Get request */
const request = event.Records[0].cf.request;
const requestbody = Buffer.from(request.body.data, 'base64').toString();
const data = JSON.parse(requestbody);
const Id = data.Name;
console.log(Id);
/* Generate body for response */
const body =
'<html>\n'
+ '<head><title>Hello From Lambda#Edge</title></head>\n'
+ '<body>\n'
+ '<h1>You clicked more than 10 Times </h1>\n'
+ '</body>\n'
+ '</html>';
var params = {
TableName: "Test",
ProjectionExpression: "#V,#N",
KeyConditionExpression: "#N = :v1",
ExpressionAttributeNames: {
"#N" : "Name",
"#V" : "Value"
},
ExpressionAttributeValues: {
":v1": Id
}
};
var querydb = docClient.query(params).promise();
querydb.then(function(data) {
console.log(data.Items[0].Value);
if(data.Items[0].Value >= 11){
const response = {
status: '200',
body: body,
};
callback(null, response);
}else {
callback(null,request);
}
}).catch(function(err) {
console.log(err);
});
};
When i triggered the same lambda through console it is giving correct response. But when i deployed through Cloudfront it is giving 503 Error. But i had tried the same code withcode Dynamodb Client it worked perfectly fine. Here is the working one
'use strict';
const AWS = require("aws-sdk");
const docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = (event, context, callback) => {
/* Get request */
const request = event.Records[0].cf.request;
const requestbody = Buffer.from(request.body.data, 'base64').toString();
const data = JSON.parse(requestbody);
/* Generate body for response */
const body =
'<html>\n'
+ '<head><title>Hello From Lambda#Edge</title></head>\n'
+ '<body>\n'
+ '<h1>You clicked more than 10 Times </h1>\n'
+ '</body>\n'
+ '</html>';
if(data.Value >= 10){
const response = {
status: '200',
body: body,
};
callback(null, response);
}
else {
callback(null, request);
}
};
I had given full dynamodb permissions to the lambda#edge.
Any help is appreciated
Thanks
Where have you specified region for DyanamoDB?
It is possible that Lambda#Edge is executing in a region where your DDB table is missing.
Have a look at AWS doc on region's order of precedence. You can also look at this L#E workshop code and documentation for more details on calling DDB.
On a side note: A viewer facing Lambda function, making a call to a cross region dynamodb table will have negative effects on your latency. Not sure about your use case but see if it is possible to move this call to an origin facing event or make async call to ddb.
I have a Cloud Firestore database that stores the number of inhabitants of all cities in England in 2017.
Then I have a Dialogflow. Whenever I tell the name of a city to Dialogflow, I want it to get the number of inhabitants in that city from Firestore and return it to Dialogflow.
Specifically, I want to implement this via the Inline Editor.
Question: What lines of code do I need to add to the code below in order to make this happen?
So here is the code that I write in the Inline Editor in Dialogflow > Fulfillment > index.js:
'use strict';
const functions = require('firebase-functions');
const firebaseAdmin = require('firebase-admin');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const App = require('actions-on-google').DialogflowApp;
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function welcome(agent) {
agent.add(`Hello and welcome!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
agent.handleRequest(intentMap);
});
Here is some sample code showing how to connect Firebase's Firestore database to Dialogflow fulfillment hosting on Firebase functions:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {WebhookClient} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function writeToDb (agent) {
// Get parameter from Dialogflow with the string to add to the database
const databaseEntry = agent.parameters.databaseEntry;
// Get the database collection 'dialogflow' and document 'agent' and store
// the document {entry: "<value of database entry>"} in the 'agent' document
const dialogflowAgentRef = db.collection('dialogflow').doc('agent');
return db.runTransaction(t => {
t.set(dialogflowAgentRef, {entry: databaseEntry});
return Promise.resolve('Write complete');
}).then(doc => {
agent.add(`Wrote "${databaseEntry}" to the Firestore database.`);
}).catch(err => {
console.log(`Error writing to Firestore: ${err}`);
agent.add(`Failed to write "${databaseEntry}" to the Firestore database.`);
});
}
function readFromDb (agent) {
// Get the database collection 'dialogflow' and document 'agent'
const dialogflowAgentDoc = db.collection('dialogflow').doc('agent');
// Get the value of 'entry' in the document and send it to the user
return dialogflowAgentDoc.get()
.then(doc => {
if (!doc.exists) {
agent.add('No data found in the database!');
} else {
agent.add(doc.data().entry);
}
return Promise.resolve('Read complete');
}).catch(() => {
agent.add('Error reading entry from the Firestore database.');
agent.add('Please add a entry to the database first by saying, "Write <your phrase> to the database"');
});
}
// Map from Dialogflow intent names to functions to be run when the intent is matched
let intentMap = new Map();
intentMap.set('ReadFromFirestore', readFromDb);
intentMap.set('WriteToFirestore', writeToDb);
agent.handleRequest(intentMap);
});
This came from Dialogflow's Firestore sample located here: https://github.com/dialogflow/fulfillment-firestore-nodejs
I am trying to make my file management a bit easier and make each intent in its own file. How do I include that so my index.js uses that intent. Heres and example of what I have tried.
var alexa = require('alexa-app');
var app = new alexa.app();
var GetLunchSuggestions = require('./Intents/GetLunchSuggestions');
app.launch(function(request, response) {
response.say('Welcome I am built to handle your lunch requests');
response.shouldEndSession(false);
});
app.use(GetLunchSuggestions);
// Connect to lambda
exports.handler = app.lambda();
if (process.argv.length === 3 && process.argv[2] === 'schema') {
console.log(app.schema());
console.log(app.utterances());
}
I want use lunch suggestions intent in this file. How do you do this?
In your ./Intents/GetLunchSuggestions.js
module.exports = {
'AMAZON.HelpIntent': function () {
const speechOutput = 'I'm a handler from different file.';
this.response.speak(speechOutput).shouldEndSession(isLaunched);
this.emit(':responseReady');
}
}
then in your index.js
const GetLaunchSuggestions = require('./Intents/GetLunchSuggestions');
exports.handler = function (event, context, callback) {
const alexa = Alexa.handler(event, context, callback);
alexa.appId = APP_ID; // APP_ID is your skill id which can be found in the Amazon developer console where you create the skill.
alexa.registerHandlers(
handlers, // this where some of your handlers being defined. You can remove this if it was not define it your code.
GetLaunchSuggestions // this is your handler from different file.
);
alexa.execute();
};