Usage of Bing SpellChecker into LUIS.ai - azure

I try to improve LUIS intents prediction accuracy. Because there is an issue when missing one letter leads LUIS to a wrong intent.
I use the tutorial, but still there is no promised key like alteredQuery in the JSON returned form LUIS.
EXPECTATION:
"query": "bouk me a fliht to kayro",
"prediction": {
"alteredQuery": "book me a flight to cairo",
"topIntent": "book a flight",
"intents": {
"book a flight": {
"score": 0.9480589
}
"None": {
"score": 0.0332136229
}
},
"entities": {}
}
}
REALITY:
"query": "bouk me a fliht to kayro",
"prediction": {
"topIntent": "book a flight",
"intents": {
"book a flight": {
"score": 0.9480589
}
"None": {
"score": 0.0332136229
}
},
"entities": {}
}
}
This is how I use it:
const luisConfig: LuisApplication = {
applicationId: [APP_ID_GOES_HERE],
endpointKey: [KEY_GOES_HERE],
endpoint: [ENDPOINT_GOES_HERE],
};
const recognizerOptions: LuisRecognizerOptionsV2 = {
apiVersion: 'v2',
bingSpellCheckSubscriptionKey: [KEY_GOES_HERE];
includeAllIntents: true,
log: true,
spellCheck: true,
log: true,
includeInstanceData: true
};
const luisRecognizer = new LuisRecognizer(luisConfig, recognizerOptions, true);

I have the same issue... if I call the API directly:
/luis/prediction/v3.0/apps/<xxxxx>/slots/production/predict?verbose=true&show-all-intents=false&log=true&subscription-key=<xxxxx>&mkt-bing-spell-check-key=<xxxxx>&query=gretings
The response have not the object "alteredQuery".
Im also try with more parameters with the same result:
https://<xxxxx>.cognitiveservices.azure.com/luis/prediction/v3.0/apps/<xxxxx>/slots/production/predict?verbose=true&show-all-intents=false&log=true&subscription-key=<xxxxx>&spellCheck=true&mkt-bing-spell-check-key=<xxxxx>&bing-spell-check-subscription-key=<xxxxx>&query=gretings
If I call directly the BING API with the Ocp-Apim-Subscription-Key header, they response correctly:
https://api.bing.microsoft.com/v7.0/spellcheck?text=gretings

Related

How to get Session Entities to work as part of Dialogflow detect intent

