type2docfx
Version:
A tool to convert json format output from TypeDoc to universal reference model for DocFx to consume.
156 lines • 6.65 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
/**
* @module botbuilder-ai
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
const botbuilder_1 = require("botbuilder");
const request = require("request-promise-native");
const entities = require("html-entities");
var htmlentities = new entities.AllHtmlEntities();
const ENDPOINT_REGEXP = /\/knowledgebases\/(.*)\/generateAnswer\r\nHost:\s(.*)\r\n.*(?:EndpointKey|Ocp-Apim-Subscription-Key:)\s(.*)\r\n/i;
const UNIX_ENDPOINT_REGEXP = /\/knowledgebases\/(.*)\/generateAnswer\nHost:\s(.*)\n.*(?:EndpointKey|Ocp-Apim-Subscription-Key:)\s(.*)\n/i;
/**
* Manages querying an individual QnA Maker knowledge base for answers. Can be added as middleware
* to automatically query the knowledge base anytime a messaged is received from the user. When
* used as middleware the component can be configured to either query the knowledge base before the
* bots logic runs or after the bots logic is run, as a fallback in the event the bot doesn't answer
* the user.
*/
class QnAMaker {
/**
* Creates a new QnAMaker instance. You can initialize the endpoint for the instance by
* passing in the publishing endpoint provided in the QnA Maker portal.
*
* For version 2 this looks like:
*
* ```JS
* POST /knowledgebases/98185f59-3b6f-4d23-8ebb-XXXXXXXXXXXX/generateAnswer
* Host: https://westus.api.cognitive.microsoft.com/qnamaker/v2.0
* Ocp-Apim-Subscription-Key: 4cb65a02697745eca369XXXXXXXXXXXX
* Content-Type: application/json
* {"question":"hi"}
* ```
*
* And for the new version 4 this looks like:
*
* ```JS
* POST /knowledgebases/d31e049e-2557-463f-a0cc-XXXXXXXXXXXX/generateAnswer
* Host: https://test-knowledgebase.azurewebsites.net/qnamaker
* Authorization: EndpointKey 16cdca0b-3826-4a0f-a112-XXXXXXXXXXXX
* Content-Type: application/json
* {"question":"<Your question>"}
* ```
* @param endpoint The endpoint of the knowledge base to query.
* @param options (Optional) additional settings used to configure the instance.
*/
constructor(endpoint, options) {
// Initialize endpoint
if (typeof endpoint === 'string') {
// Parse endpoint
let matched = ENDPOINT_REGEXP.exec(endpoint);
if (!matched) {
matched = UNIX_ENDPOINT_REGEXP.exec(endpoint);
}
if (!matched) {
throw new Error(`QnAMaker: invalid endpoint of "${endpoint}" passed to constructor.`);
}
this.endpoint = {
knowledgeBaseId: matched[1],
host: matched[2],
endpointKey: matched[3]
};
}
else {
this.endpoint = endpoint;
}
// Initialize options
this.options = Object.assign({
scoreThreshold: 0.3,
top: 1,
answerBeforeNext: false
}, options);
}
onTurn(context, next) {
// Filter out non-message activities
if (context.activity.type !== botbuilder_1.ActivityTypes.Message) {
return next();
}
// Route request
if (this.options.answerBeforeNext) {
// Attempt to answer user and only call next() if not answered
return this.answer(context).then((answered) => {
return !answered ? next() : Promise.resolve();
});
}
else {
// Call next() and then attempt to answer only if nothing else responded
return next().then(() => {
return !context.responded ? this.answer(context).then(() => { }) : Promise.resolve();
});
}
}
/**
* Calls [generateAnswer()](#generateanswer) and sends the answer as a message ot the user.
* Returns a value of `true` if an answer was found and sent. If multiple answers are
* returned the first one will be delivered.
* @param context Context for the current turn of conversation with the use.
*/
answer(context) {
const { top, scoreThreshold } = this.options;
return this.generateAnswer(context.activity.text, top, scoreThreshold).then((answers) => {
if (answers.length > 0) {
return context.sendActivity({ text: answers[0].answer, type: 'message' }).then(() => true);
}
else {
return Promise.resolve(false);
}
});
}
/**
* Calls the QnA Maker service to generate answer(s) for a question. The returned answers will
* be sorted by score with the top scoring answer returned first.
* @param question The question to answer.
* @param top (Optional) number of answers to return. Defaults to a value of `1`.
* @param scoreThreshold (Optional) minimum answer score needed to be considered a match to questions. Defaults to a value of `0.001`.
*/
generateAnswer(question, top, scoreThreshold) {
const q = question ? question.trim() : '';
if (q.length > 0) {
return this.callService(this.endpoint, question, typeof top === 'number' ? top : 1).then((answers) => {
const minScore = typeof scoreThreshold === 'number' ? scoreThreshold : 0.001;
return answers.filter((ans) => ans.score >= minScore).sort((a, b) => b.score - a.score);
});
}
return Promise.resolve([]);
}
callService(endpoint, question, top) {
const url = `${endpoint.host}/knowledgebases/${endpoint.knowledgeBaseId}/generateanswer`;
const headers = {};
if (endpoint.host.endsWith('v2.0') || endpoint.host.endsWith('v3.0')) {
headers['Ocp-Apim-Subscription-Key'] = endpoint.endpointKey;
}
else {
headers['Authorization'] = `EndpointKey ${endpoint.endpointKey}`;
}
return request({
url: url,
method: 'POST',
headers: headers,
json: {
question: question,
top: top
}
}).then(result => {
const answers = [];
return result.answers.map((ans) => {
return { score: ans.score / 100, answer: htmlentities.decode(ans.answer) };
});
});
}
}
exports.QnAMaker = QnAMaker;
//# sourceMappingURL=qnaMaker.js.map