When trying to debug my chatbot locally using the BotFramework emulator I'm getting the following output in my console when the bot tries to respond
Session.beginDialog(/)
console.pub.js:42
Error: LUIS model not found for locale 'en'.
console.pub.js:42
at LuisRecognizer.onRecognize (c:\Bots\sdk3\node_modules\botbuilder\lib\dialogs\LuisRecognizer.js:71:26)
at c:\Bots\sdk3\node_modules\botbuilder\lib\dialogs\IntentRecognizer.js:18:23
at next (c:\Bots\sdk3\node_modules\botbuilder\lib\dialogs\IntentRecognizer.js:50:17)
at LuisRecognizer.IntentRecognizer.isEnabled (c:\Bots\sdk3\node_modules\botbuilder\lib\dialogs\IntentRecognizer.js:53:9)
at LuisRecognizer.IntentRecognizer.recognize (c:\Bots\sdk3\node_modules\botbuilder\lib\dialogs\IntentRecognizer.js:10:14)
at LuisRecognizer.recognizer.recognize (c:\Bots\sdk3\node_modules\botbuilder-instrumentation\dist\main.js:224:32)
at c:\Bots\sdk3\node_modules\botbuilder\lib\dialogs\IntentRecognizerSet.js:68:28
at c:\Bots\sdk3\node_modules\botbuilder\node_modules\async\lib\async.js:181:20
at replenish (c:\Bots\sdk3\node_modules\botbuilder\node_modules\async\lib\async.js:319:21)
at c:\Bots\sdk3\node_modules\botbuilder\node_modules\async\lib\async.js:330:15
/ - ERROR: LUIS model not found for locale 'en'.
console.pub.js:42
/ - Session.endConversation()
I'm connecting the emulator to my chatbot via http://localhost:3978/api/messages and from what I can see the error seems to be tied to the "Locale:" field. however, no matter what I change it too I'm getting a similar error to the above. Is there something I need to change in my code or is this an issue purely with the emulator?
Code snippet that is causing the issue is below. it throws the above error when trying to use the q_BBApps intent along with any others in the code. This works fine when in azure or when using the webchat channel
var restify = require('restify');
var builder = require('botbuilder');
var azure = require('botbuilder-azure');
const instrumentation = require('botbuilder-instrumentation');
// Setting up advanced instrumentation
let logging = new instrumentation.BotFrameworkInstrumentation({
instrumentationKey: process.env.APPINSIGHTS_INSTRUMENTATION_KEY,
sentiments: {
key: process.env.CG_SENTIMENT_KEY,
},
// Will omit the user name from the logs for anonimization
omitUserName: false,
// Application insights options, all set to false by default
autoLogOptions: {
autoCollectConsole: true,
autoCollectExceptions: true,
autoCollectRequests: true,
autoCollectPerf: true // (auto collect performance)
}
});
// LUIS Link
var model = process.env.LUIS_URL;
let recognizer = new builder.LuisRecognizer(model);
logging.monitor(bot, recognizer);
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
// Table storage setup
var tableName = process.env.AZURE_TABLE_NAME; // You define
var storageName = process.env.AZURE_STORAGE_NAME; // Obtain from Azure Portal
var storageKey = process.env.AZURE_STORAGE_KEY; // Obtain from Azure Portal
var azureTableClient = new azure.AzureTableClient(tableName, storageName, storageKey);
var tableStorage = new azure.AzureBotStorage({
gzipData: false
}, azureTableClient);
// Create chat connector for communicating with the Bot Framework Service
var connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword,
stateEndpoint: process.env.BotStateEndpoint,
openIdMetadata: process.env.BotOpenIdMetadata
});
// Listen for messages from users
server.post('/api/messages', connector.listen());
// help messages and welcome messages
var helpMessage = "\n * Looking for help with Oracle R12, SharePoint or Yammer? Just start asking a question \n * If you're after anything specific choose one of the options below \n * If you need assistance at any point type 'Help' to bring up the help menu \n * You can cancel an action at any point by typing Cancel";
// Setup bot with default dialog
var bot = new builder.UniversalBot(connector)
.set('storage', tableStorage);
// Enable Conversation Data persistence
bot.set('persistConversationData', true);
// sets up the luis intent searching
var intents = new builder.IntentDialog({
recognizers: [recognizer]
});
//=========================================================
// Bots Events
//=========================================================
//Sends greeting message when the bot is first added to a conversation
bot.on('event', function(message) {
if(message.name == 'requestWelcomeDialog'){
bot.beginDialog(message.address, 'welcome');
}
});
// sets the default intent. this is needed for the welcome message to pass users into a normal conversation
bot.dialog('/', intents);
//Rest API call to the BBApps QnA service which & return results in cards
intents.matches('q_BBApps', [
function (session) {
var SPQuestion = session.message.text;
var request = require('request');
request({
method: 'Post',
headers: {
'Content-Type': 'application/json',
'Authorization': 'EndpointKey ' + process.env.QNA_endpointKey
},
body: {
"question": SPQuestion,
"top": 1
},
uri: process.env.QNA_BBAPPS_URL,
json: true
}, function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
var isCardFormat = body.answers[0].answer.includes('///');
if (!isCardFormat) {
// Not semi colon delimited, send a normal text response
let customEventName = 'BBApps - Non Card';
logging.trackCustomEvent(customEventName, session.message);
session.send(body.answers[0].answer);
} else if (body.answers && body.answers[0].score >= 0.1) {
var qnaAnswer = body.answers[0].answer;
var qnaAnswerData = qnaAnswer.split('///');
var title = qnaAnswerData[0];
var description = qnaAnswerData[1];
var url = qnaAnswerData[2];
var imageURL = qnaAnswerData[3];
var msg = new builder.Message(session);
msg.attachments([
new builder.HeroCard(session)
.title(title)
.text(description)
.images([builder.CardImage.create(session, imageURL)])
.buttons([
builder.CardAction.openUrl(session, url, "Learn More")
])
]);
}
let customEventName = 'BBApps';
logging.trackCustomEvent(customEventName, session.message);
session.send(msg);
session.endDialog();
});
}
]);
//Rest API call to raise an issue within gitlab
intents.matches('l_issue.Raiser', [
function(session) {
if (session.message && session.message.value) {
// A Card's Submit Action obj was received
processSubmitAction(session, session.message.value);
return;
}
var card = {
'contentType': 'application/vnd.microsoft.card.adaptive',
'content': {
'$schema': 'http://adaptivecards.io/schemas/adaptive-card.json',
'type': 'AdaptiveCard',
'version': '1.0',
'body': [
{
'type': 'Container',
'items': [
{
'type': 'ColumnSet',
'columns': [
{
'type': 'Column',
'width': 'stretch',
'items': [
{
'type': 'TextBlock',
'text': 'Welcome to the Issue Raiser!',
'speak': '<s>Welcome to the Issue Raiser! Please note that this skill is not to raise issues with the knowledge of BB Bot.</s>',
'weight': 'bolder',
'size': 'large'
},
{
'type': 'TextBlock',
'text': 'Please note that this skill is not to raise issues with the knowledge of BB Bot.',
'wrap': true,
'speak': '<s>Please note that this skill is not to raise issues with the knowledge of BB Bot.</s>'
},
{
'type': 'TextBlock',
'text': 'Please enter a title for your issue:*'
},
{
'type': 'Input.Text',
'id': 'issueTitle',
'speak': '<s>Please enter a title for your issue</s>',
'style': 'text'
},
{
'type': 'TextBlock',
'text': 'Please write a description for your issue:*'
},
{
'type': 'Input.Text',
'id': 'issueDescription',
'speak': '<s>Please write a description for your issue</s>',
'style': 'text'
},
{
'type': 'TextBlock',
'text': 'What is your email address?*'
},
{
'type': 'Input.Text',
'id': 'issueEmail',
'speak': '<s>What is your email address?</s>',
'style': 'text'
}
]
}
]
}
]
}
],
'actions': [
{
'type': 'Action.Submit',
'title': 'OK',
'data' : {
'type':'issueRaiser'
}
}
]
}
};
var msg = new builder.Message(session)
.addAttachment(card);
session.send(msg);
}
]);
//Allows the user to cancel the currently running command.
bot.dialog('issue-raiser', (session, issueRaiser) => {
var bugTitle = issueRaiser.issueTitle;
var bugDesc = issueRaiser.issueDescription;
var bugEmail = issueRaiser.issueEmail;
session.send("Function Started" + bugDesc + bugEmail + bugTitle)
session.endDialog();
});
function processSubmitAction(session, value) {
var defaultErrorMessage = "Please complete all of the required fields."
switch (value.type) {
case 'issueRaiser':
if (validateIssueRaiser(value)) {
session.beginDialog('issue-raiser', value);
}
break;
}
}
function validateIssueRaiser(issueRaiser) {
if (!issueRaiser) {
return false;
}
// Issue Description
var hasissueDescription = typeof issueRaiser.issueDescription === 'string' && issueRaiser.issueDescription.length > 3;
// Issue Title
var hasissueTitle = typeof issueRaiser.issueTitle === 'string' && issueRaiser.issueTitle.length > 3;
// Issue Email
var hasissueEmail = typeof issueRaiser.issueEmail === 'string' && issueRaiser.issueEmail.length > 3;
return hasissueDescription && hasissueTitle && hasissueEmail;
};
/*-----------------------------------------------------------------------------
Bot Dialogs
-----------------------------------------------------------------------------*/
// Welcome message - gets sent at the start of a convo
bot.dialog('welcome', function (session) {
//Send a help message
// Add the help dialog to the top of the dialog stack
// (override the default behavior of replacing the stack)
var msg = new builder.Message(session);
msg.attachmentLayout(builder.AttachmentLayout.carousel);
msg.attachments([
new builder.HeroCard(session)
.title("Help")
.text("Hi. I'm BB Bot and i can help you with the following: %s", helpMessage)
.buttons([
builder.CardAction.imBack(session, "I'd like to get an update on a ticket", "ServiceNow Status Update"),
builder.CardAction.imBack(session, "I'd like to search the Service Now Knowledge Base", "ServiceNow Knowledge Base Search"),
builder.CardAction.postBack(session, "/feedback", "Provide feedback")
]),
]);
session.send(msg);
session.endDialog();
let customEventName = 'Welcome';
logging.trackCustomEvent(customEventName, session.message);
session.endDialog();
})
// Once triggered, will start a new dialog as specified by
// the 'onSelectAction' option.
.triggerAction({
matches: /help/i,
});
//Allows the user to cancel the currently running command.
bot.dialog('/cancel', (session) => {
let customEventName = 'Cancel';
logging.trackCustomEvent(customEventName, session.message);
session.send('The current actioned has been cancelled. either try again or type "Help"');
//return session.beginDialog('/');
session.endDialog();
})
.triggerAction({
matches: /cancel/i,
});
// Ends the currently running speach and launches the Help menu
bot.dialog('help', function (session) {
//Send a help message
// Add the help dialog to the top of the dialog stack
// (override the default behavior of replacing the stack)
var msg = new builder.Message(session);
msg.attachmentLayout(builder.AttachmentLayout.carousel);
msg.attachments([
new builder.HeroCard(session)
.title("Help")
.text("looks like you've requested some help. Here's a list of handy options you can choose to talk to me about. Please select one below %s", helpMessage)
.buttons([
builder.CardAction.imBack(session, "I'd like to get an update on a ticket", "ServiceNow Status Update"),
builder.CardAction.imBack(session, "I'd like to search the Service Now Knowledge Base", "ServiceNow Knowledge Base Search"),
builder.CardAction.postBack(session, "/feedback", "Provide feedback")
]),
]);
session.send(msg);
session.endDialog();
let customEventName = 'Help';
logging.trackCustomEvent(customEventName, session.message);
session.endDialog("Global help menu.");
})
// Once triggered, will start a new dialog as specified by
// the 'onSelectAction' option.
.triggerAction({
matches: /help/i,
});
// feedback function - asks user for feedback and then submits to the database
// This is pulled out in the powerbi report
bot.dialog('/feedback', [
function (session) {
builder.Prompts.text(session, "Please type in your feedback");
},
function (session, results) {
session.dialogData.feedbackData = results.response;
builder.Prompts.text(session, "Please provide your name");
},
function (session, results) {
session.dialogData.feedbackName = results.response;
// Process request and display reservation details
session.send('Thank you. Your feedback has been sent');
session.conversationData.feedback = session.dialogData.feedbackData;
session.conversationData.feedbackUser = session.dialogData.feedbackName;
session.save();
let customEventName = 'Feedback';
let customEventData = {
feedback: session.dialogData.feedbackData,
feedbackUser: session.dialogData.feedbackName
};
logging.trackCustomEvent(customEventName, customEventData, session);
session.endDialog();
}
])
.triggerAction({
matches: /feedback/i,
});
// Default message is a LUIS intent isnt found for the given text
intents.onDefault([
function (session) {
session.send("Sorry, i didn't understand that. Could you try rephrasing your question?");
}
]);
If we focus this error message in SDK's source code, we can find the condition at https://github.com/Microsoft/BotBuilder/blob/master/Node/core/src/dialogs/LuisRecognizer.ts#L63L103, which will check wether the mode
we set to LuisRecognizer
exists. IF not, the SDK will throw this general error you met.
And I found that you are using the nodejs runtime variable by var model = process.env.LUIS_URL;
, so you can double check wether the environment variable is set correctly on local and be loaded in the runtime when you are running your bot locally.