I have an Entity that is supposed to be updated on a per-session basis with user-specific information. This had worked when I was using Dialogflow v1, and I thought it had worked with v2, but I'm now having significant problems with it.
I believe I am setting the Session Entity information correctly, but for the Intent that uses it, it only matches when a value from the Developer Entity is used.
How can I get it to use the Session Entity? Am I doing something wrong when updating it? Am I using the wrong Session ID? Is there a way I can better verify or test that I'm using the correct ID or that I'm updating the Entity correctly? Is this just a bug?
Documentation of everything follows.
The project is configured to use v2 and to allow for beta features, although I've tried this without the beta features as well.
There are only three Intents. A Fallback Intent to capture failures, a Welcome Intent that gets the welcome event, and the "entry" intent that is supposed to capture the entry code which should match the "code" Entity. All of them use a webhook for fulfillment.
The Fallback Intent
The Welcome Intent
The "entry" Intent
As shown in the "entry" Intent, it uses the "code" Entity, which is the only Developer Entity in the system
The code has most of the Dialogflow specific work in a separate module that uses the "dialogflow" module from npm to set the Session Entity. (Note this is different than the dialogflow-fulfillment module, which is used to handle fulfillment. I'm using the multivocal library for fulfillment, but that shouldn't matter.) (It also uses firebase functions to run on, but I don't think any of these are relevant.)
From the package.json:
"dependencies": {
"dialogflow": "^0.9.0",
"firebase-admin": "~7.0.0",
"firebase-functions": "^2.2.0",
"multivocal": "^0.11.1"
},
This is imported as dialogflow, specifying the API version to use:
const dialogflow = require('dialogflow').v2beta1;
The functions I show below call envToConfig(env) which takes the environment (a multivocal concept that just stores relevant information, including the Dialogflow parent and certificate information) and returns the configuration that needs to be passed to dialogflow.SessionEntityTypesClient( config ). Given no errors are thrown in further calls, it appears to work correctly.
The makeEntityType( name, entityMap ) function takes a map of values to be used for the entities in a SessionEntityType and returns an object that will be used to build a full SessionEntityType. The name provided here is the display name.
function makeEntityType( name, entityMap ){
let ret = {
displayName: name,
entities: []
};
Object.keys( entityMap ).map( key => {
let val = entityMap[key];
let entity = {
value: key,
synonyms: [key, ...val]
};
ret.entities.push( entity );
});
return ret;
}
exports.makeEntityType = makeEntityType;
The result from this is passed to setSessionEntity( env, entityType ) along with the multivocal environment, which contains some information we use in the session. It makes sure the name and entityOverrideMode are set correctly in the entityType and then tries to create it. I've tried using PATCH as well, and it behaves the same way. It also does a bunch of logging, that I'll show later when it runs to prove it actually works.
function setSessionEntity( env, entityType ){
const config = envToConfig( env );
const client = new dialogflow.SessionEntityTypesClient( config );
let parent = env.dialogflow.parent;
if( entityType.displayName && !entityType.name ){
entityType.name = `${parent}/entityTypes/${entityType.displayName}`;
}
if( !entityType.entityOverrideMode ){
entityType.entityOverrideMode = 'ENTITY_OVERRIDE_MODE_OVERRIDE';
}
console.log('setSessionEntity parent',parent);
const request = {
parent: parent,
sessionEntityType: entityType
};
console.log('setSessionEntity request',JSON.stringify(request,null,1));
return client.createSessionEntityType( request )
.then( create => {
console.log('setSessionEntity created',JSON.stringify(create,null,1));
return Promise.resolve( env );
})
.catch( err => {
console.error('setSessionEntity problem creating',err);
return Promise.resolve( env );
})
}
exports.setSessionEntity = setSessionEntity;
For debugging, I also have a function that lists the session entities:
function listSessionEntities( env ){
let parent = env.dialogflow && env.dialogflow.parent;
console.log('listSessionEntities parent', parent);
if( !parent ){
return Promise.resolve( env );
}
const config = envToConfig( env );
const client = new dialogflow.SessionEntityTypesClient( config );
const request = {
parent: parent
};
return client.listSessionEntityTypes(request)
.then( result => {
console.log('listSessionEntities', JSON.stringify(result,null,1));
})
.catch( err => {
console.log('listSessionEntities err', err);
})
.then( () => Promise.resolve( env ) );
}
exports.listSessionEntities = listSessionEntities;
The code that calls this imports it as Dialogflow:
const Dialogflow = require('./dialogflow');
As part of all the webhook calls, the listSessionEntities() function is called before any specific handler is:
function debugSessionEntities( env ){
return Dialogflow.listSessionEntities( env );
}
When the Welcome Intent is triggered, it sets the "code" Session Entity to have two new types that should override the type that was defined in the "code" Developer Entity:
function handleWelcome( env ){
const entityType = Dialogflow.makeEntityType('code',{
'alpha': [],
'bravo': []
});
return Dialogflow.setSessionEntity( env, entityType )
.then( env => Multivocal.handleDefault( env ) );
}
When I run this through the simulator, it doesn't pick up on the Session Entity Types that are set, but do still respond to the Developer Entity Type. (Using a real device works the same way.)
In the simulator, this is what it reports in the Request tab for the Welcome Intent:
{
"responseId": "55a9eb06-ce05-48f9-8a56-b993fa512aee",
"queryResult": {
"queryText": "GOOGLE_ASSISTANT_WELCOME",
"action": "multivocal.welcome",
"parameters": {},
"allRequiredParamsPresent": true,
"fulfillmentText": "Hello! How can I help you?",
"fulfillmentMessages": [
{
"text": {
"text": [
"Greetings! How can I assist?"
]
}
}
],
"outputContexts": [
{
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/contexts/google_assistant_welcome"
},
{
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/contexts/actions_capability_screen_output"
},
{
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/contexts/actions_capability_audio_output"
},
{
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/contexts/actions_capability_account_linking"
},
{
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/contexts/google_assistant_input_type_keyboard"
},
{
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/contexts/actions_capability_media_response_audio"
},
{
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/contexts/actions_capability_web_browser"
}
],
"intent": {
"name": "projects/session-test-XXXXX/agent/intents/ca79c951-4d75-4b2b-acd4-7dac2f81856e",
"displayName": "welcome"
},
"intentDetectionConfidence": 1,
"languageCode": "en-us"
},
"originalDetectIntentRequest": {
"source": "google",
"version": "2",
"payload": {
"isInSandbox": true,
"surface": {
"capabilities": [
{
"name": "actions.capability.SCREEN_OUTPUT"
},
{
"name": "actions.capability.ACCOUNT_LINKING"
},
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.MEDIA_RESPONSE_AUDIO"
},
{
"name": "actions.capability.WEB_BROWSER"
}
]
},
"requestType": "SIMULATOR",
"inputs": [
{
"rawInputs": [
{
"query": "Talk to my test app",
"inputType": "KEYBOARD"
}
],
"intent": "actions.intent.MAIN"
}
],
"user": {
"userStorage": "{\"UserId\":\"ABwppHHd40lIZ1o0bRERAKlHNtNcS2qFtz7NbRQnb31AQDFuV41VPFQivXwwpQGtv_5SlsZNp0N3kxalIIXXXXXX\",\"NumVisits\":1}",
"lastSeen": "2019-05-18T19:12:38Z",
"locale": "en-US",
"userId": "ABwppHHd40lIZ1o0bRERAKlHNtNcS2qFtz7NbRQnb31AQDFuV41VPFQivXwwpQGtv_5SlsZNp0N3kxalIIXXXXXX"
},
"conversation": {
"conversationId": "ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX",
"type": "NEW"
},
"availableSurfaces": [
{
"capabilities": [
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.WEB_BROWSER"
},
{
"name": "actions.capability.SCREEN_OUTPUT"
}
]
}
]
}
},
"session": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX"
}
Most notable from that is the session attribute set at the bottom from that. The code uses this as the parent and session values when building the
The request objects for the other two Intents are similar, and all have the same value for session. None of the response objects are notable in any way.
When the Welcome Intent is triggered, the call to listSessionEntities(), unsurprisingly, shows there aren't any yet:
info: listSessionEntities parent projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX
info: listSessionEntities [
[],
null,
null
]
The parent appears to have the correct value from the session however.
When the handler for the Welcome Intent goes and creates the Session Entity, things appear to work ok:
info: setSessionEntity request {
"parent": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX",
"sessionEntityType": {
"displayName": "code",
"entities": [
{
"value": "alpha",
"synonyms": [
"alpha"
]
},
{
"value": "bravo",
"synonyms": [
"bravo"
]
}
],
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/entityTypes/code",
"entityOverrideMode": "ENTITY_OVERRIDE_MODE_OVERRIDE"
}
}
info: setSessionEntity created [
{
"entities": [
{
"synonyms": [
"alpha"
],
"value": "alpha"
},
{
"synonyms": [
"bravo"
],
"value": "bravo"
}
],
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/entityTypes/code",
"entityOverrideMode": "ENTITY_OVERRIDE_MODE_OVERRIDE"
},
null,
null
]
The parent appears to be the same as the session, and the name appears to follow the correct format, including the additional part that has the "/entityTypes/" followed by the display name.
When I try calling it with the code "alpha", which should trigger the "entry" Intent, it instead triggers the Fallback Intent. The call to listSessionEntities() seems to show the "code" Entity with the Entity Types we expect, even tho there was no match for "alpha".
info: listSessionEntities parent projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX
info: listSessionEntities [
[
{
"entities": [
{
"synonyms": [
"alpha"
],
"value": "alpha"
},
{
"synonyms": [
"bravo"
],
"value": "bravo"
}
],
"name": "projects/session-test-XXXXX/agent/sessions/ABwppHFGTpcFtHOOo6ehQfKys9AnH14V5-RhzrNKsea6y6L5zgv4eN-j3IesfuqSsKMc7qRt1CAOhkUYA9XXXXXX/entityTypes/code",
"entityOverrideMode": "ENTITY_OVERRIDE_MODE_OVERRIDE"
}
],
null,
null
]
Again, everything looks correct. When I try it again with "zulu", it shows the same thing for the call to listSessionEntites(), but this time it matches the "entry" Intent, since "zulu" is one of the Entity Types for "code" that is defined as a Developer Entity.
This is where I am stuck. Everything looks correct. It looks like the Session Entity should be set correctly for this session. It looks like it should be using those values. But it never seems to do so.
What is going on? All help would be greatly appreciated. (Did you even read till the end of the question? If so - thank you! I know it's long, but wanted to be as complete as possible.)
This appears to be a bug - I've gotten feedback from other developers that they're seeing the same problem, sometimes on previously working code.
A bug has been opened at https://issuetracker.google.com/issues/133166381 to track the issue. Star it to indicate you have similar problems and to track progress.

How to get rid of "null" node in payload (fulfillment response)?

I'm using dialogflow-fulfillment for fulfillment. I am trying to send a payload using the following function;
function getName(agent) {
let options = {
"options": [
{
"id": "make_reservation",
"text": "Reservations",
"payload": "make_reservation"
}
]
};
let newPayload = new Payload('PLATFORM_UNSPECIFIED', options);
agent.add('added by webhook');
agent.add(newPayload);
}
But I'm getting a null node in the fulfillment response.
{
"payload": {
"null": {
"options": [
{
"id": "make_reservation",
"text": "Reservations",
"payload": "make_reservation"
}
]
}
},
"outputContexts": []
}
In the place of the null node, I need the options node. Any idea how to do that?
Thanks
After doing some more search, I got the answer! Payload takes three parameters.
new Payload(platform, payload, options);
where the default options are {sendAsMessage: false, rawPayload: false}. Since rawPayload is false, payload will be nested on the platform name. In my case platform was PLATFORM_UNSPECIFIED, that's why it was nested on a null node.
So the solution is to set rawPayload to true.
let newPayload = new Payload('PLATFORM_UNSPECIFIED', options, {rawPayload: true});

API.ai Actions on Google - Failed to parse JSON response string with 'INVALID_ARGUMENT' error: ": Cannot find field."

This error is similar to what I asked here, but this time it's with NodeJs client.
I am trying to find directions to a location. As soon as the intent is triggered on my webhook, I am calculating the directions using GoogleMapAPI. But before it can finish and send a response, I receive the error on my Actions Console. I checked total response time and it is less than 2 seconds which is less than 5 seconds timeout by Google.Where I am wrong???
My API.ai Intent
Using express.js with Action-on-Google Node Client
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const intentHandler = require('./intent_handler')
const app = express();
app.use(bodyParser.json());
const ApiAiAssistant = require('actions-on-google').ApiAiAssistant;
// Create functions to handle requests here
....
....
const DIRECTION_INTENT = 'action_direction';
function MyAssistant(req, res) {
const assistant = new ApiAiAssistant({request: req, response: res});
assistant.handleRequest(responseHandler(assistant));
}
function responseHandler (assistant) {
// intent contains the name of the intent you defined in the Actions area of API.AI
let intent = assistant.getIntent();
switch (intent) {
case WELCOME_INTENT:
...
break;
case WELCOME_FALLBACK_PERMISSION_INTENT:
...
break;
case DIRECTION_INTENT:
console.log(">>>>>>>DIRECTION_INTENT<<<<<<<");
intentHandler.directionIntent(assistant);
break;
}
}
app.post('/', function (req, res) {
MyAssistant(req, res);
});
app.listen(8080, function () {
console.log('app listening on port 8080!')
});
Handler Code
'use strict';
const speech = require("./speech_template");
const direction = require("./directionModule");
const intent_handler = {
'welcomeIntent': function (assistant) {
.....
},
'welcomeFallbackPermissionIntent': function (assistant) {
.....
},
'directionIntent':function (assistant) {
console.log('direction intent');
direction.getDirectionWithSavedAddress(function (response) {
assistant.ask(response);
});
}
};
module.exports = intent_handler;
Direction Extraction --- ERROR comes on Action Console before this get finished
'use strict';
const striptags = require('striptags');
const speech = require("./speech_template");
let googleMapsClient = require('#google/maps').createClient({
key: global.GOOGLE_DIRECTION_KEY
});
const directionModule = {
'getDirectionWithSavedAddress': function (eventCallback) {
let myAdd = <From Saved Data>;
if (myAdd === undefined) {
console.log("error......");
}
let destination = <From Saved Data>;
this.getDirectionWithAddress(myAdd, destination, function (dir) {
....
if(SUCCESS){
eventCallback(`<speak> ${steps} </speak>`);
}else{
eventCallback(`<speak> ${speech.ERROR_DIRECTIONS} </speak>`);
}
});
},
'getDirectionWithAddress': function (add1, add2, eventCallback) {
let dir = {};
googleMapsClient.directions({
origin: add1,
destination: add2,
mode: "driving",
departure_time: "now"
}, function (err, response) {
if (!err) {
console.log(response.json.routes[0]);
....
....
....
} else {
console.log(`Error --> ${err.toString()}`);
....
}
eventCallback(dir);
});
}
};
module.exports = directionModule;
UPDATE
I am running the code locally via WebStorm and exposing webhook via port forwarding using ngrok.
Update2
BAD REQUEST 400
{
"originalRequest": {
"source": "google",
"version": "2",
"data": {
"isInSandbox": true,
"surface": {
"capabilities": [
{
"name": "actions.capability.AUDIO_OUTPUT"
}
]
},
"inputs": [
{
"rawInputs": [
{
"query": "get me there",
"inputType": "VOICE"
}
],
"arguments": [
{
"rawText": "get me there",
"textValue": "get me there",
"name": "text"
}
],
"intent": "actions.intent.TEXT"
}
],
"user": {
"locale": "en-US",
"userId": "<uID>"
},
"device": {},
"conversation": {
"conversationId": "<cID>",
"type": "ACTIVE",
"conversationToken": "[\"_actions_on_google_\",\"defaultwelcomeintent-followup\"]"
}
}
},
"id": "<ID>",
"timestamp": "2017-09-12T17:08:10.321Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "get me there",
"speech": "",
"action": "action_direction",
"actionIncomplete": false,
"parameters": {},
"contexts": [
{
"name": "_actions_on_google_",
"parameters": {},
"lifespan": 99
},
{
"name": "google_assistant_input_type_voice",
"parameters": {},
"lifespan": 0
},
{
"name": "actions_capability_audio_output",
"parameters": {},
"lifespan": 0
},
{
"name": "defaultwelcomeintent-followup",
"parameters": {},
"lifespan": 4
}
],
"metadata": {
"intentId": "<iID>",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "false",
"nluResponseTime": 15,
"intentName": "DirectionIntent"
},
"fulfillment": {
"speech": "",
"messages": [
{
"type": 0,
"speech": ""
}
]
},
"score": 1
},
"status": {
"code": 200,
"errorType": "success"
},
"sessionId": "<sID>"
}
This looks like before my callback is finished, my webhook is sending empty response to Google Actions.
Why is this happening and How to resolve it?????
The problem lies in how your directionIntent() function calls, and handles the result of, your getDirectionWithSavedAddress() function. It expects getDirectionWithSavedAddress() returns a function, when it does not. Instead, getDirectionWithSavedAddress() expects to send its results to a callback.
So after it makes its call to getDirectionWithAddress(), the function ends, returning nothing. This "nothing" is sent to assistant.ask(), which returns that to Google's server. This is an invalid response, so you're getting the error.
Fixing this should be straightforward. You need to call getDirectionWithSavedAddress() with a callback function. Inside this function you should call assistant.ask() with the value sent to the callback.
So directionIntent() might look something like
'directionIntent':function (assistant) {
console.log('direction intent');
direction.getDirectionWithSavedAddress( function( msg ){
assistant.ask( msg );
} );
}
Updated
This line makes no sense:
assistant.handleRequest(responseHandler(assistant));
The assistant.handleRequest() function is supposed to be passed a Map of Intent names to functions to call to handle the event. You're doing this manually in the responseHandler() function and you're not returning a Map. Since you're not returning a Map, it fails when trying to do the handleRequest() and generates the error "Action Error: Request handler can NOT be empty".
You can fix this by just calling responseHandler(assistant) and not dealing with handleRequest() at all. Or you can create the map that handleRequest() is expecting and get rid of responseHandler() completely.

