Categories:
Configure Aura Bot in bypass mode
Orderly steps for the configuration of Aura Bot in bypass mode
Introduction
The main functionality of bypass mode, is that once we are in this mode within a conversation, any input message to aura-bot will be directly sent to the external service. Likewise, any message from the external service will be shown to the user without going through the bot recognition system.
In section bypass-mode-middleware you can find detailed information regarding the middleware that manages the bypass mode and the bypass mode model.
How to configure Aura Bot in bypass mode
The configuration of aura-bot in bypass mode implies three steps that are shown below.
Initialize bypass mode
To use bypass mode, the dialog must initialize the bypass object.
Params Bypass.initialize method:
| Property | Description |
|---|---|
| BypassState | Current bypass state |
| context | Current context of dialog |
| duration | Bypass life time in minutes |
| initialData | Initial data of bypass |
| payloadName | Name of the property in the channelData.payload used to send data to bypass.ex: 'handover'.For asynchronous APIs, it must be asyncCallback. |
| closeString | Comma-separated string or array of string with the words that directly close the bypass |
const bypass: Bypass<BypassModelData> = await Bypass.initialize(
stepContext.context,
this.timeout,
{},
'handover',
BypassState.Init,
getLiteral(stepContext?.context)('factotum:bypass.close.words'));
Once the bypass mode is initialized, all user’s messages will be redirected to the dialog. In bypass mode, when the dialog finishes all the actions of the use case logic, the dialog must be closed.
return await stepContext.endDialog();
Execute bypass mode
Bypass information can be loaded into the dialog by calling the Bypass.loadBypass method.
const bypass = await Bypass.loadBypass(stepContext.context);
From its initialization, the dialog should monitor the status of the bypass that can be one of the following:
BypassState.OffBypassState.InitBypassState.BypassBypassState.Closed
const bypass = await Bypass.loadBypass(stepContext.context);
switch (bypass.state) {
case BypassState.Off:
case BypassState.Init: {
.....
break;
}
case BypassState.Bypass: {
....
break;
}
case BypassState.Closed: {
....
break;
}
}
Bypass data field is used to exchange information between bypass interations, it is an any object.
To update the information in the bypass object, the bypass object is modified to invoke updateBypass.
const bypass = await Bypass.loadBypass(stepContext.context);
bypass.data.value = bypass.data.value + stepContext.context.activity.text + ' ';
await bypass.updateBypass(stepContext.context, false);
Close bypass mode
At any time the dialog can close the bypass mode by using the Bypass.closeBypass method.
await Bypass.closeBypass(stepContext.context);
The bypass may also be closed because of its expiration or because the user has said any of the closing words.
In these cases, when the control returns to the dialog, the state of the bypass will change to BypassState.Closed and the closeReason field will indicate the reason for the closing.
Example of dialog with Aura Bot in bypass mode
export default class AuraBotTestBypass extends ComponentDialog {
/**
* Dialog id for the base Aura Bot app dialog.
*/
public static readonly id: string = 'test-bypass';
public TTL_BYPASS_MIN: number = 0.25; // 15 seconds
/**
* logger
*/
public readonly logger = new AuraLogger.AuraBusEmitter('AuraTestBypass');
public readonly autoRegister: boolean = true;
protected configuration: Configuration;
/**
* Constructor for Bypass Handover.
*
* @param {Configuration} configuration The configuration joi schema.
*/
constructor(configuration: Configuration) {
super(AuraBotTestBypass.id);
super.initialDialogId = AuraBotTestBypass.id;
this.configuration = configuration;
super.addDialog(new WaterfallDialog(AuraBotTestBypass.id, [
this.handoverStart.bind(this),
this.handoverContinue.bind(this)
]));
}
/**
* Check Bypass Status
* INIT: Start Bypass Mode and create Bypass model into userData.
* BYPASS: Execute handoverContinue step dialog.
* CLOSE: Remove Bypass Model from userData and close Dialog.
*
* @param {WaterfallStepContext} stepContext The current step context.
*/
private async handoverStart(stepContext: WaterfallStepContext): Promise<DialogTurnResult> {
const corr = ContextUtils.getCorrelator(stepContext.context);
try {
let bypass = await Bypass.loadBypass(stepContext.context);
switch (bypass.state) {
case BypassState.Off:
case BypassState.Init:
bypass = await Bypass.initialize(stepContext.context, this.TTL_BYPASS_MIN, { value: '' }, 'test', BypassState.Bypass, ['exit','disconnect','disable']);
this.logger.info({ msg: `Bypass for: ${bypass.intent.intent} initialized`, corr });
await stepContext.context.sendActivity('Welcome to the CONCATENATOR, type "exit" to finish.');
return await stepContext.endDialog();
case BypassState.Bypass:
this.logger.info({ msg: `Bypass for: ${bypass.intent.intent} continue`, corr });
return await stepContext.next(bypass);
case BypassState.Closed:
await Bypass.closeBypass(stepContext.context);
this.logger.info({ msg: `Bypass for: ${bypass.intent.intent} closed`, corr });
if (bypass.closeReason === BypassCloseReason.BypassExpired) {
await stepContext.context.sendActivity('The CONCATENATOR has expired!');
}
await stepContext.context.sendActivity('The final result was: [ ' + bypass.data.value + ']');
return await stepContext.endDialog();
}
} catch (reason) {
this.logger.error({
corr,
msg: 'Error handover bypass',
error: reason.message,
stck: reason.stack
});
await stepContext.context.sendActivity(reason.message);
return await stepContext.endDialog();
}
}
/**
* Executes the logic for Bypass
*
* @param {WaterfallStepContext} stepContext The current step context.
*/
private async handoverContinue(stepContext: WaterfallStepContext ): Promise<DialogTurnResult> {
const bypass = stepContext.result;
bypass.data.value = bypass.data.value + stepContext.context.activity.text + ' ';
await bypass.updateBypass(stepContext.context, false);
await stepContext.context.sendActivity(bypass.data.value);
return await stepContext.endDialog();
}
}
How to use prompts in bypass mode
Prompts can be used within a dialog working in bypass mode.
public DEFAULT_RETRIES: number = 2;
public promptsNames = {
COMMAND_CHOICE_PROMPT: 'command-choice-prompt'
};
constructor(configuration: Configuration) {
....
// adds the prompt dialog to the Dialogs Set
this.customPrompt = new ChoicePrompt(this.promptsNames.COMMAND_CHOICE_PROMPT,
PromptUtils.getRetriesValidator(this.DEFAULT_RETRIES));
...
}
- The prompt will be instantiated, when the bypass mode is initialized.
this.customPrompt = new ChoicePrompt(this.promptsNames.COMMAND_CHOICE_PROMPT, PromptUtils.getRetriesValidator(retries));
// To change retries we need replace original dialog for prompt
(this.dialogs as any).dialogs['command-choice-prompt'] = this.customPrompt;
return await Bypass.initialize(
context,
parseFloat(timeoutMin),
{ value: '' },
'test',
BypassState.Bypass,
closeWords
);
- The prompt can be sent to the user and and the returned value can be collected in the dialog.
private async sendPrompt(stepContext: WaterfallStepContext): Promise<DialogTurnResult> {
const choicesText: Choice[] = [
{ value: 'id-0001', action: { title: 'Option 1 (id-0001)', type: '', value: '' }, synonyms: [] },
{ value: 'id-0002', action: { title: 'Option 2 (id-0002)', type: '', value: '' }, synonyms: [] },
{ value: 'id-0003', action: { title: 'Option 3 (id-0003)', type: '', value: '' }, synonyms: [] },
{ value: 'id-0004', action: { title: 'Option 4 (id-0004)', type: '', value: '' }, synonyms: [] },
{ value: 'id-0005', action: { title: 'Option 5 (id-0005)', type: '', value: '' }, synonyms: [] }
];
const promptOptions: PromptOptions = {
prompt: 'Hello, select one option: write the number, cardinal, ordinal or text',
choices: ChoiceFactory.toChoices(choicesText)
};
return await stepContext.prompt(this.promptsNames.COMMAND_CHOICE_PROMPT, promptOptions);
}
private async fallbackStep(stepContext: WaterfallStepContext): Promise<DialogTurnResult> {
await stepContext.context.sendActivity(`Option selected: ${stepContext.result.value}`);
return await stepContext.endDialog();
}