diff --git a/libraries/botbuilder-dialogs-adaptive/src/adaptiveDialog.ts b/libraries/botbuilder-dialogs-adaptive/src/adaptiveDialog.ts index 2eedf06023..0b44e000cd 100644 --- a/libraries/botbuilder-dialogs-adaptive/src/adaptiveDialog.ts +++ b/libraries/botbuilder-dialogs-adaptive/src/adaptiveDialog.ts @@ -42,7 +42,7 @@ import isEqual from 'lodash/isEqual'; import { ActionContext } from './actionContext'; import { AdaptiveDialogState } from './adaptiveDialogState'; import { AdaptiveEvents } from './adaptiveEvents'; -import { OnCondition } from './conditions'; +import { OnCondition, OnIntent } from './conditions'; import { DialogSetConverter, LanguageGeneratorConverter, RecognizerConverter } from './converters'; import { EntityAssignment } from './entityAssignment'; import { EntityAssignmentComparer } from './entityAssignmentComparer'; @@ -655,11 +655,35 @@ export class AdaptiveDialog extends DialogContainer im this._recognizerSet.recognizers.push(new ValueRecognizer()); } const recognized = await this._recognizerSet.recognize(actionContext, activity); - const { intent } = getTopScoringIntent(recognized); - for (const key in recognized.intents) { - if (key !== intent) { - delete recognized[key]; + + const intents = Object.entries(recognized.intents); + + if (intents.length > 0) { + // Score + // Gathers all the intents with the highest Score value. + const scoreSorted = intents.sort(([, a], [, b]) => b.score - a.score); + const [[, firstItemScore]] = scoreSorted; + const topIntents = scoreSorted.filter(([, e]) => e.score == firstItemScore.score); + + // Priority + // Gathers the Intent with the highest Priority (0 being the highest). + // Note: this functionality is based on the FirstSelector.SelectAsync method. + let [topIntent] = topIntents; + + if (topIntents.length > 1) { + let highestPriority = Number.MAX_SAFE_INTEGER; + for (const [key, intent] of topIntents) { + const [triggerIntent] = this.triggers.filter((x) => x instanceof OnIntent && x.intent == key); + const priority = triggerIntent.currentPriority(actionContext); + if (priority >= 0 && priority < highestPriority) { + topIntent = [key, intent]; + highestPriority = priority; + } + } } + + const [key, value] = topIntent; + recognized.intents = { [key]: value }; } return recognized; } else { diff --git a/libraries/botbuilder-dialogs-adaptive/src/recognizers/regexRecognizer.ts b/libraries/botbuilder-dialogs-adaptive/src/recognizers/regexRecognizer.ts index 3fd2750fa7..d5f95ed071 100644 --- a/libraries/botbuilder-dialogs-adaptive/src/recognizers/regexRecognizer.ts +++ b/libraries/botbuilder-dialogs-adaptive/src/recognizers/regexRecognizer.ts @@ -104,7 +104,7 @@ export class RegexRecognizer extends AdaptiveRecognizer implements RegexRecogniz const regexp = intentPattern.regex; while ((matched = regexp.exec(text))) { matches.push(matched); - if (regexp.lastIndex == text.length) { + if (regexp.lastIndex == text.length || !regexp.lastIndex) { break; // to avoid infinite loop } } @@ -135,8 +135,6 @@ export class RegexRecognizer extends AdaptiveRecognizer implements RegexRecogniz }); } }); - - break; } if (this.entities) { diff --git a/libraries/botbuilder-dialogs-adaptive/tests/adaptiveDialog.test.js b/libraries/botbuilder-dialogs-adaptive/tests/adaptiveDialog.test.js index 80f86e42cf..63472779d5 100644 --- a/libraries/botbuilder-dialogs-adaptive/tests/adaptiveDialog.test.js +++ b/libraries/botbuilder-dialogs-adaptive/tests/adaptiveDialog.test.js @@ -206,4 +206,62 @@ describe('AdaptiveDialog', function () { .assertReply('passed') .startTest(); }); + + it('Get top intent using score and priority', async function () { + const priorityDialog = new AdaptiveDialog('priority').configure({ + autoEndDialog: false, + recognizer: new RegexRecognizer().configure({ + intents: [ + { + intent: 'cancel', + pattern: '^(?:cancel|quit|stop|end)', + }, + { + intent: 'default', + pattern: '.', + }, + { + intent: 'help', + pattern: '^(?:support|advice|help|\\?)', + }, + ], + }), + triggers: [ + new OnIntent().configure({ + intent: 'cancel', + priority: 0, + actions: [new SendActivity('Cancel intent recognized')], + }), + new OnIntent().configure({ + intent: 'default', + priority: 999, + actions: [new SendActivity('Default intent recognized')], + }), + new OnIntent().configure({ + intent: 'help', + priority: 0, + actions: [new SendActivity('Help intent recognized')], + }), + ], + }); + + const dm = new DialogManager(priorityDialog); + + const adapter = new TestAdapter(async (context) => { + await dm.onTurn(context); + }); + const storage = new MemoryStorage(); + useBotState(adapter, new ConversationState(storage), new UserState(storage)); + + await adapter + .send('help') + .assertReply('Help intent recognized') + .send('?') + .assertReply('Help intent recognized') + .send('cancel') + .assertReply('Cancel intent recognized') + .send('random-text') + .assertReply('Default intent recognized') + .startTest(); + }); });