Removing bugs after updating to BOT Framework latest dialog pattern.
This commit is contained in:
parent
fbdae843cf
commit
5ecf922999
6 changed files with 185 additions and 156 deletions
|
@ -47,7 +47,7 @@ import * as simplegit from "simple-git/promise";
|
|||
import { AppServicePlan } from "azure-arm-website/lib/models";
|
||||
import { GBConfigService } from "../../../packages/core.gbapp/services/GBConfigService";
|
||||
import { GBAdminService } from "../../../packages/admin.gbapp/services/GBAdminService";
|
||||
import { GBCorePackage } from "packages/core.gbapp";
|
||||
import { GBCorePackage } from "../../../packages/core.gbapp";
|
||||
|
||||
const Spinner = require("cli-spinner").Spinner;
|
||||
const scanf = require("scanf");
|
||||
|
@ -148,7 +148,7 @@ export class AzureDeployerService extends GBService {
|
|||
logger.info(`Deploying Search...`);
|
||||
let searchName = `${name}-search`;
|
||||
await this.createSearch(name, searchName, instance.cloudLocation);
|
||||
let searchKeys = await this.searchClient.queryKeys.listBySearchService(
|
||||
let searchKeys = await this.searchClient.adminKeys.get(
|
||||
name,
|
||||
searchName
|
||||
);
|
||||
|
@ -502,11 +502,11 @@ export class AzureDeployerService extends GBService {
|
|||
displayName: name,
|
||||
endpoint: endpoint,
|
||||
iconUrl: iconUrl,
|
||||
//luisAppIds: [nlpAppId],
|
||||
//luisKey: nlpKey,
|
||||
luisAppIds: [nlpAppId],
|
||||
luisKey: nlpKey,
|
||||
msaAppId: appId,
|
||||
msaAppPassword: appPassword,
|
||||
//enabledChannels: ["webchat"], // , "skype", "facebook"],
|
||||
enabledChannels: ["webchat"], // , "skype", "facebook"],
|
||||
configuredChannels: ["webchat"] // , "skype", "facebook"]
|
||||
}
|
||||
};
|
||||
|
@ -528,9 +528,6 @@ export class AzureDeployerService extends GBService {
|
|||
return;
|
||||
}
|
||||
|
||||
logger.info(`Bot creation request done waiting for key generation...`);
|
||||
resolve(instance);
|
||||
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${
|
||||
|
|
|
@ -108,10 +108,16 @@ export class GBConversationalService implements IGBConversationalService {
|
|||
try {
|
||||
nlp = await model.recognize(step.context);
|
||||
} catch (error) {
|
||||
if (error.statusCode == 404){
|
||||
logger.warn ('NLP application still not publish and there are no other options for answering.')
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
else{
|
||||
let msg = `Error calling NLP server, check if you have a published model and assigned keys on the service. Error: ${
|
||||
error.statusCode ? error.statusCode : ""
|
||||
} ${error.message}`;
|
||||
return Promise.reject(new Error(msg));
|
||||
return Promise.reject(new Error(msg));}
|
||||
|
||||
}
|
||||
|
||||
// Resolves intents returned from LUIS.
|
||||
|
|
|
@ -177,7 +177,6 @@ export class GBDeployer {
|
|||
Path.extname(filename) === ".gbapp" ||
|
||||
Path.extname(filename) === ".gblib"
|
||||
) {
|
||||
|
||||
/** Themes for bots. */
|
||||
} else if (Path.extname(filename) === ".gbtheme") {
|
||||
server.use("/themes/" + filenameOnly, express.static(filename));
|
||||
|
@ -341,15 +340,31 @@ export class GBDeployer {
|
|||
let connectionString = GBDeployer.getConnectionStringFromInstance(instance);
|
||||
|
||||
const dsName = "gb";
|
||||
await search.deleteDatasource(dsName);
|
||||
await search.createDatasource(
|
||||
try {
|
||||
await search.deleteDataSource(dsName);
|
||||
} catch (err) {
|
||||
if (err.code != 404) {
|
||||
// First time, nothing to delete.
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
await search.createDataSource(
|
||||
dsName,
|
||||
dsName,
|
||||
"GuaribasQuestion",
|
||||
"azuresql",
|
||||
connectionString
|
||||
);
|
||||
await search.deleteIndex();
|
||||
|
||||
try {
|
||||
await search.deleteIndex();
|
||||
} catch (err) {
|
||||
if (err.code != 404) {
|
||||
// First time, nothing to delete.
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
await search.createIndex(
|
||||
AzureDeployerService.getKBSearchSchema(instance.searchIndex),
|
||||
dsName
|
||||
|
|
|
@ -429,7 +429,7 @@ export class GBMinService {
|
|||
// Otherwise, continue to the active dialog in the stack.
|
||||
} else {
|
||||
if (step.activeDialog) {
|
||||
await step.continue();
|
||||
await step.continueDialog();
|
||||
} else {
|
||||
await step.beginDialog("/answer", {
|
||||
query: context.activity.text
|
||||
|
@ -466,7 +466,7 @@ export class GBMinService {
|
|||
let token = (context.activity as any).data;
|
||||
await step.beginDialog("/adminUpdateToken", { token: token });
|
||||
} else {
|
||||
await step.continue();
|
||||
await step.continueDialog();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
@ -52,175 +52,181 @@ export class AskDialog extends IGBDialog {
|
|||
static setup(bot: BotAdapter, min: GBMinInstance) {
|
||||
const service = new KBService(min.core.sequelize);
|
||||
|
||||
min.dialogs.add(new WaterfallDialog("/answerEvent", [
|
||||
async step => {
|
||||
if (step.options && step.options["questionId"]) {
|
||||
let question = await service.getQuestionById(
|
||||
min.instance.instanceId,
|
||||
step.options["questionId"]
|
||||
);
|
||||
let answer = await service.getAnswerById(
|
||||
min.instance.instanceId,
|
||||
question.answerId
|
||||
);
|
||||
min.dialogs.add(
|
||||
new WaterfallDialog("/answerEvent", [
|
||||
async step => {
|
||||
if (step.options && step.options["questionId"]) {
|
||||
let question = await service.getQuestionById(
|
||||
min.instance.instanceId,
|
||||
step.options["questionId"]
|
||||
);
|
||||
let answer = await service.getAnswerById(
|
||||
min.instance.instanceId,
|
||||
question.answerId
|
||||
);
|
||||
|
||||
// Sends the answer to all outputs, including projector.
|
||||
// Sends the answer to all outputs, including projector.
|
||||
|
||||
await service.sendAnswer(min.conversationalService, step, answer);
|
||||
await service.sendAnswer(min.conversationalService, step, answer);
|
||||
|
||||
await step.replaceDialog("/ask", { isReturning: true });
|
||||
}
|
||||
return await step.next();
|
||||
}
|
||||
]));
|
||||
|
||||
min.dialogs.add(new WaterfallDialog("/answer", [
|
||||
async step => {
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
let text = step.options["query"];
|
||||
if (!text) {
|
||||
throw new Error(`/answer being called with no args.query text.`);
|
||||
}
|
||||
|
||||
let locale = step.context.activity.locale;
|
||||
|
||||
// Stops any content on projector.
|
||||
|
||||
await min.conversationalService.sendEvent(step, "stop", null);
|
||||
|
||||
// Handle extra text from FAQ.
|
||||
|
||||
if (step.options && step.options["query"]) {
|
||||
text = step.options["query"];
|
||||
} else if (step.options && step.options["fromFaq"]) {
|
||||
await step.context.sendActivity(Messages[locale].going_answer);
|
||||
}
|
||||
|
||||
// Spells check the input text before sending Search or NLP.
|
||||
|
||||
if (min.instance.spellcheckerKey) {
|
||||
let data = await AzureText.getSpelledText(
|
||||
min.instance.spellcheckerKey,
|
||||
text
|
||||
);
|
||||
|
||||
if (data != text) {
|
||||
logger.info(`Spelling corrected: ${data}`);
|
||||
text = data;
|
||||
await step.replaceDialog("/ask", { isReturning: true });
|
||||
}
|
||||
return await step.next();
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
// Searches KB for the first time.
|
||||
min.dialogs.add(
|
||||
new WaterfallDialog("/answer", [
|
||||
async step => {
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
let text = step.options["query"];
|
||||
if (!text) {
|
||||
throw new Error(`/answer being called with no args.query text.`);
|
||||
}
|
||||
|
||||
user.lastQuestion = text;
|
||||
await min.userProfile.set(step.context, user);
|
||||
let resultsA = await service.ask(
|
||||
min.instance,
|
||||
text,
|
||||
min.instance.searchScore,
|
||||
user.subjects
|
||||
);
|
||||
let locale = step.context.activity.locale;
|
||||
|
||||
// If there is some result, answer immediately.
|
||||
// Stops any content on projector.
|
||||
|
||||
if (resultsA && resultsA.answer) {
|
||||
// Saves some context info.
|
||||
await min.conversationalService.sendEvent(step, "stop", null);
|
||||
|
||||
user.isAsking = false;
|
||||
user.lastQuestionId = resultsA.questionId;
|
||||
// Handle extra text from FAQ.
|
||||
|
||||
if (step.options && step.options["query"]) {
|
||||
text = step.options["query"];
|
||||
} else if (step.options && step.options["fromFaq"]) {
|
||||
await step.context.sendActivity(Messages[locale].going_answer);
|
||||
}
|
||||
|
||||
// Spells check the input text before sending Search or NLP.
|
||||
|
||||
if (min.instance.spellcheckerKey) {
|
||||
let data = await AzureText.getSpelledText(
|
||||
min.instance.spellcheckerKey,
|
||||
text
|
||||
);
|
||||
|
||||
if (data != text) {
|
||||
logger.info(`Spelling corrected: ${data}`);
|
||||
text = data;
|
||||
}
|
||||
}
|
||||
|
||||
// Searches KB for the first time.
|
||||
|
||||
user.lastQuestion = text;
|
||||
await min.userProfile.set(step.context, user);
|
||||
|
||||
// Sends the answer to all outputs, including projector.
|
||||
|
||||
await service.sendAnswer(
|
||||
min.conversationalService,
|
||||
step,
|
||||
resultsA.answer
|
||||
);
|
||||
|
||||
// Goes to ask loop, again.
|
||||
|
||||
await step.replaceDialog("/ask", { isReturning: true });
|
||||
} else {
|
||||
// Second time running Search, now with no filter.
|
||||
|
||||
let resultsB = await service.ask(
|
||||
let resultsA = await service.ask(
|
||||
min.instance,
|
||||
text,
|
||||
min.instance.searchScore,
|
||||
null
|
||||
user.subjects
|
||||
);
|
||||
|
||||
// If there is some result, answer immediately.
|
||||
|
||||
if (resultsB && resultsB.answer) {
|
||||
if (resultsA && resultsA.answer) {
|
||||
// Saves some context info.
|
||||
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
|
||||
user.isAsking = false;
|
||||
user.lastQuestionId = resultsB.questionId;
|
||||
user.lastQuestionId = resultsA.questionId;
|
||||
await min.userProfile.set(step.context, user);
|
||||
|
||||
// Informs user that a broader search will be used.
|
||||
|
||||
if (user.subjects.length > 0) {
|
||||
let subjectText = `${KBService.getSubjectItemsSeparatedBySpaces(
|
||||
user.subjects
|
||||
)}`;
|
||||
await step.context.sendActivity(Messages[locale].wider_answer);
|
||||
}
|
||||
|
||||
// Sends the answer to all outputs, including projector.
|
||||
|
||||
await service.sendAnswer(
|
||||
min.conversationalService,
|
||||
step,
|
||||
resultsB.answer
|
||||
resultsA.answer
|
||||
);
|
||||
await step.replaceDialog("/ask", { isReturning: true });
|
||||
|
||||
// Goes to ask loop, again.
|
||||
|
||||
return await step.replaceDialog("/ask", { isReturning: true });
|
||||
} else {
|
||||
if (!(await min.conversationalService.routeNLP(step, min, text))) {
|
||||
await step.context.sendActivity(Messages[locale].did_not_find);
|
||||
await step.replaceDialog("/ask", { isReturning: true });
|
||||
// Second time running Search, now with no filter.
|
||||
|
||||
let resultsB = await service.ask(
|
||||
min.instance,
|
||||
text,
|
||||
min.instance.searchScore,
|
||||
null
|
||||
);
|
||||
|
||||
// If there is some result, answer immediately.
|
||||
|
||||
if (resultsB && resultsB.answer) {
|
||||
// Saves some context info.
|
||||
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
|
||||
user.isAsking = false;
|
||||
user.lastQuestionId = resultsB.questionId;
|
||||
await min.userProfile.set(step.context, user);
|
||||
|
||||
// Informs user that a broader search will be used.
|
||||
|
||||
if (user.subjects.length > 0) {
|
||||
let subjectText = `${KBService.getSubjectItemsSeparatedBySpaces(
|
||||
user.subjects
|
||||
)}`;
|
||||
await step.context.sendActivity(Messages[locale].wider_answer);
|
||||
}
|
||||
|
||||
// Sends the answer to all outputs, including projector.
|
||||
|
||||
await service.sendAnswer(
|
||||
min.conversationalService,
|
||||
step,
|
||||
resultsB.answer
|
||||
);
|
||||
return await step.replaceDialog("/ask", { isReturning: true });
|
||||
} else {
|
||||
if (
|
||||
!(await min.conversationalService.routeNLP(step, min, text))
|
||||
) {
|
||||
await step.context.sendActivity(Messages[locale].did_not_find);
|
||||
return await step.replaceDialog("/ask", { isReturning: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return await step.next();
|
||||
}
|
||||
]));
|
||||
])
|
||||
);
|
||||
|
||||
min.dialogs.add(new WaterfallDialog("/ask", [
|
||||
async step => {
|
||||
const locale = step.context.activity.locale;
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
user.isAsking = true;
|
||||
if (!user.subjects) {
|
||||
user.subjects = [];
|
||||
min.dialogs.add(
|
||||
new WaterfallDialog("/ask", [
|
||||
async step => {
|
||||
const locale = step.context.activity.locale;
|
||||
const user = await min.userProfile.get(step.context, {});
|
||||
user.isAsking = true;
|
||||
if (!user.subjects) {
|
||||
user.subjects = [];
|
||||
}
|
||||
let text;
|
||||
|
||||
// Three forms of asking.
|
||||
|
||||
if (step.options && step.options["firstTime"]) {
|
||||
text = Messages[locale].ask_first_time;
|
||||
} else if (step.options && step.options["isReturning"]) {
|
||||
text = Messages[locale].anything_else;
|
||||
} else if (user.subjects.length > 0) {
|
||||
text = Messages[locale].which_question;
|
||||
} else {
|
||||
throw new Error("Invalid use of /ask");
|
||||
}
|
||||
|
||||
if (text.length > 0) {
|
||||
return await step.prompt("textPrompt", text);
|
||||
}
|
||||
return await step.next();
|
||||
},
|
||||
async step => {
|
||||
return await step.replaceDialog("/answer", { query: step.result });
|
||||
}
|
||||
let text;
|
||||
|
||||
// Three forms of asking.
|
||||
|
||||
if (step.options && step.options["firstTime"]) {
|
||||
text = Messages[locale].ask_first_time;
|
||||
} else if (step.options && step.options["isReturning"]) {
|
||||
text = Messages[locale].anything_else;
|
||||
} else if (user.subjects.length > 0) {
|
||||
text = Messages[locale].which_question;
|
||||
} else {
|
||||
throw new Error("Invalid use of /ask");
|
||||
}
|
||||
|
||||
if (text.length > 0) {
|
||||
await step.prompt("textPrompt", text);
|
||||
}
|
||||
return await step.next();
|
||||
},
|
||||
async step => {
|
||||
await step.replaceDialog("/answer", { query: step.result });
|
||||
return await step.next();
|
||||
}
|
||||
]));
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
17
src/app.ts
17
src/app.ts
|
@ -61,17 +61,20 @@ let appPackages = new Array<IGBPackage>();
|
|||
* General Bots open-core entry point.
|
||||
*/
|
||||
export class GBServer {
|
||||
|
||||
/**
|
||||
* Program entry-point.
|
||||
*/
|
||||
|
||||
static run() {
|
||||
|
||||
logger.info(`The Bot Server is in STARTING mode...`);
|
||||
|
||||
// Creates a basic HTTP server that will serve several URL, one for each
|
||||
// bot instance. This allows the same server to attend multiple Bot on
|
||||
// the Marketplace until GB get serverless.
|
||||
|
||||
let port = process.env.port || process.env.PORT || 4242;
|
||||
logger.info(`The Bot Server is in STARTING mode...`);
|
||||
let server = express();
|
||||
|
||||
server.use(bodyParser.json()); // to support JSON-encoded bodies
|
||||
|
@ -86,7 +89,7 @@ export class GBServer {
|
|||
server.listen(port, () => {
|
||||
(async () => {
|
||||
try {
|
||||
logger.info(`Accepting connections on ${port}...`);
|
||||
logger.info(`Now accepting connections on ${port}...`);
|
||||
|
||||
// Reads basic configuration, initialize minimal services.
|
||||
|
||||
|
@ -95,7 +98,7 @@ export class GBServer {
|
|||
|
||||
// Ensures cloud / on-premises infrastructure is setup.
|
||||
|
||||
logger.info(`Establishing a development local proxy...`);
|
||||
logger.info(`Establishing a development local proxy (ngrok)...`);
|
||||
let proxyAddress = await core.ensureProxy(port);
|
||||
|
||||
let azureDeployer = new AzureDeployerService();
|
||||
|
@ -103,17 +106,17 @@ export class GBServer {
|
|||
try {
|
||||
await core.initDatabase();
|
||||
} catch (error) {
|
||||
logger.info(`Deploying cognitive infrastructure...`);
|
||||
logger.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
|
||||
try {
|
||||
bootInstance = await azureDeployer.deployFarm(proxyAddress);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
"In case of error, please cleanup any infrastructure (cloud or on-premises) objects created before running again."
|
||||
"In case of error, please cleanup any infrastructure objects created during this procedure before running again."
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
core.writeEnv(bootInstance);
|
||||
logger.info(`File .env written, starting...`);
|
||||
logger.info(`File .env written, starting General Bots...`);
|
||||
GBConfigService.init();
|
||||
|
||||
await core.initDatabase();
|
||||
|
@ -157,6 +160,7 @@ export class GBServer {
|
|||
try {
|
||||
instances = await core.loadInstances();
|
||||
let instance = instances[0];
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
|
||||
|
||||
|
@ -199,6 +203,7 @@ export class GBServer {
|
|||
|
||||
logger.info(`Deploying packages...`);
|
||||
let deployer = new GBDeployer(core, new GBImporter(core));
|
||||
await deployer.rebuildIndex(instances[0]);
|
||||
await deployer.deployPackages(core, server, appPackages);
|
||||
|
||||
// If instances is undefined here it's because storage has been formatted.
|
||||
|
|
Loading…
Add table
Reference in a new issue