in Node/core/src/dialogs/PromptChoice.ts [76:205]
constructor(features?: IPromptChoiceFeatures) {
super({
defaultRetryPrompt: 'default_choice',
defaultRetryNamespace: consts.Library.system,
recognizeNumbers: true,
recognizeOrdinals: true,
recognizeChoices: true,
defaultListStyle: ListStyle.list,
inlineListCount: 3,
minScore: 0.4
});
this.updateFeatures(features);
// Default recognizer logic
this.onRecognize((context, cb) => {
if (context.message.text && !this.features.disableRecognizer) {
this.findChoices(context, true, (err, choices) => {
if (err || !choices || !choices.length) {
return cb(err, 0.0);
}
let topScore = 0.0;
let topMatch: IFindMatchResult = null;
let utterance = context.message.text.trim();
// Recognize using choices
if (this.features.recognizeChoices) {
let options = { allowPartialMatches: true };
let match = PromptRecognizers.findTopEntity(PromptRecognizers.recognizeChoices(utterance, choices, options));
if (match) {
topScore = match.score;
topMatch = match.entity
}
}
// Recognize index by number
if (this.features.recognizeNumbers) {
let options = { minValue: 1, maxValue: choices.length, integerOnly: true };
let match = PromptRecognizers.findTopEntity(PromptRecognizers.recognizeNumbers(context, options));
if (match && match.score > topScore) {
let index = Math.floor(match.entity - 1);
topScore = match.score;
topMatch = {
score: match.score,
index: index,
entity: choices[index].value
};
}
}
// Recognize index by ordinal
if (this.features.recognizeOrdinals) {
let match = PromptRecognizers.findTopEntity(PromptRecognizers.recognizeOrdinals(context));
if (match && match.score > topScore) {
let index = match.entity > 0 ? match.entity - 1 : choices.length + match.entity;
if (index >= 0 && index < choices.length) {
topScore = match.score;
topMatch = {
score: match.score,
index: index,
entity: choices[index].value
};
}
}
}
// Return result
if (topScore >= this.features.minScore && topScore > 0) {
cb(null, topScore, topMatch);
} else {
cb(null, 0.0);
}
});
} else {
cb(null, 0.0);
}
});
// Default message formatter
this.onFormatMessage((session, text, speak, callback) => {
let context = (<IPromptContext>session.dialogData);
let options = (<IPromptChoiceOptions>context.options);
this.findChoices(session.toRecognizeContext(), false, (err, choices) => {
let msg: IMessage;
if (!err && choices) {
// Resolve list style
let sendChoices = context.turns === 0 || context.isReprompt;
let listStyle = options.listStyle;
if (listStyle === undefined || listStyle === null || listStyle === ListStyle.auto) {
// Find maximum title length
let maxTitleLength = 0;
choices.forEach((choice) => {
let l = choice.action && choice.action.title ? choice.action.title.length : choice.value.length;
if (l > maxTitleLength) {
maxTitleLength = l;
}
});
// Determine list style
let supportsKeyboards = Channel.supportsKeyboards(session, choices.length);
let supportsCardActions = Channel.supportsCardActions(session, choices.length);
let maxActionTitleLength = Channel.maxActionTitleLength(session);
let hasMessageFeed = Channel.hasMessageFeed(session);
if (maxTitleLength <= maxActionTitleLength &&
(supportsKeyboards || (!hasMessageFeed && supportsCardActions))) {
listStyle = ListStyle.button;
sendChoices = true;
} else {
listStyle = this.features.defaultListStyle;
let inlineListCount = this.features.inlineListCount;
if (listStyle === ListStyle.list && inlineListCount > 0 && choices.length <= inlineListCount) {
listStyle = ListStyle.inline;
}
}
}
// Format message
msg = PromptChoice.formatMessage(session, listStyle, text, speak, sendChoices ? choices : null);
}
callback(err, msg);
});
});
// Add repeat intent handler
this.matches(consts.Intents.Repeat, (session) => {
// Set to turn-0 and re-prompt.
(<IPromptContext>session.dialogData).turns = 0;
this.sendPrompt(session);
});
}