Dialogflow Detect Intent Fulfillment - node.js

Hi i have created a dialogflow nodejs backend which detects intents using the client library for nodejs.
const sessionPath = this.sessionClient.sessionPath(this.configService.get('dialogFlowProjectId'), sessionId);
const request = {
session: sessionPath,
queryInput: {
text: {
text: query,
languageCode: "en-US"
}
}
};
// Send request and log result
Logger.log(request);
const responses = await this.sessionClient.detectIntent(request);
this works fine but I also want to trigger a fulfillment for certain intents.
I have setup a webhook url - this works fine when you use the chat in the dialogflow console. But when I use the method that I have created to send the request to dialogflow the webhook doesn't get called and goes to fallback intent. I'm calling the same intent through the dialogflow console chat and through my own API.
How can I trigger the webhook call when I use the client library API?

First, remember that you don't "call an Intent", either through the test console or through the API. What you can do is send query text that should be matched by an Intent.
If you have set the Intent so that it should call the Fulfillment webhook, then this should happen no matter the source, as long as the Intent itself is triggered.
That the Fallback Intent is getting triggered instead suggests that something about the query text isn't matching the Intent you think it should be matching. I would check to make sure the text you're sending in query should match the training phrases for the Intent in question, that you have no Input Contexts, and that your agent is set for the "en-US" language code.

For routing multiple intents through a webhook you're going to want to have a handler.js file with an express router in it...
const def = require('./intents/default')
const compression = require('compression')
const serverless = require('serverless-http')
const bodyParser = require('body-parser')
const { WebhookClient } = require('dialogflow-fulfillment')
const express = require('express')
const app = express()
// register middleware
app.use(bodyParser.json({ strict: false }))
app.use(function (err, req, res, next) {
res.status(500)
res.send(err)
})
app.use(compression())
app.post('/', async function (req, res) {
// Instantiate agent
const agent = new WebhookClient({ request: req, response: res })
const intentMap = new Map()
intentMap.set('Default Welcome Intent', def.defaultWelcome)
await agent.handleRequest(intentMap)
})
module.exports.server = serverless(app)
As you can see, this handler.js is using serverless-http and express.
Your intent.js function could look something like this ...
module.exports.defaultWelcome = async function DefaultWelcome (agent) {
const responses = [
'Hi! How are you doing?',
'Hello! How can I help you?',
'Good day! What can I do for you today?',
'Hello, welcoming to the Dining Bot. Would you like to know what\'s being served? Example: \'What\'s for lunch at cronkhite?\'',
'Greetings from Dining Bot, what can I do for you?(Hint, you can ask things like: \'What\'s for breakfast at Annenberg etc..\' or \'What time is breakfast at Anennberg?\')',
'greetings',
'hey',
'long time no see',
'hello',
"lovely day isn't it"
]
const index = Math.floor((Math.random() * responses.length) + 1)
console.log(responses[index].text)
agent.add(responses[index].text)
}
This gives you the bare bones for routing multiple request through your webhook. Hope this helps.

Related

Sending Proactive Messages from Azure functions to botservice - node

I am using botframework v4, but coming over from v3, I have not found any documentation that is similar to the code I use below but for v4, regarding sending proactive messages from Azure Function App
Below is the code I previously used but am having trouble adapting:
var builder = require('botbuilder');
// setup bot credentials
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
module.exports = function (context, req) {
if (req.body) {
var savedAddress = req.body.channelAddress;
var inMemoryStorage = new builder.MemoryBotStorage();
var bot = new builder.UniversalBot(connector).set('storage', inMemoryStorage);
sendProactiveMessage(savedAddress, bot)
}
};
function sendProactiveMessage(address, bot) {
var msg = new builder.Message().address(address);
msg.textLocale('en-US');
var img = {
attachments: [{
contentType: "image/jpg",
contentUrl: latestUrl,
}]
};
msg.addAttachment(img.attachments[0]);
msg.text('hello');
bot.send(msg);
}
This works fine with v3 but not v4.
If possible I would also like to find a way to log a user out:
await botAdapter.signOutUser(innerDc.context, this.connectionName);
This is how I do it in the bot itself, but doing so from Azure Functions again is proving difficult.
Any help would be appreciated.
Great that you are making the move from v3 to v4! Have you had a look at Send proactive notifications to users? This example is pretty straight forward and can be used within an Azure function.
First you retrieve the Conversation Reference in your bot by calling TurnContext.getConversationReference(context.activity);. This is the reference you could use in your proactive function to open the conversation. In your case you provide that via the request body to a proactive function, so I will do the same in my example.
My proactive endpoint example is written in Typescript, however it works the same way in plain Javascript. Create a HTTP trigger in Azure Functions and use the following code. I have added comments inline for clarity.
const { BotFrameworkAdapter } = require('botbuilder');
// Create adapter.
// If you need to share this adapter in multiple functions, you could
// instantiate it somewhere else and import it in every function.
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
module.exports = async function (context, req) {
// Validate if request has a body
if (!req.body) {
context.res = {
status: 400,
body: "Please pass a conversation reference in the request body"
};
return;
}
// Retrieve conversation reference from POST body
const conversationReference = req.body;
// Open the conversation and retrieve a TurnContext
await adapter.continueConversation(conversationReference, async turnContext => {
// Send a text activity
// Depending on the channel, you might need to use https://aka.ms/BotTrustServiceUrl
await turnContext.sendActivity('Proactive hello');
});
context.res = {
body: 'Message sent!'
};
};
In the end you could make a request to this Azure Function, where you pass the Conversation Reference as body of the type application/json.
Extending this example with features like signOutUser is simple. You can call all functions within the continueConversation function, just as in a normal bot. You can even receive the adapter object there if you wish.
await adapter.continueConversation(conversationReference, async turnContext => {
// Sign user out
turnContext.adapter.signOutUser(turnContext, 'connection-name');
});