Intent handler does not work when multiple intents have scores higher than default threshold

The high scoring intent is not handled when multiple LUIS intents have their respective scores higher than the default threshold(0.1). For example, this is the LUIS response I'm receiving:
"entities": [],
"intents": [
{
"intent": "Good",
"score": 0.38138606327968266
},
{
"intent": "Not Good",
"score": 0.14239219383059437
},
{
"intent": "getInfo1",
"score": 0.058470389996233038
},
{
"intent": "getInfo3",
"score": 0.052354321946208544
},
{
"intent": "End Conversation",
"score": 0.051956717501244802
},
{
"intent": "getInfo2",
"score": 0.049671852402879912
},
{
"intent": "getInfo4",
"score": 0.04166953247839323
},
{
"intent": "getInfo5",
"score": 0.032684937293639832
},
{
"intent": "getInfo6",
"score": 0.027630602530947044
},
{
"intent": "getInfo7",
"score": 0.027075797198458249
}
],
"query": "iam good",
"topScoringIntent": {
"intent": "Good",
"score": 0.38138606327968266
}
}
I believe that the below intents are getting into some kind of conflict as they both have scores > 0.1.
I tried adding intentThreshold parameter for the triggerAction of the respective handlers. However, it is of no use.
{ "intent": "Good", "score": 0.38138606327968266 }, { "intent": "Not
Good", "score": 0.14239219383059437 }
Code Example
//BOT REST end point
var server = restify.createServer(options);
server.listen(config.port,function() {
console.log('%s listening to %s', server.name, server.url);
});
var connector = new builder.ChatConnector({
appId: config.BOTFRAMEWORK_APPID,
appPassword: config.BOTFRAMEWORK_APPSECRET
});
server.post('/test', connector.listen());
var bot = new builder.UniversalBot(connector);
var recognizer = new builder.LuisRecognizer(config.model);
bot.recognizer(recognizer);
bot.dialog("/",[function(session){
if(utilities.isEmpty(session.userData.name)) {
// Greetings
builder.Prompts.text(session,prompt.greeting_text);
}else {
session.beginDialog("/beginConversation");
}
},
function(session,results){
session.userData.name = results.response;
console.log("username :"+ session.userData.name);
session.beginDialog("/beginConversation");
}]);
// beginConversation
bot.dialog("/beginConversation", function(session){
builder.Prompts.text(session,"Hello "+session.userData.name+"! "+prompt.conversation_begin_text);
});
// userGood
bot.dialog("/userGood", function(session){
session.userData.isHappy = true;
builder.Prompts.text(session,prompt.user_good_response_text+" "+prompt.address_query_text);
}).triggerAction({
matches: "Good",
intentThreshold:0.20
});
// userBad
bot.dialog("/userBad", function(session){
session.userData.isHappy = false;
builder.Prompts.text(session,prompt.user_bad_response_text+" "+prompt.address_query_text);
}).triggerAction({
matches: "Not Good",
intentThreshold:0.20
});
Conversation flow:
User: Hi Bot: Greetings from bot! Please tell me your name
User: John Doe
Bot: Hello John Doe! How’re you doing ?
User: Iam good
I believe that the intent handler corresponding to the highest scoring intent should always be triggered. The bot should respond based on this intent handler.
However, there is no response as multiple intents surpass the default intent threshold, despite explicitly setting the intentThreshold parameter of triggerAction to a different threshold value.
Am I doing something wrong here ?
SDK Version: 3.8.4

Facebook messenger platform: generic template with quick replies

I was looking at some pretty popular bots like "The Guardian" and i noticed that whenever you get a generic template reply from it it also displays some quick reply buttons (see the photo attached). How did "The Guardian Bot" achieve this? How he combined quick replies and a generic template? It must be two messages involved.
This worked for me in Dialogflow, return similar Json Object in backend to achieve the result:
{
"facebook": {
"attachment":{
"type":"template",
"payload":{
"template_type":"generic",
"elements":[
{
"title":"Welcome!",
"image_url":"https://petersfancybrownhats.com/company_image.png",
"subtitle":"We have the right hat for everyone.",
"default_action": {
"type": "web_url",
"url": "https://petersfancybrownhats.com/view?item=103",
"webview_height_ratio": "tall"
},
"buttons":[
{
"type":"web_url",
"url":"https://petersfancybrownhats.com",
"title":"View Website"
},{
"type":"postback",
"title":"Start Chatting",
"payload":"DEVELOPER_DEFINED_PAYLOAD"
}
]
}
]
}
},
"quick_replies":[
{
"content_type":"text",
"title":"Search",
"payload":"<POSTBACK_PAYLOAD>",
"image_url":"http://example.com/img/red.png"
},
{
"content_type":"location"
}
]
}
}
Quick replies are usually accompanied by a 'text' property that sends a text message before the quick reply. It appears you can substitute any template for that. For example, here is the request body for a generic template carousel with quick replies:
{
"recipient":{
"id":"{{PSID}}"
},
"messaging_type": "response",
"message":{
"quick_replies": [
{
"content_type":"text",
"title":"Quick Reply 1",
"image_url":"https://raw.githubusercontent.com/fbsamples/messenger-platform-samples/master/images/Messenger_Icon.png",
"payload":"payload1"
},
{
"content_type":"text",
"title":"Quick Reply 2",
"payload":"payload2"
}
],
"attachment":{
"type":"template",
"payload":{
"template_type":"generic",
"elements":[
{
"title":"This is a generic template",
"subtitle":"Plus a subtitle!",
"image_url":"https://raw.githubusercontent.com/fbsamples/messenger-platform-samples/master/images/Messenger_Icon.png",
"buttons":[
{
"type":"postback",
"title":"Postback Button",
"payload":"<POSTBACK_PAYLOAD>"
}
]
},
{
"title":"Another generic template",
"subtitle":"Plus a subtitle!",
"image_url":"https://raw.githubusercontent.com/fbsamples/messenger-platform-samples/master/images/Messenger_Icon.png",
"buttons":[
{
"type":"postback",
"title":"Postback Button",
"payload":"<POSTBACK_PAYLOAD>"
}
]
},
{
"title":"And another!",
"subtitle":"Plus a subtitle!",
"image_url":"https://raw.githubusercontent.com/fbsamples/messenger-platform-samples/master/images/Messenger_Icon.png",
"buttons":[
{
"type":"postback",
"title":"Postback Button",
"payload":"<POSTBACK_PAYLOAD>"
}
]
}
]
}
}
}
}
I have implemented the bot in nodejs and I am using a node module called messenger-bot which makes it easier to call the messenger bot API. Here's my customized code for you
const http = require('http')
const https = require('https')
const Bot = require('messenger-bot')
var bot = new Bot({
token: 'your FB app token',
verify: 'VERIFY_TOKEN'
})
bot.on('postback', (payload, reply) => {
var postback = payload.postback.payload;
if (postback == "yes") {
function getQuickReplies() {
console.log("in next function");
var quick_list = {
"text": "Check the next article?",
"quick_replies": [{
"content_type": "text",
"title": "More stories",
"payload": "more stories"
},
{
"content_type": "text",
"title": "Sport",
"payload": "sport"
},
{
"content_type": "text",
"title": "Business",
"payload": "business"
}
]
};
bot.getProfile(payload.sender.id, (err, profile) => {
if (err) throw err
text = quick_list;
bot.sendMessage(payload.sender.id, text) {//this prints quick replies
console.log("sending message");
}
});
}
//calling generic template
var generic_temp = "message": {
"attachment": {
-- - your code-- -
}
}; //generic template refer - https://developers.facebook.com/docs/messenger-platform/send-api-reference/generic-template
bot.getProfile(payload.sender.id, (err, profile) => {
if (err) throw err
bot.sendMessage(payload.sender.id, generic_temp) {//this prints generic template
console.log("sending message");
}
});
//calling the quick replies once the generic template is sent
getQuickReplies(); //to avoid async execution issue, we will have to put this in a function.
}
});
references - Generic template, Quick replies, messenger-bot npm
Hope this helps! Happy coding ;)
NEW UPDATE
{
"facebook": {
"attachment":{
"type":"template",
"payload":{
"template_type":"generic",
"elements":[
{
"title":"Welcome!",
"image_url":"https://petersfancybrownhats.com/company_image.png",
"subtitle":"We have the right hat for everyone.",
"default_action": {
"type": "web_url",
"url": "https://petersfancybrownhats.com/view?item=103",
"webview_height_ratio": "tall"
},
"buttons":[
{
"type":"web_url",
"url":"https://petersfancybrownhats.com",
"title":"View Website"
},{
"type":"postback",
"title":"Start Chatting",
"payload":"DEVELOPER_DEFINED_PAYLOAD"
}
]
}
]
}
},
"quick_replies":[
{
"content_type":"text",
"title":"Red",
"payload":"<POSTBACK_PAYLOAD>",
"image_url":"http://example.com/img/red.png"
},{
"content_type":"text",
"title":"Green",
"payload":"<POSTBACK_PAYLOAD>",
"image_url":"http://example.com/img/green.png"
}
]
}
}

Resources