From aa2723a6e2f02f9ccc5a8339622ca749f2380f95 Mon Sep 17 00:00:00 2001 From: trojenguri Date: Fri, 6 Dec 2019 23:51:14 +0530 Subject: [PATCH] [QnA Maker] Support for IsTest and RankerType in GetAnswerAsync call in QnAMaker (#1478) --- .../qnamaker-interfaces/qnamakerOptions.ts | 10 ++++++ .../src/qnamaker-interfaces/rankerTypes.ts | 28 +++++++++++++++ .../src/qnamaker-utils/generateAnswerUtils.ts | 10 +++--- ...should_call_qnamaker_with_isTest_true.json | 13 +++++++ ...qnamaker_with_rankerType_questionOnly.json | 35 +++++++++++++++++++ .../botbuilder-ai/tests/qnaMaker.test.js | 20 +++++++++++ 6 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 libraries/botbuilder-ai/src/qnamaker-interfaces/rankerTypes.ts create mode 100644 libraries/botbuilder-ai/tests/TestData/QnAMaker/should_call_qnamaker_with_isTest_true.json create mode 100644 libraries/botbuilder-ai/tests/TestData/QnAMaker/should_call_qnamaker_with_rankerType_questionOnly.json diff --git a/libraries/botbuilder-ai/src/qnamaker-interfaces/qnamakerOptions.ts b/libraries/botbuilder-ai/src/qnamaker-interfaces/qnamakerOptions.ts index 2d80a4f5df..4aad7048cd 100644 --- a/libraries/botbuilder-ai/src/qnamaker-interfaces/qnamakerOptions.ts +++ b/libraries/botbuilder-ai/src/qnamaker-interfaces/qnamakerOptions.ts @@ -54,4 +54,14 @@ export interface QnAMakerOptions { * Id of the current question asked. */ qnaId?: number; + + /** + * A value indicating whether to call test or prod environment of knowledgebase. + */ + isTest?: boolean; + + /** + * Ranker types. + */ + rankerType?: string; } \ No newline at end of file diff --git a/libraries/botbuilder-ai/src/qnamaker-interfaces/rankerTypes.ts b/libraries/botbuilder-ai/src/qnamaker-interfaces/rankerTypes.ts new file mode 100644 index 0000000000..a8f5a54612 --- /dev/null +++ b/libraries/botbuilder-ai/src/qnamaker-interfaces/rankerTypes.ts @@ -0,0 +1,28 @@ +/** + * @module botbuilder-ai + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +/** + * Enumeration of types of ranking. + */ +export class RankerTypes { + + /** + * Default Ranker Behaviour. i.e. Ranking based on Questions and Answer. + */ + public static readonly default: string = 'Default'; + + /** + * Ranker based on question Only. + */ + public static readonly questionOnly: string = 'QuestionOnly'; + + /** + * Ranker based on Autosuggest for question field Only. + */ + public static readonly autoSuggestQuestion: string = 'AutoSuggestQuestion'; +} \ No newline at end of file diff --git a/libraries/botbuilder-ai/src/qnamaker-utils/generateAnswerUtils.ts b/libraries/botbuilder-ai/src/qnamaker-utils/generateAnswerUtils.ts index eebde70489..4ea9cc3ef2 100644 --- a/libraries/botbuilder-ai/src/qnamaker-utils/generateAnswerUtils.ts +++ b/libraries/botbuilder-ai/src/qnamaker-utils/generateAnswerUtils.ts @@ -17,6 +17,7 @@ import { QnARequestContext } from '../qnamaker-interfaces/qnaRequestContext'; import { HttpRequestUtils } from './httpRequestUtils'; import { QNAMAKER_TRACE_TYPE, QNAMAKER_TRACE_LABEL, QNAMAKER_TRACE_NAME } from '..'; +import { RankerTypes } from '../qnamaker-interfaces/rankerTypes'; /** * Generate Answer api utils class. @@ -58,8 +59,9 @@ export class GenerateAnswerUtils { */ public async queryQnaServiceRaw(endpoint: QnAMakerEndpoint, question: string, options?: QnAMakerOptions): Promise { const url: string = `${ endpoint.host }/knowledgebases/${ endpoint.knowledgeBaseId }/generateanswer`; - const queryOptions: QnAMakerOptions = { ...this._options, ...options } as QnAMakerOptions; - + var queryOptions: QnAMakerOptions = { ...this._options, ...options } as QnAMakerOptions; + + queryOptions.rankerType = !queryOptions.rankerType ? RankerTypes.default : queryOptions.rankerType; this.validateOptions(queryOptions); var payloadBody = JSON.stringify({ @@ -109,8 +111,8 @@ export class GenerateAnswerUtils { * * @param options (Optional) The options for the QnA Maker knowledge base. If null, constructor option is used for this instance. */ - public validateOptions(options: QnAMakerOptions): void { - const { scoreThreshold, top } = options; + public validateOptions(options: QnAMakerOptions) { + const { scoreThreshold, top, rankerType } = options; if (scoreThreshold) { this.validateScoreThreshold(scoreThreshold); diff --git a/libraries/botbuilder-ai/tests/TestData/QnAMaker/should_call_qnamaker_with_isTest_true.json b/libraries/botbuilder-ai/tests/TestData/QnAMaker/should_call_qnamaker_with_isTest_true.json new file mode 100644 index 0000000000..4723ee95e0 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/QnAMaker/should_call_qnamaker_with_isTest_true.json @@ -0,0 +1,13 @@ +{ + "activeLearningEnabled": true, + "answers": [ + { + "questions": [], + "answer": "No good match found in KB.", + "score": 0, + "id": -1, + "source": null, + "metadata": [] + } + ] + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/QnAMaker/should_call_qnamaker_with_rankerType_questionOnly.json b/libraries/botbuilder-ai/tests/TestData/QnAMaker/should_call_qnamaker_with_rankerType_questionOnly.json new file mode 100644 index 0000000000..c3df1eb40e --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/QnAMaker/should_call_qnamaker_with_rankerType_questionOnly.json @@ -0,0 +1,35 @@ +{ + "activeLearningEnabled": false, + "answers": [ + { + "questions": [ + "Q1" + ], + "answer": "A1", + "score": 80, + "id": 15, + "source": "Editorial", + "metadata": [ + { + "name": "topic", + "value": "value" + } + ] + }, + { + "questions": [ + "Q2" + ], + "answer": "A2", + "score": 78, + "id": 16, + "source": "Editorial", + "metadata": [ + { + "name": "topic", + "value": "value" + } + ] + } + ] + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/qnaMaker.test.js b/libraries/botbuilder-ai/tests/qnaMaker.test.js index bc85e95db3..30d8340468 100644 --- a/libraries/botbuilder-ai/tests/qnaMaker.test.js +++ b/libraries/botbuilder-ai/tests/qnaMaker.test.js @@ -252,6 +252,26 @@ describe('QnAMaker', function () { assert.strictEqual(qnaResults[0].score < 1, true, 'score should be low'); }); + it('should call qnamaker with isTest true', async function() { + const qna = new QnAMaker(endpoint); + const turnContext = new TestContext({ text: "Q11" }); + const options = { top: 1, context: null, isTest: true }; + + const qnaResults = await qna.getAnswers(turnContext, options); + + assert.strictEqual(qnaResults.length, 0, 'no answers should be returned'); + }); + + it('should call qnamaker with rankerType questionOnly', async function() { + const qna = new QnAMaker(endpoint); + const turnContext = new TestContext({ text: "Q11" }); + const options = { top: 1, context: null, rankerType: "questionOnly" }; + + const qnaResults = await qna.getAnswers(turnContext, options); + + assert.strictEqual(qnaResults.length, 2, 'no answers should be returned'); + }); + it('should return answer with timeout option specified', async function() { const timeoutOption = { timeout: 500000 }; const qna = new QnAMaker(endpoint, timeoutOption);