how to run the dialogflow node js?

i am still confused about the documentation on Dialogflow nodejs, can I get clear for this?
I have read the documentation this -> https://cloud.google.com/dialogflow/docs/reference/rest/v2/projects.agent.sessions/detectIntent
how to test my code and see the result of my query I input then ?
should I do POST via Axios inside this functions
async function runSample(projectId = 'your-project-id') {
// A unique identifier for the given session
const sessionId = uuid.v4();
// Create a new session
const sessionClient = new dialogflow.SessionsClient();
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
// The query to send to the dialogflow agent
text: 'hello',
// The language used by the client (en-US)
languageCode: 'en-US',
},
},
};
// Send request and log result
const responses = await sessionClient.detectIntent(request);
console.log('Detected intent');
const result = responses[0].queryResult;
console.log(` Query: ${result.queryText}`);
console.log(` Response: ${result.fulfillmentText}`);
if (result.intent) {
console.log(` Intent: ${result.intent.displayName}`);
} else {
console.log(` No intent matched.`);
}
}
i still not get clear for this, so do we need use HTTP request also in local to check this?
I already follow the Dialogflow nodejs example, but what next?
it said on google we must POST to
POST https://dialogflow.googleapis.com/v2/{session=projects//agent/sessions/}:detectIntent
nd the req body is on the request variable inside that functions,
but I still not clear on dialogflow nodejs for the next step to run that method
You can run this directly using the express app. Just call this function from your express app route or you can call the given function in the file and use node <filename.js> to run the code. Make sure you add your google cloud credentials in the path name using process.env.GOOGLE_APPLICATION_CREDENTIALS = "<your_json_file_path>";
const sessionClient = new dialogflow.SessionsClient(
// below can be used to set the variable in code itself, if below is not working you can run following in the terminal
//export GOOGLE_APPLICATION_CREDENTIALS="/<path of the json file"
{
keyFilename: "/<path of the json file"
}
);

How to manage a customize for returning users in dialogflow fullfilment?

Following the design tips on action-on-google developer platform, I'm trying to give a custom welcome response for users who come back in a Google Assistant Voice app.
https://developers.google.com/actions/design/tips
To proceed, i started by switch My Default Welcome intent in Dialogflow in the Fullfilment mode. Then deploy my code on firebase. It's work well when i ask for a name permission, but when I try to input the 'AppRequest.User lastSeen', my app shut down?
Maybe you know what i missed in my code?
'use strict';
const {
dialogflow,
Permission,
} = require('actions-on-google');
const functions = require('firebase-functions');
const app = dialogflow({debug: true});
app.intent('Default Welcome Intent', (conv) => {
const welcome = (conv) => {
if (conv.user.last.seen) {
conv.ask(`Hey you're back...`);
} else {
conv.ask('Welcome to World Cities Trivia!...');
}
}});
// Set the DialogflowApp object to handle the HTTPS POST request.
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
It looks like the code in your welcome Intent Handler doesn't actually do anything.
You setup the handler
app.intent('Default Welcome Intent', (conv) => {
...
});
but inside your handler you create another function
const welcome = (conv) => {
if (conv.user.last.seen) {
conv.ask(`Hey you're back...`);
} else {
conv.ask('Welcome to World Cities Trivia!...');
}
}
that never gets called. It isn't clear why you have this inner function, since it isn't necessary. You just need the body of that function in the outer one. So it can look something like this, perhaps:
app.intent('Default Welcome Intent', (conv) => {
if (conv.user.last.seen) {
conv.ask(`Hey you're back...`);
} else {
conv.ask('Welcome to World Cities Trivia!...');
}
});
You should read it from the conv object, it represents a turn in the conversation and exposes the attributes of the individual requests: conv.user.last.

Possibility to send context data to dialogflow without webhook being called from Dialogflow, but from server itself

