/
dialogHelper.js
177 lines (135 loc) · 5.7 KB
/
dialogHelper.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const { CardFactory } = require('botbuilder');
const { DialogTurnStatus, WaterfallDialog, WaterfallStepContext } = require('botbuilder-dialogs');
const { QnAMaker } = require('botbuilder-ai');
const { ActiveLearningHelper } = require('./activeLearningHelper');
class DialogHelper {
/**
* QnAMaker Active Learning Dialog helper class.
* @param {QnAMakerEndpoint} endpoint An instance of QnAMaker Endpoint.
*/
constructor(endpoint) {
this.activeLearningDialogName = "active-learning-dialog";
this.qnaData = "value-qnaData";
this.currentQuery = "value-current-query";
this.qnaMaker = new QnAMaker(endpoint);
this.activeLearningHelper = new ActiveLearningHelper();
this.qnaMakerActiveLearningDialog = new WaterfallDialog(this.activeLearningDialogName);
this.qnaMakerActiveLearningDialog
.addStep(this.callGenerateAnswer.bind(this))
.addStep(this.filterLowVariationScoreList.bind(this))
.addStep(this.callTrain.bind(this))
.addStep(this.displayQnAResult.bind(this));
}
/**
* @param {WaterfallStepContext} stepContext contextual information for the current step being executed.
*/
async callGenerateAnswer(stepContext){
// Default QnAMakerOptions
var qnaMakerOptions = {
ScoreThreshold: 0.03,
Top: 3
};
if(stepContext.activeDialog.state["options"] != null){
qnaMakerOptions = stepContext.activeDialog.state["options"];
}
// Perform a call to the QnA Maker service to retrieve matching Question and Answer pairs.
var qnaResults = await this.qnaMaker.getAnswers(stepContext.context, qnaMakerOptions);
var filteredResponses = qnaResults.filter(r => r.score > qnaMakerOptions.ScoreThreshold);
stepContext.values[this.qnaData] = filteredResponses;
stepContext.values[this.currentQuery] = stepContext.context.activity.text;
return await stepContext.next();
}
/**
* @param {WaterfallStepContext} stepContext contextual information for the current step being executed.
*/
async filterLowVariationScoreList(stepContext){
var responses = stepContext.values[this.qnaData];
var filteredResponses = this.activeLearningHelper.getLowScoreVariation(responses);
stepContext.values[this.qnaData] = filteredResponses;
if(filteredResponses.length > 1){
var suggestedQuestions = [];
filteredResponses.forEach(element => {
suggestedQuestions.push(element.questions[0]);
});
var message = GetHeroCard(suggestedQuestions);
await stepContext.context.sendActivity(message);
return { status: DialogTurnStatus.waiting };
}
else{
return await stepContext.next(responses);
}
}
/**
* @param {WaterfallStepContext} stepContext contextual information for the current step being executed.
*/
async callTrain(stepContext){
var trainResponses = stepContext.values[this.qnaData];
var currentQuery = stepContext.values[this.currentQuery];
if(trainResponses.length > 1){
var reply = stepContext.context.activity.text;
var qnaResults = trainResponses.filter(r => r.questions[0] == reply);
if(qnaResults.length > 0){
stepContext.values[this.qnaData] = qnaResults;
var feedbackRecords = {
FeedbackRecords:[
{
UserId:stepContext.context.activity.id,
UserQuestion: currentQuery,
QnaId: qnaResults[0].id
}
]
};
// Call Active Learning Train API
this.activeLearningHelper.callTrain(this.qnaMaker.endpoint.host, feedbackRecords, this.qnaMaker.endpoint.knowledgeBaseId, this.qnaMaker.endpoint.endpointKey);
return await stepContext.next(qnaResults);
}
else{
return await stepContext.endDialog();
}
}
return await stepContext.next(stepContext.result);
}
/**
* @param {WaterfallStepContext} stepContext contextual information for the current step being executed.
*/
async displayQnAResult(stepContext) {
var responses = stepContext.result;
var message = "No QnAMaker answers found."
if(responses != null){
if(responses.length > 0){
message = responses[0].answer;
}
}
await stepContext.context.sendActivity(message);
return await stepContext.endDialog();
}
}
/**
* Get Hero card to get user feedback
* @param {Array} suggestionList A list of suggested questions strings.
* @param {string} cardTitle Title of the card.
* @param {string} cardNoMatchText No match text.
*/
function GetHeroCard(suggestionList, cardTitle = "Did you mean:", cardNoMatchText = "None of the above."){
var cardActions = [];
suggestionList.forEach(element => {
cardActions.push({
value: element,
type: "imBack",
title: element
});
});
cardActions.push({
value: cardNoMatchText,
type: "imBack",
title: cardNoMatchText
});
var heroCard = CardFactory.heroCard(
cardTitle,
[],
CardFactory.actions(cardActions));
return { attachments: [heroCard] }
}
module.exports.DialogHelper = DialogHelper;