Currently I have a webhook which is being called from Dialogflow (fulfillment) and checks which intent is currently applicable. (I do everything via JSON) once I know which Intent, I send back (via the webhook) a JSON response. I was wondering if it is possible to initiate a JSON object and send it to Dialogflow. (I want to send context without letting the user type first and Dialogflow receiving a fallback) (NodeJS Server -> Dialogflow) Currently the only way I can do it is (Dialogflow -> NodeJS Server (JSON object with contex) -> Dialogflow)
CURRENT:
var dialog_flow = {
// Create context using Google dialogflow
createContext: async function(projectId, sessionId, contextId, parameters, lifespanCount){
console.log("params to be sent: ", parameters);
//Instantiates client to create the actual context before sending
const contextsClient = new dialogflow.ContextsClient({
keyFilename: './dialogflow_credentials.json'
});
const sessionPath = contextsClient.sessionPath(projectId, sessionId);
const contextPath = contextsClient.contextPath(
projectId,
sessionId,
contextId
);
const createContextRequest = {
parent: sessionPath,
context: {
name: contextPath,
lifespanCount: lifespanCount,
parameters: parameters
},
};
const response = await contextsClient.createContext(createContextRequest);
console.log(`Created ${response[0].name}`);
}
};
PARAMETERS:
let testobj = {
firstname: 'test name',
lastname: 'itworks!!',
dd: 'fgfgf',
ff: 'fgfg'
}
You can call directly Dialogflow from Node.js, without the need of receiving a webhook. For that you will need to use the dialogflow package.
const sessionClient = new dialogflow.SessionsClient();
const session = sessionClient.sessionPath(projectId, sessionId);
const request = {
session,
queryInput: {
text: {
text: 'Some text'
}
},
queryParams: {
contexts: [context] // here you send the data
}
};
sessionClient.detectIntent(request)
.then(console.log)
.catch(console.error);
In order to send a context, with data, you will need to use dialogflow.ContextsClient
I give a detailed explanation about that in these other questions:
set input or output context dialogflow nodejs v2
Dialogflow pass parameters through NodeJS
And in order to authenticate the client you can check:
Dialogflow easy way for authorization
UPDATE: 2 weeks ago, Dialogflow, removed the structjson.js sample from the repository, but in favor of an npm package to handle the encoding/decoding of the protobuf struct: pb-util

Unable to handle multiple fulfillment responses

I am using the dialogflow enterprise edition bot,the issue is about receiving responses from rest api(node.js sdk).Recently we have shifted from dialogflow standard edition to Enterprise edition. We have referred and used the following code in this link
https://www.npmjs.com/package/dialogflow
We have created our own cloud function using rest Api which takes the user answer as the request and send the fulfillment text(questions in that particular intent) as the response to the user.When multiple request calls happen the the cloud function is not showing proper response which means
When user(Android/IOS) "A" started answering the bot then the cloud function triggering gets started and it send the questions as the response to the user but when the multiple users started answering the bot, due to multiple calls for the cloud function, the questions which are displayed to the one user are not going to be displayed to the other user say "B". Please help us in handling the multiple calls for node.js sdk
https://i.stack.imgur.com/BVAci.png
This is the view of an intent in dialogflow.
https://i.stack.imgur.com/aRAr6.png ,
https://i.stack.imgur.com/oMp0d.png
https://i.stack.imgur.com/T7Jo7.png
https://i.stack.imgur.com/7U3Rb.png
https://i.stack.imgur.com/EwWSo.png
Above five screenshots represent the first time when the user triggers
the cloud function, and the 1st question is displayed. When we answer the
first question and submit the answer, next question is not displayed.(I am
getting the empty response).
https://i.stack.imgur.com/rLI2I.png ,
https://i.stack.imgur.com/T5wZL.png
These screenshots represent the empty response after first question is answered.
const functions = require('firebase-functions');
const dialogflow = require('dialogflow');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
admin.initializeApp();
var db = admin.firestore();
const {WebhookClient} = require('dialogflow-fulfillment');
exports.fulfillmenttext = functions.https.onRequest((req,res) =>{
runSample();
async function runSample() {
const projectId = 'bodha-192606';
const sessionId = uuid.v4();
const answer = req.body.Text;
console.log("Text said by the user",answer);
const languageCode = 'en-US';
const credentials = {
client_email: 'xxxx ',
Private_key:'xxxx ',
};
// Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
const sessionClient = new dialogflow.SessionsClient({
projectId,
credentials,
});
// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
text: {
text: answer,
languageCode,
},
},
};
const responses = await sessionClient.detectIntent(request);
console.log('Detected intent');
const result = responses[0].queryResult;
let action = result.action;
console.log("action is"+action);
console.log(` Query: ${result.queryText}`);
console.log(` Response: ${result.fulfillmentText}`);
if (result.intent) {
console.log(` Intent: ${result.intent.displayName}`);
res.status(200).send({"question":result.fulfillmentText});
} else {
console.log(` No intent matched.`);
res.status(400).send("No Intent matched");
}
}
});
It looks like you are using the same session ID for all of your clients. I'm not certain, but I can certainly understand that if Dialogflow gets two requests for the same session at the same time, that it can mix up the replies.
You should be generating a new session for each of your clients - possibly by putting something into an HTTP session (aha!) cookie when they first connect and continuing to use that during the session.

Resources